Jak jsem na Raspberry Pi nasadil Fail2Ban pro Apache a SSH

Veřejný web dříve nebo později najdou automatické skenery. Prakticky si ukážeme instalaci Fail2Ban na Raspberry Pi OS, ochranu Apache před hledáním neexistujících PHP skriptů a napojení SSH na systemd journal.

Veřejný web dříve nebo později najdou automatické skenery. Na mém Raspberry Pi se to projevilo desítkami podobných záznamů v Apache error logu:


[Tue Oct 13 08:22:51.047444 2026] [php:error] [client 192.0.2.123:64739] script '/var/www/example/public/mail.php' not found or unable to stat
[Tue Oct 13 08:22:52.352933 2026] [php:error] [client 192.0.2.123:51802] script '/var/www/example/public/wp-config.php' not found or unable to stat

Nešlo o úspěšné napadení. Text not found or unable to stat naopak říká, že hledaný soubor na serveru nebyl. Skenery postupně zkoušejí názvy známých webshellů, konfiguračních souborů a děravých PHP aplikací. Přesto vytvářejí zbytečnou zátěž a rychle plní log.

Na takové opakované požadavky se hodí Fail2Ban. Sleduje logy, pomocí filtrů v nich rozpoznává podezřelé chování a po překročení nastaveného limitu přidá IP adresu do firewallového banu.

Kájovo tip
Fail2Ban nenahrazuje aktualizace systému, bezpečnou konfiguraci Apache ani oprávnění souborů. Omezuje opakované útoky, ale neopraví zranitelnou aplikaci.

Použité prostředí

Tento postup jsem ověřil na následující kombinaci:

  • Raspberry Pi OS založený na Debianu,
  • Apache jako webový server,
  • PHP obsluhované přímo z Apache,
  • systemd a systemd journal,
  • Fail2Ban 1.0.2.

Na jiné distribuci se mohou lišit názvy balíčků, umístění logů i výchozí firewallová akce. Není dobré bez ověření kopírovat cestu k logu z cizího návodu.

Instalace Fail2Ban

Na Raspberry Pi OS stačí distribuční balíček:


sudo apt update
sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban
sudo systemctl is-active fail2ban
fail2ban-client --version

V mém případě byla výsledkem aktivní služba a verze:


active
Fail2Ban v1.0.2

Nejdřív najít správný Apache error log

Apache může používat společný /var/log/apache2/error.log, ale jednotlivý virtual host může mít vlastní soubor. Aktivní direktivy ErrorLog najdeme takto:


sudo grep -Rns '^[[:space:]]*ErrorLog' /etc/apache2/apache2.conf /etc/apache2/sites-enabled

Na mém serveru výstup ukázal obecný log i samostatný log webu:


/etc/apache2/apache2.conf:134:ErrorLog ${APACHE_LOG_DIR}/error.log
/etc/apache2/sites-enabled/000-default-ssl.conf:11:ErrorLog ${APACHE_LOG_DIR}/my-page.com-error.log

První test nad obecným error.log nenašel žádnou shodu. Nebyla to chyba filtru — podezřelé požadavky byly v logu konkrétního virtual hostu.

Ověření filtru apache-noscript

Balíček Fail2Ban už obsahuje filtr apache-noscript, který umí rozpoznat požadavky na neexistující skripty. Před zapnutím jailu je důležité filtr otestovat nad skutečným logem:


sudo fail2ban-regex /var/log/apache2/my-page.com-error.log /etc/fail2ban/filter.d/apache-noscript.conf

V mém logu filtr našel 257 relevantních řádků z 258:


Failregex: 257 total
Lines: 258 lines, 0 ignored, 257 matched, 1 missed

Jediný nezachycený řádek byl nesouvisející zákaz výpisu adresáře .well-known. To je správně: filtr nemá banovat libovolnou chybu Apache, ale pouze odpovídající pokusy o spuštění neexistujících skriptů.

Kájovo tip
Pokud test vrátí 0 matched, jail zatím nezapínejte. Nejprve zkontrolujte cestu k logu a formát jeho řádků. Jinak vznikne falešný pocit ochrany.

Jail pro neexistující PHP skripty

Distribuční jail.conf se přímo neupravuje, protože jej může přepsat aktualizace balíčku. Vlastní nastavení patří do souboru s příponou .local:


sudoedit /etc/fail2ban/jail.d/my-page-apache.local

Obsah:


[apache-noscript]
enabled = true
port = http,https
filter = apache-noscript
logpath = /var/log/apache2/my-page.com-error.log
backend = auto

maxretry = 5
findtime = 2m
bantime = 24h

Význam tří nejdůležitějších hodnot:

  • maxretry = 5 — pět zachycených pokusů spustí ban,
  • findtime = 2m — pokusy se musí vejít do dvou minut,
  • bantime = 24h — IP zůstane blokovaná 24 hodin.

Pro přísnější ochranu lze později snížit maxretry například na 3. Hodnota 1 už může způsobit ban po jediném starém odkazu nebo překlepu, takže ji bez dobrého důvodu nedoporučuji.

Když chybí /var/log/auth.log

Po vytvoření Apache jailu jsem otestoval celou konfiguraci:


sudo fail2ban-client -t

Test nejprve skončil chybou:


ERROR Failed during configuration: Have not found any log file for sshd jail

