Konfiguracja fail2ban i automatyczne blokowanie ataków brute force
Jak skonfigurować fail2ban czysto przez jail.local, uruchomić jail sshd na wszystkich czterech aktualnych dystrybucjach LTS oraz sprawdzać i zdejmować blokady. Do tego błędy, które potrafią zatrzymać cię po drodze.
Każdy serwer z publicznym adresem IPv4 dostaje pierwsze próby logowania na porcie 22 już kilka minut po uruchomieniu. To nie jest ukierunkowany atak, tylko szum tła. fail2ban czyta wiersze logu z tych nieudanych prób i każe firewallowi blokować adres źródłowy przez określony czas. Narzędzie jest spakietowane we wszystkich omawianych tu dystrybucjach i konfiguruje się je w kilka minut. Pułapki czają się gdzie indziej: na Debianie 12 fail2ban chętnie startuje z błędem konfiguracji, na części systemów blokuje tylko pozornie, bo żaden pakiet nie zostaje naprawdę odrzucony, a jeden nieprzemyślany wiersz w jail.local zamyka dostęp tobie samemu.
Ten artykuł zajmuje się dokładnie tymi miejscami, dla Debiana 13, Debiana 12, Ubuntu 24.04 LTS i Ubuntu 22.04 LTS.
Dlaczego jail.local, a nigdy jail.conf
Plik /etc/fail2ban/jail.conf należy do pakietu. Przy każdej aktualizacji może zostać nadpisany, a wtedy dpkg w najlepszym razie zapyta cię o zdanie, w najgorszym, podczas nienadzorowanej aktualizacji, nie zapyta w ogóle. Twoje zmiany mieszkają dlatego w /etc/fail2ban/jail.local. Ten plik nie istnieje po instalacji, zakładasz go samodzielnie, a znaleźć się w nim mają wyłącznie te wartości, które naprawdę zmieniasz.
Ważna jest kolejność, w jakiej fail2ban czyta pliki. Strona podręcznika jail.conf(5) wymienia ją wprost:
jail.confjail.d/*.confw kolejności alfabetycznejjail.localjail.d/*.localw kolejności alfabetycznej
Wygrywają pliki wczytane później. Konkretnie znaczy to tyle: twoja jail.local nadpisuje również ustawienia z /etc/fail2ban/jail.d/defaults-debian.conf, które przynosi pakiet dystrybucji. Wiele poradników twierdzi coś przeciwnego i dlatego zaleca własny plik w jail.d/. Nie ma takiej potrzeby. Katalog jail.d/ ma sens tylko wtedy, gdy chcesz wdrażać poszczególne jaile pojedynczo przez system zarządzania konfiguracją.
Co twoja dystrybucja ustawia za ciebie
Największa różnica między tymi czterema systemami tkwi nie w samym fail2ban, tylko w jednym pliku, który pakiet umieszcza w /etc/fail2ban/jail.d/defaults-debian.conf. Obejrzyj go na samym początku:
cat /etc/fail2ban/jail.d/defaults-debian.conf
| System | fail2ban | Zawartość defaults-debian.conf |
|---|---|---|
| Debian 13 | 1.1.0 | banaction = nftables, banaction_allports = nftables[type=allports], dla [sshd] dodatkowo backend = systemd, journalmatch = _SYSTEMD_UNIT=ssh.service + _COMM=sshd oraz enabled = true |
| Debian 12 | 1.0.2 | tylko [sshd] oraz enabled = true |
| Ubuntu 24.04 | 1.0.2 | banaction = nftables, banaction_allports = nftables[type=allports], backend = systemd, do tego [sshd] z enabled = true |
| Ubuntu 22.04 | 0.11.2 | tylko [sshd] oraz enabled = true |
Z tego wynika prawie wszystko inne. Debian 13 i Ubuntu 24.04 czytają fabrycznie journal systemd i blokują przez nftables. Debian 12 i Ubuntu 22.04 wracają do wartości domyślnych z jail.conf, czyli do banaction = iptables-multiport oraz backend = auto. A auto wyraźnie nie oznacza „wybierz sobie to, co pasuje”. Komentarz w jail.conf mówi, że auto próbuje po kolei pyinotify i polling. Oba sposoby opierają się na plikach. Wartość auto nigdy nie wybiera journala. Bez pliku /var/log/auth.log jail sshd trafia tam w próżnię.
Instalacja i pierwsza kontrola
apt update
apt install -y fail2ban
Na Debianie 12 i Ubuntu 22.04 dochodzi jeszcze jeden pakiet, jeśli chcesz analizować journal zamiast pliku logu:
apt install -y python3-systemd
Na Debianie 13 python3-systemd jest twardą zależnością pakietu i jest już zainstalowany. Na Debianie 12 ma status jedynie zalecanego, co bywa na tym systemie najczęstszym źródłem błędów. Sprawdź następnie wersję i stan usługi:
apt-cache policy fail2ban
fail2ban-client --version
systemctl status fail2ban --no-pager
Jeśli widzisz tu już active (running), połowa pracy jest za tobą. Jeśli stoi tam failed, przeskocz od razu do rozdziału o komunikatach błędów.
Budowa własnego pliku jail.local
Załóż plik /etc/fail2ban/jail.local. Ta wersja działa na wszystkich czterech systemach, o ile zainstalowany jest python3-systemd:
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.7
bantime = 1h
findtime = 10m
maxretry = 5
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 7d
banaction = nftables
banaction_allports = nftables[type=allports]
[sshd]
enabled = true
backend = systemd
port = ssh
maxretry = 4
bantime = 2h
Znaczenie tych wartości bez owijania w bawełnę: findtime to przesuwane okno czasowe, maxretry liczba nieudanych prób w tym oknie, bantime czas trwania blokady. Cztery nieudane próby w ciągu dziesięciu minut kończą się tutaj dwugodzinną blokadą. Wartości czasu rozumieją przyrostki m, h, d i w, a sama liczba oznacza sekundy. bantime o wartości -1 blokuje na stałe.
Najciekawsza jest opcja bantime.increment. Dzięki niej fail2ban podwaja czas blokady przy każdym nawrocie tego samego adresu IP, z górnym ograniczeniem ustawionym przez bantime.maxtime. Jednorazowa literówka prawdziwego użytkownika kosztuje dwie godziny, a uparty bot po kilku rundach ląduje przy tygodniu. Ta funkcja istnieje od fail2ban 0.11, jest więc dostępna także na Ubuntu 22.04. Żeby działała ponad restartami, baza danych musi zachować historię, czym steruje dbpurgeage w /etc/fail2ban/fail2ban.conf, domyślnie jeden dzień. Jeśli ustawiasz bantime.maxtime = 7d, podnieś też dbpurgeage w pliku fail2ban.local.
W tym bloku czyhają dwie pułapki. Po pierwsze port = ssh: rozwija się to przez /etc/services do portu 22. Jeśli twoja usługa SSH działa na porcie 2222, musi tam stać port = 2222, inaczej powstanie reguła firewalla dla portu, do którego nikt nie puka. Po drugie ignoreip: wpisz tam swój stały adres IP z biura, ale nigdy całej sieci dostawcy. I nie polegaj wyłącznie na tym wpisie. Wartość ignoreself i tak stoi domyślnie na true i chroni własne adresy IP serwera.
Odstępstwa w zależności od systemu
Na Debianie 12 masz dwie równorzędne drogi. Albo zostajesz przy backend = systemd i instalujesz python3-systemd, albo instalujesz rsyslog, pomijasz backend i pracujesz z /var/log/auth.log. Poza tym na Debianie 12 warto świadomie ustawić banaction. Pakiet zaleca nftables albo iptables, a apt instaluje z tego z reguły tylko nftables. Wartość domyślna z jail.conf brzmi jednak iptables-multiport. Na oszczędnej instalacji fail2ban sięga wtedy po plik binarny, którego w ogóle nie ma.
Na Ubuntu 22.04 pakiet rsyslog wchodzi w skład standardowej instalacji serwerowej, plik /var/log/auth.log istnieje, a jail działa bez żadnych zabiegów. Jeśli nie chcesz tam niczego przestawiać, po prostu pomiń backend i banaction w swojej jail.local. fail2ban 0.11.2 zna wprawdzie akcję nftables, ale droga przez iptables-multiport jest na tym systemie bardziej wydeptana.
Na Debianie 13 i Ubuntu 24.04 twoja jail.local pokrywa się z ustawieniami pakietu. To nie szkodzi, robi konfigurację jedynie jawną, a przez to czytelną.
Wczytanie zmian i sprawdzenie, czy naprawdę działają
Wczytanie konfiguracji bez utraty istniejących blokad:
fail2ban-client reload
Na Debianie 12 i Ubuntu 24.04 fail2ban 1.0.2 zgłasza przy starcie i przy przeładowaniu ostrzeżenie 'allowipv6' not defined in 'Definition'. Jest nieszkodliwe, usługa mimo to pracuje dalej normalnie.
Polecenie systemctl restart fail2ban jest rzadko potrzebne i kasuje aktywne blokady. Potem przychodzi kolej na zapytania o stan:
fail2ban-client ping
fail2ban-client status
fail2ban-client status sshd
Najwięcej mówi ostatnie polecenie. Pokazuje Currently failed, Total failed, liczbę blokad oraz listę zablokowanych adresów. Dokładnie w tym miejscu kończy się większość poradników i dokładnie tutaj zaczyna się problem: jail, który startuje czysto i melduje „0 banned”, może być całkowicie ślepy. Jail bez trafień wygląda identycznie jak jail, który czyta niewłaściwe źródło logu.
Trzy kontrole, które tę różnicę uwidaczniają. Po pierwsze: czy filtr w ogóle widzi te wiersze?
fail2ban-regex systemd-journal /etc/fail2ban/filter.d/sshd.conf
Przy backendzie opartym na plikach zamiast tego:
fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf
Na końcu pojawia się wiersz w rodzaju Lines: 4211 lines, 0 ignored, 137 matched, 4074 missed. Jeśli stoi tam 0 matched, chociaż w logu wyraźnie widać nieudane próby, nie pasuje filtr albo wybór wpisów z journala.
Po drugie: jakiego filtra journala jail faktycznie używa?
fail2ban-client get sshd journalmatch
Odpowiedź wypada różnie w zależności od systemu, a ta różnica ma znaczenie. Debian 13 melduje _SYSTEMD_UNIT=ssh.service + _COMM=sshd, natomiast Ubuntu 24.04 _SYSTEMD_UNIT=sshd.service + _COMM=sshd. Unit nazywa się tam więc sshd.service, a na Debianie ssh.service. Na Debianie 12 i Ubuntu 22.04 w stanie fabrycznym wraca No journal match filter set, bo backend systemd nie jest tam ustawiony domyślnie, a jail czyta plik logu. To nie jest błąd, tylko potwierdzenie, że filtra journala w ogóle nie ma. Dopiero backend = systemd w twojej jail.local sprawia, że na tych systemach jakiś filtr zaczyna działać.
Poszczególne warunki są rozdzielone znakiem + i łączone jak alternatywa, czyli logiczne „lub”. Zapis _SYSTEMD_UNIT=ssh.service + _COMM=sshd pasuje więc do wszystkiego z unitu ssh.service albo do każdego procesu o nazwie sshd. Do kontrolnego sprawdzenia w journalu weź dokładnie tę nazwę unitu, którą zwróciło powyższe polecenie:
journalctl -u ssh --no-pager -n 50
journalctl _COMM=sshd --no-pager -n 50
Na Ubuntu 24.04 pierwszy wiersz brzmi odpowiednio journalctl -u sshd --no-pager -n 50. Kto weźmie tu złą nazwę, zobaczy pusty wynik, a potem będzie szukał w zupełnie niewłaściwym miejscu.
Jeśli pojawiają się tu wiersze z Failed password for invalid user, a fail2ban mimo to nic nie liczy, winny jest wybór wpisów z journala. Nabiera to znaczenia, gdy używasz OpenSSH z backportów albo przestawiasz usługę na aktywację przez socket. Od OpenSSH 9.8 proces potomny nazywa się sshd-session, a nie sshd, a przy aktywacji przez socket unit nie nazywa się już ssh.service. Wtedy dopisujesz w swojej jail.local (na Ubuntu 24.04 z _SYSTEMD_UNIT=sshd.service):
[sshd]
journalmatch = _SYSTEMD_UNIT=ssh.service + _COMM=sshd + _COMM=sshd-session
Po trzecie twarda próba. Zablokuj ręcznie adres z sieci dokumentacyjnej i sprawdź, czy dociera on do firewalla:
fail2ban-client set sshd banip 203.0.113.10
Polecenie odpowiada wartością 1, czyli jednym zablokowanym adresem. To, jakim narzędziem udowodnisz tę blokadę, zależy od obowiązującej banaction, a ta jest domyślnie ustawiona różnie w poszczególnych dystrybucjach:
| System | banaction fabrycznie | Sprawdzenie aktywnej blokady |
|---|---|---|
| Debian 13 | nftables | nft list table inet f2b-table |
| Ubuntu 24.04 | nftables | nft list table inet f2b-table |
| Debian 12 | iptables-multiport | iptables -n -L f2b-sshd |
| Ubuntu 22.04 | iptables-multiport | iptables -n -L f2b-sshd |
Starszy Debian 11 zachowuje się przy tym jak Debian 12. Skąd bierze się ta różnica, widać w /etc/fail2ban/jail.d/defaults-debian.conf: tylko Debian 13 i Ubuntu 24.04 przynoszą tam wpis banaction = nftables, pozostałe zostają przy wartości domyślnej iptables-multiport z jail.conf. Kto nie chce rozróżniać tego serwer po serwerze, wpisuje banaction = nftables do własnej jail.local, tak jak w przykładzie powyżej, i sprawdza potem wszędzie poleceniem nft.
Przy akcji nftables istnieje tabela f2b-table w rodzinie inet, w niej łańcuch f2b-chain z priorytetem filter - 1 oraz zbiór o nazwie addr-set-sshd. To tam musi stać 203.0.113.10 jako element. Łańcucha f2b-sshd na tych systemach nie ma, polecenie iptables -n -L f2b-sshd odpowiada tam komunikatem No chain/target/match by that name. Odwrotnie, nft list table inet f2b-table melduje na Debianie 12 i Ubuntu 22.04 Error: No such file or directory, dopóki pracuje tam iptables-multiport. Żaden z tych komunikatów nie dowodzi zepsutej konfiguracji, a jedynie tego, że polecenie nie pasuje do ustawionej akcji.
Przy akcji iptables adres 203.0.113.10 musi odpowiednio stać w łańcuchu f2b-sshd. Dopiero gdy adres IP pojawi się w firewallu, łańcuch od wiersza logu aż po odrzucony pakiet jest zamknięty. Nie zapomnij posprzątać po teście.
Zdejmowanie blokad
Zdjęcie pojedynczego adresu z konkretnego jaila:
fail2ban-client set sshd unbanip 203.0.113.10
Adres ze wszystkich jaili naraz:
fail2ban-client unban 203.0.113.10
A w sytuacji awaryjnej wszystko od zera:
fail2ban-client unban --all
Ostatnie polecenie przydaje się wtedy, gdy zablokowałeś sam siebie i siedzisz przy serwerze na konsoli awaryjnej. Zdejmuje wszystkie blokady bez restartu usługi. Jeśli w ogóle nie dostajesz się już na serwer, pozostaje tylko droga przez konsolę w panelu klienta. Na serwerach root KVM i serwerach dedykowanych KernelHost znajdziesz tam sesję VNC, która działa niezależnie od SSH. To także powód, dla którego na serwerze bez drugiej drogi dostępu nie powinieneś ustawiać bantime = -1.
Zdjęta blokada wraca natychmiast, jeśli nieudane próby są jeszcze liczone w oknie czasowym. Dlatego unban kasuje przy okazji także powiązane z nią nieudane próby. Kto ma zostać trwale wolny, trafia do ignoreip, a po tym wpisie wykonujesz fail2ban-client reload.
Współpraca z UFW i nftables
Tutaj powstaje szkoda, którą najtrudniej zauważyć, bo fail2ban wesoło melduje kolejne blokady, podczas gdy pakiety spokojnie przechodzą.
UFW to nakładka na iptables. Przy starcie albo przeładowaniu zapisuje tablicę filtrów od nowa przez iptables-restore. Znikają przy tym bezpowrotnie łańcuchy założone przez fail2ban, a fail2ban sam ich z powrotem nie tworzy. Po każdym ufw enable, ufw disable albo ufw reload obowiązuje więc zasada: systemctl restart fail2ban. Do tego momentu fail2ban-client status sshd pokazuje długą listę zablokowanych adresów IP, z których w rzeczywistości nie jest blokowany ani jeden.
Czystszym rozwiązaniem jest pozwolić fail2ban blokować bezpośrednio przez UFW. Akcja leży w /etc/fail2ban/action.d/ufw.conf i jest zawarta we wszystkich czterech dystrybucjach. W jail.local:
[DEFAULT]
banaction = ufw
banaction_allports = ufw
Blokady lądują wtedy jako reguły w samym UFW i przeżywają jego przeładowanie. Kontrola:
ufw status numbered
Zablokowane adresy pojawiają się tam jako DENY IN wysoko na liście. Ważne, żeby reguły blokujące stały przed twoją regułą zezwalającą na port 22, inaczej najpierw zadziała zezwolenie. Dołączona akcja wstawia je dlatego na sam początek poleceniem insert.
Kto nie używa UFW i zamiast tego utrzymuje własny zestaw reguł w /etc/nftables.conf, ma pokrewny problem. Wpis flush ruleset na początku tego pliku, tak jak proponuje go standardowy szablon, kasuje przy przeładowaniu również f2b-table. Albo rezygnujesz z globalnego czyszczenia i opróżniasz wyłącznie własną tabelę, albo podwieszasz restart fail2ban pod usługę nftables. Zaletą własnej tabeli fail2ban jest to, że z priorytetem filter - 1 działa przed zwykłą tablicą filtrów, a tym samym także przed regułami z iptables-nft.
Nie mieszaj banaction = nftables z własnym zestawem reguł iptables w oczekiwaniu, że iptables -L pokaże blokady. Obie drogi działają równolegle, ale każde narzędzie pokazuje tylko własny zestaw reguł. Sprawdzaj zawsze tym narzędziem, które pasuje do ustawionej banaction.
Komunikaty błędów dosłownie i co za nimi stoi
Logi znajdziesz, w zależności od systemu, w dwóch miejscach:
journalctl -u fail2ban --no-pager -n 100
tail -n 100 /var/log/fail2ban.log
Failed during configuration: Have not found any log file for sshd jail
Znak rozpoznawczy Debiana 12. Jail pracuje z backend = auto, szuka /var/log/auth.log, a pliku nie ma, bo nie zainstalowano rsyslog i wszystko ląduje w journalu. Usługa w ogóle nie startuje, systemctl status fail2ban pokazuje Failed with result 'exit-code'. Dwa rozwiązania: albo apt install -y rsyslog i jednorazowy restart, albo wpisanie w jail.local pod [sshd] wiersza backend = systemd. Druga droga jest nowocześniejsza, wymaga jednak kolejnego pakietu.
Backend 'systemd' failed to initialize due to No module named 'systemd'
Ustawiłeś backend = systemd, ale brakuje pythonowego dowiązania do journala. Na Debianie 12 python3-systemd jest tylko zalecany i przy instalacjach bez pakietów zalecanych nie zostaje dociągnięty. Zaradzisz temu tak:
apt install -y python3-systemd
systemctl restart fail2ban
Failed to access socket path: /var/run/fail2ban/fail2ban.sock. Is fail2ban running?
Klient nie dociera do serwera. W zdecydowanej większości przypadków usługa po prostu nie działa, bo wyłożyła się na błędzie konfiguracji. Najpierw przeczytaj systemctl status fail2ban, potem journal. Rzadziej po twardym przerwaniu zostaje osierocony plik socketu, a wtedy start serwera melduje Server already running. W takim przypadku usuń plik socketu i uruchom usługę ponownie.
NOK: ('sshd',)
Odpowiedź na fail2ban-client status sshd, gdy takiego jaila w czasie działania nie ma. Albo brakuje enabled = true, albo pomyliłeś się w nazwie, albo błąd składni gdzieś wyżej w jail.local połknął całą sekcję. Które jaile naprawdę działają, pokazuje fail2ban-client status. To, co fail2ban poskładał ze wszystkich plików, pokazuje zrzut konfiguracji:
fail2ban-client -d
Error banning oraz iptables not found
Jail liczy poprawnie, ale blokada rozbija się o akcję. Typowe dla oszczędnych instalacji Debiana 12, na których jest tylko nftables, podczas gdy wartość domyślna brzmi iptables-multiport. Albo doinstaluj brakujące narzędzie poleceniem apt install -y iptables, albo przełącz się w jail.local na banaction = nftables. Tę drugą drogę Debian 13 i Ubuntu 24.04 i tak wybierają same z siebie.
Kiedy nic już nie pomaga: uporządkowany odwrót
Ponieważ wszystkie zmiany stoją w jail.local, droga powrotna jest krótka. Odłóż plik na bok, zrestartuj usługę i jesteś z powrotem przy ustawieniach pakietu:
mv /etc/fail2ban/jail.local /root/jail.local.bak
systemctl restart fail2ban
fail2ban-client status
Jeśli teraz działa, błąd tkwi w twojej konfiguracji, więc wstawiaj bloki z powrotem pojedynczo. Jeśli i tak nie działa, winne jest środowisko, czyli brakujące źródło logu albo brakujące narzędzie firewalla. Właśnie dlatego jail.conf ma pozostać nietknięty: taki odwrót w dziesięć sekund jest możliwy tylko dopóty, dopóki plik z pakietu pozostaje w stanie fabrycznym.
Na koniec kilka słów o proporcjach. fail2ban zmniejsza szum w logach i zatrzymuje powolne, powtarzane próby logowania. Poważny atak z dużego botnetu używa każdego adresu tylko raz i przechodzi obok każdego ograniczenia częstotliwości. Skuteczniejszym krokiem przeciw atakom brute force na SSH pozostaje wyłączenie logowania hasłem na rzecz kluczy. A przeciw atakom wolumetrycznym i tak pomaga wyłącznie filtrowanie w sieci, u nas filtrowanie Arbor w czasie rzeczywistym o wydajności 3,2 Tbps bezpośrednio w centrum danych we Frankfurcie nad Menem oraz do 17 Tbps globalnej pojemności filtrowania w taryfach Professional. fail2ban jest warstwą pod spodem, na hoście, i tam wywiązuje się ze swojego zadania niezawodnie, gdy tylko zgadzają się trzy rzeczy: właściwe źródło logu, właściwa akcja firewalla oraz jail.local, którą w razie wątpliwości usuniesz jednym ruchem.
Najczęstsze pytania
Czy naprawdę muszę zostawić jail.conf nietknięty?
Dlaczego fail2ban nie startuje na Debianie 12 zaraz po instalacji?
Co dokładnie oznacza backend = auto?
Jak zdjąć założoną blokadę?
Dlaczego fail2ban blokuje, a atakujący i tak przechodzi?
Po czym poznam, że fail2ban naprawdę działa?
Czy fail2ban zastępuje ochronę DDoS?
2026 KernelHost GmbH. Wszelkie prawa zastrzeżone. Ten poradnik jest chroniony prawem autorskim. Publikowanie go w innych serwisach, w całości, we fragmentach lub w zmienionej formie, wymaga naszej pisemnej zgody. Cytaty z podaniem źródła i z linkiem są jak najbardziej mile widziane.

