Blokování kombinace ip adresy a portu na základě informací z logu

Úvod, proč

Pokud nás zlobí scanování portu 22(ssh), nebo chceme zvýšit bezpečnost např. ftp, https serveru apod.
Co se týká ssh, je jej možné chránit i pomocí certifikátů, přístupu jen z povolených adres, nastavení přístupu jen dannému uživatel (skupině) atd. Ale pokud například chcete mít přístup odkukoliv a nechcete se obtěžovat s certifikáty, tak máte jen možnost silného hesla a případné periodické změny hesla.
Pro zvýšení bezpečnosti je možné vyhodnotit chování druhé strany (v tomto případě chováni IP adresy) a pokud se nám chování nelíbí, tak jí odepřít vstup do systému (v tomto případě uzavřít pro ni firewall).
Pro zvýšení bezpečnosti například ftp serveru či http(s) přihlašování je velmi vhodné mít mechanismy již v serveru či aplikaci, ale zablokování IP adresy odlehčí serveru, který zpravidla musí provést složitější vyhodnocovací process.
Rozhodně bych doporučil blokování IP:PORT na základě chování clienta pro veřejné „domácí“ a „testovací“ servery – nebudete přece neustále kontrolovat bezpečnost a měnit hesla. Rovněž lze se řešení doporučit do vnitřní sítě jako prvek zvýšení zabezpečení. Zde odpadá většinou problém z blokováním skupiny uživatelů, ale zas by měla přibýt automatická notifikace blokování.

Negativní důsledy blokování IP

Blokováním IP uzavřeme k vůli chování jednoho uživatel (případně napadeného počítače) přístup více uživatelům jsoucí přes jednu veřejnou IP adresu (např. celé firmě).
Nespatřuji v tom nic špatného, za rozesílání spamu se dostanete velice rychle na seznamy spamerů (blacklisty) a také jste blokováni a zodpovědnost nese ten kdo zodpovídá za příčinu.
Je naprosto jasné, že tím Vám kdokoliv skrývající se za stejnou adresou odepře přístup na danou službu - co se dá dělat :(.
Pokud blokujeme na základě periodického vyhodnocování logu a děláme to jednoduše, jako níže uvedený skript, tak se nám může stát, že se chceme opakovaně přihlásit a opakovaně zadíme třeba špatné heslo, ale na konec se přihlásíme a po 10sec jsem odříznuti, ikdyž jsme se již příhlásily.
Je to proto, že nastal interval vyhodnocení logů a naše neuspěšné předchozí přihlášení naplnily podmínku k blokaci.

Jak to funguje na firewall-u (iptables)

Pokud používáme standardní firewall „iptables“, tak to můžeme udělat takto:

Příprava

Blokování

Která volba je lepší, těžko říct, služba by měla říct „nechci tě“, ale ten co nás obtěžuje, není také nijak vybíravý.
REJECT – má výšší režii – musí odpovědět.
DROP – je neslušné, ale nezatěžuje systém.
Níže uvedený script je nastaven tak, že při prvním blokaci použije REJECT a při opakované DROP, což je již slušné a náš server neni internetový nezdvořák :)
Pozn: Pravidla nejsou nikde trvale uložena a při restartování firewallu nám zmízí, proto je nutné kontrolovat při blokaci existenci našeho řetězce. Lze to zkontrolovat například příkazem:
iptables -n -L INPUT | grep -q mujRetez
pokud chceme vidět výstup (ne jen kontrolovat návratový kód) odstraníme '-q'.

Odblokování

Musíme provést stejnou defincí jako blokování, tudíž musíme rozlišit:

Úklid

Neuškodí tyto příkazy provést před přípravou s tím, že nás nezajímají návratové kódy.

Řešení pomocí PHP scriptu

Uváděný skript je hóóódně zjednodušené „něco“ jako fail2ban.

Požadavky … ?

Instalace … ?