Výchozí SSH jail hledal /var/log/auth.log, který na tomto systému neexistoval. SSH události byly pouze v systemd journalu. Řešením bylo nainstalovat Python rozhraní k journalu a nastavit SSH jail na backend systemd:


sudo apt install -y python3-systemd
sudoedit /etc/fail2ban/jail.d/sshd-systemd.local

Obsah souboru:


[sshd]
enabled = true
backend = systemd

Backend systemd nečte klasický logpath. Používá journalmatch dodaný SSH filtrem a události načítá přímo ze systemd journalu.

Kontrola konfigurace a spuštění

Po opravě SSH backendu už test prošel:


sudo fail2ban-client -t
sudo systemctl restart fail2ban
sudo systemctl is-active fail2ban
sudo fail2ban-client status
sudo fail2ban-client status apache-noscript
sudo fail2ban-client status sshd

Očekávaný stav je aktivní služba a seznam obsahující jaily apache-noscript a sshd. U SSH se místo souboru zobrazí journal filtr podobný tomuto:


Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd

Varování 'allowipv6' not defined ... Using default one: 'auto' bylo v této verzi pouze informativní. Samotný test skončil hláškou OK: configuration test is successful.

Bezprostředně po restartu se také může stát, že první fail2ban-client status ještě nenajde socket. Pokud je služba aktivní a následující dotaz už funguje, šlo jen o krátký souběh při startu služby.

Jak poznat, že ban opravdu funguje

Stav Apache jailu zobrazíme příkazem:


sudo fail2ban-client status apache-noscript

Po prvním skutečném banu vypadal výsledek přibližně takto:


Currently failed: 0
Total failed: 8
Currently banned: 1
Total banned: 1
Banned IP list: 192.0.2.123

Currently banned znamená počet IP blokovaných právě teď. Total banned je počet banů od posledního spuštění daného jailu. Po uplynutí 24 hodin první hodnota klesne, zatímco celkový počet zůstane do restartu jailu zachovaný.

Události služby lze sledovat živě:


sudo journalctl -u fail2ban -f

Proč při maxretry 5 vzniklo osm řádků?

Fail2Ban neblokuje požadavek před jeho prvním zapsáním. Pracuje v tomto pořadí:

  1. Apache přijme požadavek a zapíše chybu.
  2. Fail2Ban načte nový řádek a vyhodnotí filtr.
  3. Po dosažení maxretry nainstaluje firewallové pravidlo.
  4. Teprve další síťový provoz z dané IP narazí na ban.

Útočný skener navíc posílal více požadavků paralelně z několika spojení. Než Fail2Ban zpracoval pátý řádek, měl Apache další požadavky už přijaté. Proto se do logu dostalo sedm nebo osm chyb. Je to očekávané chování, nikoli selhání banu.

Snížení limitu na 3 zkrátí reakci, ale ani potom nelze slíbit přesně tři řádky. Chceme-li zabránit samotnému vzniku PHP chyb, je potřeba navíc zachytit neexistující *.php už v konfiguraci Apache. Fail2Ban řeší opakujícího se klienta, nikoli první zalogování.

Stejně tak nelze věřit hodnotě Referer. Skener si může bez problémů poslat například https://www.google.com/, aniž by z Googlu skutečně přišel.

Změna limitu bez restartu celé služby

Trvalou hodnotu upravíme v lokálním jailu:


sudoedit /etc/fail2ban/jail.d/my-page-apache.local

Například:


maxretry = 3

Potom konfiguraci ověříme a znovu načteme pouze příslušný jail:


sudo fail2ban-client -t
sudo fail2ban-client reload apache-noscript
sudo fail2ban-client get apache-noscript maxretry

Poslední příkaz ukáže hodnotu, kterou běžící jail skutečně používá.

Co dělat při vlastním omylem způsobeném banu

Testovat pět neexistujících PHP adres z vlastní veřejné IP není dobrý nápad. Pokud se přesto zablokujete a máte k serveru jinou cestu, konkrétní IP lze odblokovat:


sudo fail2ban-client set apache-noscript unbanip VASE_IP_ADRESA

Nevkládejte ale široké rozsahy do výjimek jen kvůli pohodlí. Každá výjimka oslabuje ochranu a u dynamické veřejné adresy může časem patřit někomu jinému.

Shrnutí

Nejdůležitější část celého nasazení nebyla samotná instalace balíčku, ale ověření skutečného toku logů. Obecný Apache log byl prázdný, web používal vlastní ErrorLog a SSH události byly pouze v systemd journalu. Teprve test přes fail2ban-regex a kontrola obou aktivních jailů potvrdily, že ochrana opravdu funguje.

Výsledkem je jednoduchá vícevrstvá ochrana:

  • Apache dál vrací 404 na neexistující skripty,
  • Fail2Ban po několika pokusech odřízne opakujícího se skenera,
  • SSH jail čte přihlašovací události ze systemd journalu.

Zdroje

Fail2Ban, Raspberry Pi, Bezpečnost, Webserver, Návod

-

Líbil se vám článek?

Nasdílejte ho svým přátelům na sociálních sítích.

Pinterest

Názory, připomínky a jiné komentáře

Zatím nikdo nic nenapsal. Buďte první :-).

Vložit nový komentář

Jsem programátor, horolezec a tak trochu FlexiBee fanatik :-).
Na CharlieBlogu sepisuji své nápady a poznatky už od roku 2006.

Powered by CharlieBlog Engine v3.0 - Login