Fail2ban на VPS: закрыть SSH от брутфорса без блокировки себя
Fail2ban на VPS: закрыть SSH от брутфорса без блокировки себя
Пока dig/scan идёт по всему интернету, ваш :22 уже в чужом скрипте. Ключи вместо пароля и UFW — база. Fail2ban — следующий слой: он читает логи SSH и временно банит IP, который слишком часто ошибается. Не вместо ключей и firewall, а вместе с ними.
Ниже — рабочая настройка на Ubuntu 22.04/24.04: jail для sshd, whitelist своего VPN, разбан себя одной командой и типичные ошибки, из‑за которых админ сам себе закрывает вход.
О базовой безопасности Ubuntu (ключи, UFW, обновления) мы писали в гайде по настройке VPS. Как зайти на сервер с Windows, macOS и Linux — в статье про SSH. Здесь — только Fail2ban поверх уже живого SSH.
Зачем fail2ban, если уже есть ключи и UFW
UFW режет порты: закрыл лишнее, оставил 22 (или свой порт) и 80/443. Ключи убирают парольный брут в классическом виде — без PasswordAuthentication бот с словарём почти бесполезен.
Но порт SSH всё равно торчит в интернет. Боты долбят его сутками: Invalid user, Failed password, Connection closed. Даже при входе только по ключу это шумит в логах, грузит sshd и иногда маскирует настоящую попытку взлома.
Fail2ban делает простое:
- следит за логами (обычно
journaldили/var/log/auth.log); - считает неудачные попытки с одного IP за окно времени (
findtime); - при превышении
maxretryдобавляет IP в ban наbantimeчерез firewall (nftables/iptables/UFW-цепочку).
Это не IDS и не «полная защита». Это автомат, который отсекает шум и дешёвый брут. Ключи, отключение пароля, смена порта (по желанию) и UFW остаются обязательными. Fail2ban — страховочный слой, когда кто‑то всё же угадал слабый пароль или вы временно включили парольный вход для аварии.
Типичная картина на свежем VPS без fail2ban за сутки: сотни–тысячи строк в auth.log с Invalid user admin, root, ubuntu. С jail’ом через час–два большинство IP уже в бане, а лог читается без боли.
Установка на Ubuntu 22.04 и 24.04
Пакет есть в стандартных репозиториях. На чистой системе:
sudo apt update
sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban
sudo systemctl status fail2ban --no-pager
Проверьте версию и что демон жив:
fail2ban-client version
sudo fail2ban-client ping
# Ответ: pong
Конфиги лежат в /etc/fail2ban/. Править jail.conf напрямую не стоит — его перезапишут при обновлении пакета. Создайте jail.local (или файлы в jail.d/) — они перекрывают дефолты.
Минимальный скелет:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# лучше не копировать целиком, а держать короткий jail.local —
# ниже рабочий вариант с нуля
Рекомендуемый подход: короткий /etc/fail2ban/jail.local только с тем, что меняете.
На Ubuntu 22.04/24.04 backend по умолчанию обычно systemd (чтение journal) или auto. Для SSH этого достаточно — отдельно монтировать auth.log не нужно, если rsyslog пишет в journal.
sudo fail2ban-client get sshd logpath
# или смотрите статус jail после включения
Если после установки jail sshd не активен — это нормально: в дефолтах он часто выключен или с мягкими параметрами. Включаем сами в следующем разделе.
jail.local для sshd: bantime, findtime, maxretry
Создайте файл:
sudo nano /etc/fail2ban/jail.local
Рабочий стартовый конфиг для VPS с одним SSH:
[DEFAULT]
# бан 1 час; для агрессивных ботов можно 12h или 1d
bantime = 1h
findtime = 10m
maxretry = 5
# backend = systemd # раскомментируйте, если auto ведёт себя странно
ignoreip = 127.0.0.1/8 ::1
[sshd]
enabled = true
port = ssh
filter = sshd
# maxretry жёстче для sshd, чем глобальный DEFAULT
maxretry = 4
findtime = 10m
bantime = 2h
Что означают параметры:
- findtime — окно, в котором считаются провалы.
10mзначит: за последние 10 минут. - maxretry — сколько провалов с одного IP за окно добана.
4— разумный баланс: вы успеете опечататься в ключе/пользователе, бот — нет. - bantime — сколько держать бан.
2hдля sshd обычно хватает; для «вечных» ботов ставят12hили-1(перманент — осторожно). - ignoreip — белый список (см. следующий раздел).
- port = ssh — читает порт из
/etc/services(22). Если SSH слушает нестандартный порт, укажите число:port = 2222.
После правки:
sudo fail2ban-client reload
# или полный рестарт:
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd
Ожидаемый кусок статуса:
Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: ...
| `- Journal matches: _SYSTEMD_UNIT=sshd.service + ...
`- Actions
|- Currently banned: N
|- Total banned: ...
`- Banned IP list: ...
Если sshd нет в списке fail2ban-client status, jail не включён — проверьте enabled = true и синтаксис ini (без лишних кавычек вокруг чисел в новых версиях обычно ок, но единообразие важно).
Ужесточение для публичного VPS, куда ходят только вы с VPN:
[sshd]
enabled = true
maxretry = 3
findtime = 5m
bantime = 12h
Не ставьте maxretry = 1 без whitelist: один сбой сети или повторный ввод passphrase к ключу — и вы в бане.
Whitelist: свой IP и VPN
Самая частая боль — забанить себя. Лечится заранее через ignoreip.
Узнайте свой текущий выход в интернет (с рабочей машины, не с сервера):
curl -4 ifconfig.me
curl -6 ifconfig.me
Если сидите через домашний динамический IP — лучше поднять простой VPN/WireGuard на отдельном порту или использовать стабильный офисный IP. Динамика и fail2ban плохо дружат: сегодня белый список, завтра провайдер выдал другой адрес.
В jail.local:
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.40 198.51.100.0/24
203.0.113.40— пример вашего домашнего/офисного IP;198.51.100.0/24— пример подсети VPN или офиса;127.0.0.1/8и::1— локалхост, чтобы не отстрелить себя тестами с сервера.
Несколько адресов — через пробел. После изменения:
sudo fail2ban-client reload
sudo fail2ban-client get sshd ignoreip
Если вход всегда через WireGuard на сам VPS, можно добавить адрес туннеля (10.0.0.2 и т.п.) в ignoreip — тогда даже при ошибках с «внешнего» интерфейса вы зайдёте по WG и разбаните себя.
Альтернатива whitelist’у на уровне UFW: разрешить SSH только с известных сетей и закрыть 22 для остальных. Тогда fail2ban почти не понадобится для брута с интернета — но это уже жёсткая модель доступа, не для каждого проекта.
Разбан себя: fail2ban-client set sshd unbanip
Забанили? Не паникуйте. Варианты по возрастанию боли.
1. Есть вторая сессия или консоль провайдера (VNC/serial/web-консоль). Зайдите и:
sudo fail2ban-client set sshd unbanip ВАШ.IP.АДРЕС
sudo fail2ban-client status sshd
Проверка, что IP ушёл из списка banned.
2. Временно остановить jail (если нужно много правок):
sudo fail2ban-client stop sshd
# ... правите ignoreip, ключи, sshd_config ...
sudo fail2ban-client start sshd
Или остановить весь сервис (откроет уже забаненные до рестарта firewall-правил — зависит от backend; обычно правила снимаются):
sudo systemctl stop fail2ban
# разбан / правка ignoreip
sudo systemctl start fail2ban
3. Нет консоли — только другое исходящее. Смените IP (мобильный интернет, другой VPN-выход) и зайдите с адреса, которого нет в бане. Сразу добавьте стабильный IP в ignoreip.
4. Посмотреть, кто в бане:
sudo fail2ban-client status sshd
sudo fail2ban-client get sshd banned
Разбанить пачку:
sudo fail2ban-client set sshd unbanip 198.51.100.10
sudo fail2ban-client set sshd unbanip 203.0.113.55
Не путайте с UFW: ufw status может не показывать fail2ban-цепочки явно так же, как iptables -L / nft list ruleset. Бан живёт в правилах, которые ставит action fail2ban (iptables-multiport, nftables, ufw — смотрите banaction в конфиге).
sudo fail2ban-client get sshd banaction
На свежих Ubuntu чаще nftables или iptables-multiport. Главное — разбанивать через fail2ban-client, а не руками вычищать цепочки вслепую.
Логи и проверка, что jail работает
Статус всех jail’ов:
sudo fail2ban-client status
sudo fail2ban-client status sshd
Логи самого fail2ban:
sudo journalctl -u fail2ban -n 50 --no-pager
sudo tail -n 50 /var/log/fail2ban.log
Сообщения вида Ban 203.0.113.8 и Unban ... — признак, что цикл «нашёл в логе → забанил» жив.
Сырые неудачи SSH (до бана):
sudo journalctl -u ssh -n 100 --no-pager
# или на системах с auth.log:
sudo grep -E 'Failed|Invalid user|Connection closed' /var/log/auth.log | tail -n 40
Смотрите, совпадает ли IP в логе sshd с тем, что потом появляется в Banned IP list. Если fail2ban «молчит», а брут идёт:
- jail
sshdвыключен; - неверный
backend/logpath— фильтр не видит строки; - SSH на нестандартном порту, а в jail
port = ssh(22) — бан может ставиться не на тот порт (зависит от action); - кто‑то уже режет трафик до sshd (cloud firewall), и до логов попытки не доходят — тогда fail2ban просто нечего банить.
Тест на стенде (не на проде с единственным доступом): с другой машины несколько раз ошибитесь паролем/пользователем и смотрите status sshd. На проде с ключами безопаснее смотреть реальный шум ботов за сутки — Total banned должен расти.
Логи fail2ban и journal при активном бруте могут раздуваться. Если диск внезапно упрётся в 100%, сначала разберитесь с местом — кто съел диск: логи, Docker, journal — и ограничьте retention journald, иначе мониторинг банов тоже потеряете.
Частые ошибки: только пароль и динамический IP
Оставили парольный вход «на всякий случай»
Fail2ban снижает скорость брута, но не отменяет словарь. Если PasswordAuthentication yes и слабый пароль root/ubuntu — бот всё равно имеет шанс между банами (с разных IP ботнет обходит один banlist).
Сделайте как в базовом гайде:
sudo nano /etc/ssh/sshd_config
# PasswordAuthentication no
# PermitRootLogin no # или prohibit-password
sudo sshd -t && sudo systemctl reload ssh
Fail2ban остаётся полезен против Invalid user и редких сбоев, но основной замок — ключи.
Забанили себя с динамического домашнего IP
Симптом: вечером всё ок, утром провайдер сменил адрес, вы не в ignoreip, ошиблись passphrase три раза — бан. Утром с «нового» IP вы уже другой клиент для fail2ban, зато вчерашний белый список бесполезен, а сегодняшний может улететь в бан снова.
Что делать:
- whitelist стабильного VPN/офиса;
- поднять WireGuard на VPS и ходить только через него;
- не ставить
maxretryв 2 иbantimeв сутки без консоли провайдера; - хранить в панели хостинга доступ к web-консоли на случай лока.
Скопировали чужой jail.conf целиком и сломали filter
Длинный jail.local с устаревшими logpath = /var/log/auth.log на системе, где sshd пишет только в journal, даёт «пустой» jail. Держите короткий local-файл и проверяйте status sshd после каждого изменения.
Бан есть, а подключения всё равно идут
Иногда смотрят не тот jail (sshd vs sshd-ddos), или банится IPv4, а клиент приходит по IPv6. Добавьте оба семейства в мониторинг и в ignoreip при необходимости. Проверьте, что sshd слушает оба стека: ss -tlnp | grep sshd.
Связка с firewall, SSH и остальной базой
Порядок, который обычно не ломает прод:
- Ключи, отключение пароля, проверка входа со второй сессии — подключение по SSH.
- UFW:
OpenSSH(или свой порт), 80/443 по необходимости — настройка Ubuntu на VPS. - Fail2ban с
ignoreipи умереннымmaxretry. - Обновления, отдельный пользователь с sudo, бэкапы.
Fail2ban не заменяет UFW: firewall режет порты целиком, fail2ban точечно банит адреса на уже открытом SSH. Не отключайте UFW «потому что есть fail2ban» — это разные слои.
Если меняете порт SSH — синхронно обновите UFW-правило и port= в jail sshd, иначе либо не зайдёте, либо бан будет вешаться мимо реального порта.
FAQ
Fail2ban обязателен, если вход только по ключу?
Не обязателен, но полезен. Боты всё равно долбят порт; jail снижает шум в логах и отсекает попытки подбора пользователей. Ключи — must-have; fail2ban — дешёвый второй слой.
Чем bantime отличается от findtime?
findtime — окно подсчёта ошибок. bantime — сколько IP сидит в бане после превышения maxretry. Ошибки старше findtime в счётчик уже не входят.
Как не забанить мониторинг и CI?
Добавьте IP раннеров и monitoring-probe в ignoreip, либо вынесите SSH за VPN и закройте 22 снаружи. Иначе health-check с неверным ключом за вечер сам себя вынесет.
fail2ban ест много RAM на маленьком тарифе?
Обычно нет: это лёгкий демон на Python. На минимальных VPS важнее не раздувать journal и не держать десятки тяжёлых jail’ов «на будущее». Один sshd — нормальная нагрузка.
Можно ли банить навсегда?
bantime = -1 в современных версиях даёт перманентный бан до ручного unban. Имеет смысл только при консоли провайдера и стабильном whitelist. Иначе один ошибочный бан своего динамического IP = квест на час.
Почему после reload IP снова в бане?
Reload не всегда чистит уже выданные баны так, как вы ждёте; часть action’ов держит правила до unbanip или истечения bantime. Явно разбаньте: fail2ban-client set sshd unbanip ....
Короткий чеклист перед уходом с сервера
-
PasswordAuthentication no, вход по ключу проверен со второй сессии - UFW разрешает SSH, лишнее закрыто
-
/etc/fail2ban/jail.localс[sshd] enabled = true -
ignoreipсодержит VPN/офис/стабильный IP -
fail2ban-client status sshdпоказывает jail без ошибок - Знаете команду
unbanipи где консоль провайдера
Fail2ban настраивается за пятнадцать минут и экономит часы на разбор auth.log. Главное — не выключить себя из сервера: whitelist, умеренный maxretry и запасной вход через консоль.
Если поднимаете новый сервер под проект и не хотите ждать выдачу полдня — на VPS Azerta машина обычно готова до 120 секунд после оплаты, тарифы от 360₽, поддержка 24/7, ДЦ в Москве (Нагатинский) и площадка в Германии. После 3 ТБ трафика действует шейпинг 1 Мбит/с — для SSH и админки этого более чем достаточно, а fail2ban как раз помогает не тратить нервы на чужие сканы в логах.