↓ До тексту
  1. Статті/

fail2ban - автоматичний вартовий проти SSH-брутфорсу

·5 хвилин
cat /etc/passwd
Автор
cat /etc/passwd
Технічний блог про Linux, DevOps та Open Source технології.
Зміст
Захист сервера - частина серії статей.
Частина 1: ця стаття (ти тут)

Будь-який сервер із відкритим SSH за кілька годин в інтернеті починає ловити фоновий брутфорс: боти перебирають root, admin, 123456 цілодобово. Руками банити їх безнадійно. fail2ban робить це сам: читає логи, рахує невдалі входи з кожного IP і, коли той переходить межу, тимчасово блокує його на рівні файрвола. Атакуюча сторона просто дропається.

Ідея стара й проста, але на свіжому Debian 13 «trixie» є кілька нюансів, через які у новачків fail2ban мовчки не банить. Розберемо все на живому прикладі - і з розбором цих граблів у кінці.

Що потрібно зробити?
#

Поставити fail2ban на свіжий Debian 13, увімкнути охорону SSH і - найцікавіше - самому зіграти брутфорсера з сусідньої машини, щоб побачити бан у дії: як після кількох невдалих спроб IP вилітає, а наступні підключення одразу відбиваються.

З чим працюю?
#

Дві одноразові машини в ізольованій пісочниці, обидві Debian 13:

  • srv-01.linuxway.com.ua (10.10.60.102) - сервер, який захищаємо;
  • attacker (10.10.60.103) - з нього ломитимемось.

Так бан буде по-справжньому: fail2ban банитиме реальний чужий IP, а не loopback.

Ставимо fail2ban
#

Встановлення - один рядок. Одразу додаю nftables явно (чому - побачимо в кінці, це головна заковика trixie):

1
apt install -y fail2ban nftables

На Debian пакет приходить уже з розумними дефолтами: сервіс запущений, а jail для SSH увімкнений із коробки. Перевіримо:

1
2
systemctl status fail2ban
fail2ban-client status

Залишається тільки підкрутити поведінку під себе. Правила живуть у /etc/fail2ban/jail.conf, але редагувати його не треба - він перезаписується при оновленні. Замість цього кладемо перевизначення в /etc/fail2ban/jail.local (він читається останнім і переживає apt upgrade):

1
2
3
4
5
6
7
# /etc/fail2ban/jail.local
[sshd]
enabled  = true
maxretry = 3       # скільки невдалих спроб дозволено
findtime = 10m     # у якому вікні їх рахувати
bantime  = 10m     # на скільки банити
ignoreip = 127.0.0.1/8 ::1

Три промахи за 10 хвилин - бан на 10 хвилин. ignoreip - це білий список, щоб не заблокувати самого себе (додай сюди свою робочу адресу, якщо ходиш зі статичного IP). Застосовуємо:

1
2
systemctl reload fail2ban
fail2ban-client status sshd

Усе, пастка на брутфорс зведена. Тепер зіграймо того, від кого захищаємось.

Хтось стукає - і одразу в бан
#

З машини attacker запускаю примітивний перебір паролів до srv-01 - кілька спроб поспіль зі свідомо неправильними паролями:

1
2
3
4
5
6
#!/bin/bash
for p in 111111 222222 333333 letmein; do
  sshpass -p "$p" ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no \
    -o StrictHostKeyChecking=no root@srv-01.linuxway.com.ua id
  sleep 1.5
done

Ліворуч - живий статус fail2ban на сервері, праворуч - те, що бачить брутфорсер. Дивись на лічильник Currently failed: він росте 1, 2, 3 - і на третій спробі IP вилітає в бан, а наступні підключення отримують Connection refused:

Найкраще тут - момент перемикання. Щойно Currently banned стає 1 і в Banned IP list з’являється 10.10.60.103, атакувальник немовби впирається в стіну: сервер більше навіть не питає пароль, а просто відбиває кожне з’єднання. І так - автоматично, без жодного твого втручання.

А що саме сталося?
#

fail2ban не чіпає конфіг sshd і не рве активні сесії - він лише додає кривдника у nftables-сет, а окреме правило відкидає все з нього. Подивитись «начинку» бану можна прямо в nftables:

1
nft list table inet f2b-table
1
2
3
4
5
6
7
set addr-set-sshd {
    type ipv4_addr
    elements = { 10.10.60.103 }
}
chain f2b-chain {
    tcp dport 22 ip saddr @addr-set-sshd reject with icmp port-unreachable
}

А якщо зібрати події з логів (/var/log/fail2ban.log для рішень fail2ban і systemd journal для самих спроб), складається наочна картина атаки: три невдалі входи - і бан.

Звіт fail2ban: три невдалі входи з 10.10.60.103 і бан через nftables reject

Коли сервер постоїть відкритим в інтернеті добу, у цій таблиці будуть вже не один IP, а сотні - з усього світу. І всі вони будуть забанені, поки ти спав.

Debian 13: три заковики, на яких спотикаються
#

Саме на trixie fail2ban поводиться трохи інакше, ніж у старих гайдах. Три речі варто знати, бо через них здається, що «воно не працює»:

  1. Немає /var/log/auth.log. На сучасному Debian немає rsyslog із коробки, і sshd пише лише в systemd journal. Старий рецепт backend = auto тут нічого не побачить - потрібен backend = systemd. На щастя, для sshd-jail Debian уже виставив його за замовчуванням, тому все працює одразу. Але якщо раптом перепишеш backend на файловий - jail осліпне.

  2. Бан іде через nftables, а не iptables. Debian 13 на nftables, і fail2ban тут за замовчуванням використовує nftables. Проблема в іншому: пакет лише рекомендує nftables | iptables. Якщо на машині вже стоїть iptables, apt вважає умову виконаною й не ставить nftables - тоді бан мовчки не накладається (jail активний, спроби рахуються, а блокування немає). Тому й ставимо fail2ban nftables разом, або перевіряємо which nft після встановлення.

  3. sshd-jail уже увімкнений. На Debian його не треба вмикати - він enabled із коробки. Новачки через це двічі описують [sshd] у різних місцях і отримують конфлікт конфігурації. У jail.local достатньо лише підкрутити параметри, а не включати заново.

Куди далі
#

fail2ban - класика, яка чудово робить одну річ: ловить брутфорс по логах на одному хості. Але він бачить лише те, що вже сталося у тебе, і банить по простих правилах. Є сучасніший підхід - CrowdSec: він теж банить, але ще й аналізує поведінку, ділиться репутацією IP зі спільнотою (тебе банять ще до того, як бот дістанеться саме до тебе) і має веб-консоль із метриками.

Його й візьмемо наступним - поставимо поруч, нацькуємо той самий брутфорс і подивимось, чим CrowdSec відрізняється від fail2ban на практиці.

Захист сервера - частина серії статей.
Частина 1: ця стаття (ти тут)

Related

Cowrie - honeypot, що ловить незваних гостей

·5 хвилин
Honeypot - це сервер-приманка: він навмисне виглядає вразливим, впускає зловмисника зі слабким паролем і тихо записує все, що той робить. Піднімаємо Cowrie в Docker, самі граємо «гостя» і дивимось, хто клюне - у реальному часі.