Короткий ответ
Установка пакета ≠ защита. Нужен jail sshd enabled, backend = systemd на Ubuntu с journal, banaction в тот firewall, что фильтрует (UFW → ufw, иначе nftables-multiport), ignoreip на LAN 10.0.20.0/24, проверка set sshd banip и nc с тестового IP. Fail2Ban не замена ключей и CIDR. Если уже стоит и не банит — диагностика jail. Хост host.example (10.0.20.10), пользователь admin.
Симптомы и как отличить
Ставите с нуля или пересобираете «как надо». Отличие от hardening SSH: здесь пакет бана, не sshd_config. Сначала ключи: парольный SSH, sshd. Firewall должен быть включён: UFW/nft.
Возможные причины «поставил — не банит»
Разжёвано в соседней статье. При установке чаще всего: нет jail.local, backend file на journal-only хосте, iptables action при чистом nft без compat.
Диагностика до установки
dpkg -l fail2ban ufw | grep ^ii
sudo ufw status
systemctl is-active ssh
sudo journalctl -u ssh -n 5 --no-pagerВыберите datapath: UFW или nftables.service, не оба.
Решение
1. Пакет
sudo apt-get update
sudo apt-get install -y fail2ban
sudo systemctl enable --now fail2banНе копируйте целиком jail.conf в jail.local навсегда — потом боль при апдейте. Только дельты в jail.d.
2. Jail sshd
Если UFW active:
sudo tee /etc/fail2ban/jail.d/10-sshd.local >/dev/null <<'EOF'
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 10.0.20.0/24
bantime = 1h
findtime = 10m
maxretry = 4
banaction = ufw
[sshd]
enabled = true
backend = systemd
port = ssh
filter = sshd
EOFЕсли UFW нет, nft:
banaction = nftables-multiportsudo fail2ban-client -t
sudo systemctl restart fail2ban
sudo fail2ban-client status
sudo fail2ban-client status sshdfail2ban-client -t проверяет конфиг в актуальных версиях пакета; если флага нет — journalctl -u fail2ban после restart.
3. Доказать бан
С лабораторного адреса, не со своего jump (он в ignoreip):
sudo fail2ban-client set sshd banip 203.0.113.50
sudo fail2ban-client status sshd
sudo ufw status numbered | grep 203 || sudo nft list ruleset | grep 203.0.113.50
sudo fail2ban-client set sshd unbanip 203.0.113.50Ожидание: IP в Banned list и в firewall. Только строка в /var/log/fail2ban.log без правила — action не тот; чините по соседней статье.
4. Не банить себя
ignoreip с LAN и jump. Не 0.0.0.0/0. Мониторинг с WAN-IP вынесите или добавьте /32.
5. Другие jail
nginx-http-auth, recidive — после рабочего sshd. Не включайте всё из jail.conf сразу.
Настройка, которая реально банит, включает мониторинг самого Fail2Ban. journalctl -u fail2ban на ошибки nft/ufw: command not found сразу после install — значит action не из пакета или PATH в systemd unit. Пакет Ubuntu кладёт actions в /etc/fail2ban/action.d/. Не копируйте action с CentOS firewallcmd.
banaction_allports нужен jail'ам, которые банят не один порт (recidive). Для sshd достаточно multiport/ssh.
IPv6: в jail.conf современных версий allowipv6 = auto. Проверьте fail2ban-client get sshd banip умеет v6. UFW должен фильтровать ip6tables/nft inet. Иначе боты с AAAA игнорируют бан.
Логи fail2ban ротируйте (logrotate.d/fail2ban пакетом). Переполненный лог не останавливает бан, но слепит расследование.
Связка с sshd: после ключей-only jail всё ещё ловит Invalid user и Failed publickey (в aggressive). Это шум сканеров. Сузьте 22 CIDR — шум упадёт сильнее любого maxretry.
Не включайте jail sshd дважды под разными именами с одним filter — двойной бан/ошибки. fail2ban-client status список уникален.
Тест без чужого IP: используйте документацию fail2ban-client set sshd banip как выше. Не устраивайте сами brute-force на прод с домашнего IP, если он не в ignoreip — запрётесь.
Документ в заявке: файл 10-sshd.local, вывод status sshd, скрин/текст nft/ufw с тестовым IP. Это определение «банит, а не только пишет лог».
Фильтры: не копируйте failregex с gist без fail2ban-regex на вашем journal. Ubuntu sshd пишет sshd[pid]: внутри journal полей; backend systemd вытаскивает MESSAGE. Если regex ждёт pam_unix а у вас только Failed publickey — поднимите mode или оставьте normal и закройте пароль.
ignorecommand и ignoreregex — для ложных срабатываний CI. Не ignore все Failed.
После apt-get upgrade fail2ban проверьте, что jail.d на месте и status sshd жив. Иногда меняется дефолтный backend.
Храните jail.d в ansible; jail.conf не в git целиком.
Проверка bantime: fail2ban-client get sshd bantime. Согласуйте с политикой IR (час vs сутки). Слишком короткий при открытом пароле бесполезен; слишком длинный при ложном regex — lockout офиса.
Не ставьте action = %(action_mwl)s думая, что это «бан плюс письмо», пока не проверите, что MTA есть. Иначе action падает, бана нет. Начните с banaction = ufw без почты. Письма — второй шаг, когда status sshd уже показывает Banned IP list.
usedns = no уменьшает задержки и ложные баны по PTR. Для SSH обычно no.
Пакет python3-pyinotify не обязателен для sshd jail на systemd backend. Не ставьте лишнее «чтобы fail2ban заработал».
Как проверить, что проблема устранена
sudo fail2ban-client status sshd
systemctl is-enabled fail2ban
sudo grep -E 'enabled|banaction|backend' /etc/fail2ban/jail.d/10-sshd.localТестовый banip виден в UFW/nft. SSH admin с LAN жив. После maxretry с чужого стенда — Ban (осторожно с IP стенда). Ключи SSH по-прежнему no password.
Если не помогло
Переходите в Fail2Ban не блокирует: regex, journalmatch, Docker, IPv6. Не маскируйте sshd.
Профилактика
- jail.d в ansible.
- Алерт по ошибкам action в fail2ban.log.
- После смены UFW↔nft — снова
banipтест. - Слой 0: CIDR на 22.
FAQ
defaults-debian.conf уже enabled sshd
Да на многих Ubuntu. Ваш 10-sshd.local должен дополнять backend/banaction. fail2ban-client get sshd banaction — истина.
recidive jail
Банит повторников дольше. Включайте когда sshd jail точно банит, иначе рецидив по «Found» без Ban бесполезен.
mode=aggressive
Больше матчей, больше ложных. После ключей-only часто достаточно normal + Invalid user.
fail2ban в контейнере
NET_ADMIN и тот же namespace, что фильтр. Обычно ставят на хост.
Нужен ли при VPN-only SSH
Низкий приоритет. Всё равно полезен против ошибок allow. Не вместо VPN.