Начиная с OpenSSH 9.7, sshd умеет автоматически ограничивать на время подозрительные IP без Fail2Ban и iptables. В Ubuntu 26.04 эта функция уже включена по умолчанию — даже если в sshd_config про неё ничего не написано. Предлагаю попробовать разобраться с тем, как это работает.
Disclaimer: статья написана без использования ИИ. Нейросеть использовалась только для стилистической редактуры. Ничего не рекламирую и в ТГ-чаты не зазываю. Гараж не продаю.

Как вы знаете, в прошлом месяце вышла новая LTS-версия Ubuntu 26.04 — Resolute Raccoon. Читая патчноуты (да, существует много людей, которые их читают), в пункте про OpenSSH я обнаружил вот такой интересный момент: новая директива PerSourcePenalties будет «наказывать» адреса клиентов, которые по какой-либо причине не завершают аутентификацию.
Вообще, функция далеко не новая. Разработчики OpenSSH добавили её ещё в 2024 году (рассылка) и включили по умолчанию в версии 9.7, но эта версия не попала в Ubuntu 24.04.

В Ubuntu 26.04 «из коробки» поставляется OpenSSH версии 10.2p1 от 27 января 2026. Следовательно, по умолчанию теперь всё включено. Я раскатил себе официальный образ, полез смотреть конфиги, и… в них ничего нет! Получается, что функциональность есть и включена, но в конфиге нет ни одного слова про это. И если вы не читали патчноуты, то даже не знаете что оно у вас есть.
В Ubuntu 26.04 проверить, что оно включено, можно командой:
sshd -T | grep -i persource

Нас интересует строка:
persourcepenalties crash:90 authfail:5 noauth:1 grace-exceeded:10 refuseconnection:10 max:600 min:15 max-sources4:65536 max-sources6:65536 overflow:permissive overflow6:permissive
Параметры новой директивы
Давайте разберём каждый параметр по порядку.
Основные штрафы (в секундах)
crash:90 — самый жёсткий штраф. Присваивается, если клиент вызывает сбой или аномальное завершение процесса sshd. Это защита от потенциальных эксплойтов и нестабильных клиентов.
authfail:5 — неудачная аутентификация (неверный пароль, ключ, метод и т. д.). Классический брутфорс. Каждый провал добавляет 5 секунд штрафа.
noauth:1 — клиент подключился, не предпринял вообще никаких действий по аутентификации и отключился. Типичное поведение сканеров и «шумов» интернета.
grace-exceeded:10 — клиент не успел пройти аутентификацию за время LoginGraceTime (по умолчанию 120 секунд). Добавляет 10 секунд штрафа.
refuseconnection:10 — сервер сам отказал в соединении (например, из-за превышения MaxStartups). Также 10 секунд.
Пороги срабатывания
min:15 — минимальный накопленный штраф, при котором IP начинает блокироваться. Пока сумма штрафов меньше 15 секунд — соединения принимаются нормально. Это защищает от случайных единичных ошибок.
max:600 — максимальный штраф (10 минут). Даже если клиент «наберёт» больше, штраф не превысит это значение.
Ограничения по количеству отслеживаемых источников
max-sources4:65536 — максимальное количество IPv4-адресов, за которыми одновременно следит sshd.
max-sources6:65536 — то же для IPv6.
Значения по умолчанию очень высокие — по сути, «отслеживать всех».
Поведение при переполнении таблицы
overflow:permissive
overflow6:permissive
Когда количество отслеживаемых источников превышает лимит, сервер продолжает добавлять новые записи, но чтобы освободить под них место в таблице, выкидывает наименее заштрафованные старые. В исходниках можно увидеть красноречивый комментарий:
/* Delete the soonest-to-expire penalties. */
Сначала я не правильно понял, как работает этот механизм, но в комментах меня поправили. Спасибо за это @firegurafiku
PerSourcePenalties не является заменой Fail2Ban. В отличие от него, механизм не банит IP полностью, а временно замедляет или блокирует подключения пропорционально степени «виновности» источника (если буфер не переполнен).
Как работает PerSourcePenalties
Отказ в соединении инициируется самим процессом sshd (на уровне приложения), а не через правила брандмауэра (iptables/nftables).
Механизм работает следующим образом:
Принятие соединения: основной процесс sshd принимает входящее TCP-соединение (выполняет accept()).
Проверка таблицы: сразу после принятия соединения sshd проверяет IP-адрес клиента по своей внутренней таблице штрафов. PerSourcePenalties хранит состояние во внутренней памяти master-процесса sshd, поэтому штатного способа посмотреть текущее содержимое таблицы нет.
Немедленный разрыв: если накопленный штраф для этого IP превышает установленный порог, sshd просто закрывает сокет, не запуская дочерний процесс для аутентификации.
Почему это сделано именно так?
Автономность. Функциональность доступна «из коробки» на любой операционной системе (Linux, BSD, macOS) без необходимости иметь права root для модификации правил фильтрации пакетов.
Безопасность. Поскольку основной процесс sshd теперь не выполняет сложную логику парсинга данных от клиента (этим занимается отдельный sshd-session), быстрая проверка IP по списку штрафов практически не потребляет ресурсов и защищает от перегрузки.
Отсутствие интеграции с ядром. В отличие от того же Fail2Ban, OpenSSH не вызывает внешние команды вроде iptables -A.... Это быстрее и исключает риск того, что ошибка в скрипте заблокирует доступ ко всему серверу.
Важное замечание. PerSourcePenalties работает по IP. А значит, офисы, VPN, мобильные операторы, CGNAT могут случайно «наказывать» сразу группу пользователей.
Рубрика «э-э-эксперименты»
Давайте протестируем, как это работает на практике. Я попробую подключиться с неверным паролем несколько раз подряд и буду смотреть логи:

Кстати, в обычном логе этого не видно. Мне пришлось добавить настройку LogLevel VERBOSE в конфиг /etc/ssh/sshd_config.
При настройках по умолчанию я вообще не заметил работу этой фичи, когда пытался брутфорсить вручную. То есть можно сделать вывод, что пользователь сам себя не заблочит при неправильном вводе своего пароля (Напоню, что вход по паролю это не самая хорошая идея. Настройте ключи и ходите по ним).
Но вручную никто такого не делает, так что автоматизируем. Я запустил Hydra на стандартных значениях и с rockyou:

Смотрим логи в терминале “жертвы”:

На первом этапе Hydra создала большое количество соединений без попытки аутентификации. За это IP получил минимальный штраф:
srclimit_penalise: ipv4: new х.х.х.11/32 deferred penalty of 1 seconds - это noauth штраф (1 секунда)
for penalty: connections without attempting authentication
Далее сервер начал отбрасывать избыточные соединения по лимиту MaxStartups.

Затем, когда соединения дошли до стадии ввода пароля, начали накапливаться штрафы за failed authentication. Критический момент наступил, когда накопленный штраф превысил порог:

srclimit_penalise: х.х.х.11/32: activating ipv4 penalty of 19 seconds - накопленный штрафПосле этого все новые соединения от этого IP стали отбрасываться с сообщением
drop connection ... penalty: failed authentication.

Важный нюанс: С точки зрения атакующего это выглядит как «Connection closed by remote host» сразу после установки TCP-сессии.
Сравнение с Fail2Ban: когда что использовать
Многие администраторы сразу вспоминают Fail2Ban, когда речь заходит о защите SSH. Давайте сравним два подхода.
Параметр | PerSourcePenalties (OpenSSH) | Fail2Ban |
Место работы | Встроено в sshd | Внешний демон, парсит логи |
Скорость реакции | Мгновенная (в момент события) | Задержка (зависит от частоты проверки логов) |
Тип блокировки | Временная, динамическая (секунды — десятки минут) | Обычно жёсткая и долгая (минуты — сутки) |
Ресурсоёмкость | Очень низкая | Средняя и выше (постоянный мониторинг логов) |
Гибкость штрафов | Высокая (разные штрафы за разные нарушения) | Высокая, но требует написания фильтров |
Защита от DoS на саму защиту | Есть (overflow:permissive) | Зависит от конфигурации |
Постоянные баны | Не сделать | Легко и привычно |
Если смотреть на плюсы обоих подходов, то вот что можно выделить.
Работает из коробки и включена по умолчанию в Ubuntu 26.04.
Не требует парсинга логов — реакция быстрее и надёжнее.
Значительно меньше ложных срабатываний на легитимных пользователей (особенно тех, кто случайно ошибся с паролем).
Устойчив к атакам на сам механизм защиты.
Минимальное потребление ресурсов.
Преимущества Fail2Ban
Более привычный и "видимый" инструмент.
Можно банить навсегда или на очень долгий срок.
Работает с любыми сервисами.
Легко интегрируется с iptables/nftables, Fail2Ban-ботами в Telegram, Cloudflare и т.д.
Богатая экосистема готовых jail'ов.
Отключать механизм полностью я бы не рекомендовал, но если это всё же необходимо, можно создать отдельный override-конфиг:
/etc/ssh/sshd_config.d/99-pensourcepenalties-disable.conf
Имя файла может быть любым. Числовой префикс определяет порядок загрузки конфигурации.
Полностью отключить механизм можно директивой:
PerSourcePenalties no
Либо отключить только отдельные типы штрафов:
PerSourcePenalties authfail:0 noauth:0
После изменения конфигурации не забудьте перезапустить sshd:
systemctl restart ssh
Выводы и полезный совет
PerSourcePenalties не заменяет Fail2Ban полностью, а отлично дополняет его, как первая линия обороны (быстрая реакция на шум и лёгкий брутфорс).
Fail2Ban — отлично подходит на роль второй линии (долгие баны за упорные атаки, защита других сервисов, удобный мониторинг).
После тестов у меня не появилось ощущения, что PerSourcePenalties должен заменить Fail2Ban. Но теперь SSH-сервер хотя бы умеет самостоятельно отбиваться от самого примитивного шума и брутфорса без внешних костылей. И, честно говоря, для встроенного механизма это работает неожиданно неплохо. Что скажете?