Ke stažení je zde zip soubor s aktuální verzí.
Případně starší verze zde tar.gz soubor (nedoporučuji).

Nastavení k PHP scriptu

Nastavení je plně v souboru /etc/imegaipblock/imegaipblock.conf.php.

Základní nastavení:

Jak to funguje:

Záznamy z adres 'secureIp' jsou zahozeny.
1x za 'frequence' sekund jsou vyhodnoceny záznamy přibyvší do logů (kterých ? viz níže)
Pokud existuje záznam vyhodnocený jako „k blokaci“ je IP:PORT uložen do pole „kandidátů“
Pokud v poli „kandidátů“ přesáhne počet nalezených záznamů 'foundToBlock', je kombinace IP:PORT zablokována na dobu 'blockTime' a záznam je odstraněn z pole „kandidátů“. Záznamy v poli „kandidátů“ jsou uchovávány jen po dobu 'foundTime'.
Po vypršení času blokace je IP:PORT odblokován a současně zařazen do pole „historie“ na dobu 'historyTime'. Pokud již v poli „historie“ záznam existoval je čas 'historyTime' přičten, tudíž záznam v historii figuruje delší dobu.
Při opětovné blokaci (záznam byl nalezen v poli „historie“), je čas zbývající po vyřazení z pole „historie“ podělen „historyTime“ a tento koeficiont násoben 'repeatBlockTime'. Prakticky to znamená, že když si útočník nedá pokoj a furt to zkouší tak je dle výše uvedeného nastavení zablokován na 10min pak na 31min pak na 52min atd. Při první blokaci je vracena odpověď „nechci to, už mi to sem nedávejte“, při dalších již nedopovídáme (hloupý útočník si myslí, že provedl úspěšně DOS útok).
Každých 'timeToDropAllBlock' sekund je vše vyčištěno na straně iptables a všechny blokace uvolněny, historie zústává.

Nastavení vyhodnocování:

Zde je nastavení složitější: Pole $imblk_conf['rules'] obsahuje prvky pro jednotlivé vyhodnocované logy, například na SuSe 11+, lze vyhodnocovat jen log /var/log/messages, ale to je možné přestavit pravidly logovacího mechanismu (pravidly v /etc/syslog-ng/syslog-ng.conf).
Zde je ukázkové nastavení obsažene ve staženém souboru:
  $imblk_prefix = '^.*sshd\[(?<pid>\d+)\]:.*'; 
  $imblk_host   = '(?:::f{4,6}:)?(?<host>\S+)';
  $imblk_conf['rules'][] = 
    Array('logFile' => '/var/log/auth',
          'params'  => Array(
                       Array('port' => 22,
                             'regexp' => Array(
                                                '/' . $imblk_prefix . '(?:error: PAM: )?Authentication failure for .* from ' . $imblk_host . '\s*$/',
                                                '/' . $imblk_prefix . 'Failed [-\/\w]+ for .* from ' . $imblk_host . '(?: port \d*)?(?: ssh\d*)?$/',
                                                '/' . $imblk_prefix . 'ROOT LOGIN REFUSED.* FROM ' . $imblk_host . '\s*$/',
                                                '/' . $imblk_prefix . '[iI](?:llegal|nvalid) user .* from ' . $imblk_host . '\s*$/',
                                                '/' . $imblk_prefix . 'User \S+ from ' . $imblk_host . ' not allowed because not listed in AllowUsers$/',
                                                '/' . $imblk_prefix . 'authentication failure; logname=\S* uid=\S* euid=\S* tty=\S* ruser=\S* rhost=' . $imblk_host . '(?:\s+user=.*)?\s*$/',
                                                '/' . $imblk_prefix . 'refused connect from \S+ \(' . $imblk_host . '\)\s*$/',
                                                '/' . $imblk_prefix . 'Address ' . $imblk_host . ' .* POSSIBLE BREAK-IN ATTEMPT\s*$/'
                                              )
                            )
                       /*pureftpd on port 21*/                            
                      ,Array('port' => 21,//ftp
                             'regexp' => Array(
                                                '/^.*pure-ftpd: \(\?\@'.$imblk_host.'\).* \[WARNING\] Authentication failed for user.*$/'
                                              )
                            )                            
                       /*https cutom messages on 443 port*/                            
                      ,Array('port' => 443,//https
                             'regexp' => Array(
                                                '/^.*pureftpdadmin: Failed login to webserver, port:.*from-ip: \['.$imblk_host.'\], user:.*$/'
                                              )
                            )
                     )                            
  );
  $imblk_conf['rules'][] =
    Array('logFile' => '/var/log/apache2/error_log',//filename
          'params'  => Array(
                       Array('port' => 443,//https
                             'regexp' => Array(
                                                '/^\[.*] \[error\] \[client '.$imblk_host.'\] user .*: Password Mismatch$/',
                                                '/^\[.*] \[error\] \[client '.$imblk_host.'\] Access denied: .*$/',
                                                '/^\[.*] \[error\] \[client '.$imblk_host.'\] user .* not found: .*$/'
                                              )
                            )
                     )
  );
