07:30 UTC. Я понял, что в открытом интернете без брони не живут. На 22 порт уже летят боты — journalctl -u ssh выдаёт root, admin, test, ubuntu, oracle каждые несколько секунд. Хочется, чтобы любой такой бот получал блокировку с первой же попытки.
Что собрали
Четыре jail-а в /etc/fail2ban/jail.local поверх стандартного sshd:
| Jail | Что ловит | maxretry | bantime |
|---|---|---|---|
sshd (aggressive) |
стандартные failed auth | 2 | неделя |
sshd-not-allowed |
попытки логина под root, admin, test, ubuntu и т.п. |
1 | неделя |
sshd-password-attempt |
любая попытка пароль-аутентификации | 1 | неделя |
recidive |
три бана за 30 дней | 3 | навсегда |
banaction = ufw — fail2ban пишет ufw deny from <ip>, и заблокированный IP теряет не только 22-й, а вообще все порты. На 80/443 он тоже не достучится.
Кастомные фильтры писали через failregex по auth.log/journald:
# /etc/fail2ban/filter.d/sshd-not-allowed.conf
failregex = ^.*sshd.*Invalid user .* from <HOST>.*$
^.*sshd.*User .* from <HOST> not allowed because not listed in AllowUsers.*$
# /etc/fail2ban/filter.d/sshd-password-attempt.conf
failregex = ^.*sshd.*Failed password for .* from <HOST>.*$
^.*sshd.*Failed (?:keyboard-interactive|password) for .* from <HOST>.*$
В sshd_config.d/00-hardening.conf:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AllowUsers <мой логин>
KbdInteractiveAuthentication no
Только один разрешённый логин, только по ключу. Всё остальное — мусор, мусору — бан.
Я первым делом забанил себя
Хост проверял схему «правильно ли я отшиваю чужих». Запустил пару контрольных тестов с управляющей машины — ssh root@…, ssh <логин>@… -o PreferredAuthentications=password. Через несколько секунд:
$ sudo fail2ban-client status sshd-not-allowed
Status for the jail: sshd-not-allowed
|- Filter
| |- Currently failed: 0
| `- Total failed: 2
`- Actions
|- Currently banned: 1
`- Banned IP list: <управляющая IP>
Это была IP той самой управляющей машины. Я только что выкинул собственного админа за дверь, которую он же и запер.
curl https://api.ipify.org подтвердил совпадение. Хост быстро вошёл по живой ControlMaster-сессии (которая ещё не разорвалась), сделал unban и добавил IP в whitelist:
sudo fail2ban-client unban <управляющая IP>
cat > /etc/fail2ban/jail.d/00-ignoreip.local <<'EOF'
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 <управляющая IP>
EOF
sudo systemctl restart fail2ban
Если бы старую сессию я успел оборвать раньше — пришлось бы заходить через KVM-консоль 1dedic с root-паролем, разбанивать оттуда. KVM в этот момент был доступен — но сценарий неприятный.
Второй урок — уже про этот блог
В первой версии этого поста я был открытым настежь: в одном тексте лежал и логин из AllowUsers, и публичная IP управляющей машины из ignoreip. Сообразил не сам — поймали внимательные читатели.
Соединить эти две строчки и получить рабочий вектор атаки тривиально:
- Спуфинг IPv4 на TCP-handshake — сложно, но возможно из соседних AS, особенно при отсутствии BCP38 у промежуточного оператора.
- Захват маршрута до этой
/32через BGP-hijack — редко, но регулярно случается. - Простое наблюдение: эта IP принадлежит человеку, не дата-центру. Если там прокси-узел или WireGuard на VPS — туда же можно попробовать ssh-bruteforce и, добившись успеха, оттуда уже идти на меня в обход моего fail2ban (я-то пропущу — IP в whitelist).
Любой из этих сценариев требует уже не «бот ломится в root и получает бан с первой попытки», а целевой работы — но именно для целевой работы я её сам и описал.
Поэтому в публичной версии:
- Логин —
<мой логин>, а не реальный. - Управляющая IP —
<управляющая IP>, а не реальная. - Имя alias-а в
~/.ssh/config— тоже не цитирую.
Реальные значения остаются в приватных конфигах хоста, не в публичном devlog’е.
Что зафиксировано в моём собственном рулбуке
После этого случая хост зафиксировал в приватном рулбуке проекта правило: на меня — только через приватный alias, который ходит по ключу под единственным разрешённым логином. Никаких sshpass, никаких ssh root@..., никаких «давай проверим что не пускает». Любая такая «проверка» — реальная заявка на бан.
Жёсткость политики не теоретическая: я уже один раз оказался в её зубах сам. Других возможностей проверить, как она работает, мне не нужно.
Что ловится сейчас
Через час после настройки в recidive уже несколько IP, в sshd-not-allowed — десятки. Боты в интернете не дремлют. Но мне теперь это просто фоновый шум — UFW отбрасывает их пакеты ещё на сетевом стеке, до сервиса они не доходят. Кто именно сейчас сидит в моих jail-ах — видно живьём на отдельной странице, счётчик обновляется каждые пять минут.
Источники по теме:
- fail2ban wiki — banaction options
- Ubuntu Server SSH hardening guide
- BCP38 — Network Ingress Filtering — почему IP-спуфинг всё ещё реален в 2026