Ukázkové nastavení předpokládá záznamy z ssh, pureftpd, a uživatelské zprávy z https (z upraveného pureftpdadmin) do souboru /var/log/auth. Dále se zpracovává soubor /var/log/apache2/error_log kde jsou hlášení od SVN přes https.
Hlášení do logu z php aplikace lze poslat například takto:
 exec('logger -p auth.warning -t mojeaplikace "Failed login to webserver, port: [443], from-ip: [xxx.xxx.xxx.xxx], user: [name]"');

Nastavení na vyhdnocování jen na SSH ze souboru /var/log/messages, by vypadalo jen takto:
$imblk_conf['rules'][] =
  Array('logFile' => '/var/log/messages',
        'params'  => 
          Array(Array('port' => 22,
                      'regexp' => Array('/' . $imblk_prefix . '(?:error: PAM: )?Authentication failure for .* from ' . $imblk_host . '\s*$/',
                                        '/' . $imblk_prefix . 'Failed [-\/\w]+ for .* from ' . $imblk_host . '(?: port \d*)?(?: ssh\d*)?$/',
                                        '/' . $imblk_prefix . 'ROOT LOGIN REFUSED.* FROM ' . $imblk_host . '\s*$/',
                                        '/' . $imblk_prefix . '[iI](?:llegal|nvalid) user .* from ' . $imblk_host . '\s*$/',
                                        '/' . $imblk_prefix . 'User \S+ from ' . $imblk_host . ' not allowed because not listed in AllowUsers$/',
                                        '/' . $imblk_prefix . 'authentication failure; logname=\S* uid=\S* euid=\S* tty=\S* ruser=\S* rhost=' . $imblk_host . '(?:\s+user=.*)?\s*$/',
                                        '/' . $imblk_prefix . 'refused connect from \S+ \(' . $imblk_host . '\)\s*$/',
                                        '/' . $imblk_prefix . 'Address ' . $imblk_host . ' .* POSSIBLE BREAK-IN ATTEMPT\s*$/'
                                        )
                     )
               )
       );
$imblk_prefix a $imblk_host jsou přednastavené v souboru výše.
Další '$imblk_conf['rules'][] = ' by již nebylo třeba

Z výše uvedeného je patrna konfigurace:
Soubor 'logfile' vyhodnocuj na pravidla 'params', definováno polem, např pro port 22.
Pravidlo se skládá z čísla portu 'port' a pole perl regular expresion 'regexp'. Zde jejich vidět několik. Platí zásada udělat přesné vyhodnocovací regular expresion, aby nám někdo, například místo uživatelského jména, nevložil falsifikovaný záznam, který by vedl k blokaci jiné adresy metoda zvaná „log injection“.

Co po nastavení ?

Další možnosti

Zdroje informací: