Начиная с 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 24,04
Проверяем максимальную версию доступную для обновления в 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).

Механизм работает следующим образом:

  1. Принятие соединения: основной процесс sshd принимает входящее TCP-соединение (выполняет accept()).

  2. Проверка таблицы: сразу после принятия соединения sshd проверяет IP-адрес клиента по своей внутренней таблице штрафов. PerSourcePenalties хранит состояние во внутренней памяти master-процесса sshd, поэтому штатного способа посмотреть текущее содержимое таблицы нет. 

  3. Немедленный разрыв: если накопленный штраф для этого IP превышает установленный порог, sshd просто закрывает сокет, не запуская дочерний процесс для аутентификации.

Почему это сделано именно так?

Автономность. Функциональность доступна «из коробки» на любой операционной системе (Linux, BSD, macOS) без необходимости иметь права root для модификации правил фильтрации пакетов.

Безопасность. Поскольку основной процесс sshd теперь не выполняет сложную логику парсинга данных от клиента (этим занимается отдельный sshd-session), быстрая проверка IP по списку штрафов практически не потребляет ресурсов и защищает от перегрузки.

Отсутствие интеграции с ядром. В отличие от того же Fail2Ban, OpenSSH не вызывает внешние команды вроде iptables -A.... Это быстрее и исключает риск того, что ошибка в скрипте заблокирует доступ ко всему серверу.

Важное замечание. PerSourcePenalties работает по IP. А значит, офисы, VPN, мобильные операторы, CGNAT могут случайно «наказывать» сразу группу пользователей.

Рубрика «э-э-эксперименты» 

Давайте протестируем, как это работает на практике. Я попробую подключиться с неверным паролем несколько раз подряд и буду смотреть логи:

IP спрятал, чтобы не привлекать внимание санитаров скрипт-кидди
IP спрятал, чтобы не привлекать внимание санитаров скрипт-кидди

Кстати, в обычном логе этого не видно. Мне пришлось добавить настройку 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  - накопленный штраф
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-сервер хотя бы умеет самостоятельно отбиваться от самого примитивного шума и брутфорса без внешних костылей. И, честно говоря, для встроенного механизма это работает неожиданно неплохо. Что скажете?

Комментарии (27)


  1. aborouhin
    07.05.2026 17:27

    Есть (overflow:permissive)

    Скорее уж overflow:deny-all плюс фиксированный IP для аварийного доступа в PerSourcePenaltyExemptList

    А permissive, если я правильно понял, при достаточно мощной атаке как раз позволит пробить PerSourcePenalty и загрузить уже основной процесс sshd, так что и администратор не пробьётся.


    1. j_larkin Автор
      07.05.2026 17:27

      Спасибо за комментарий. Думаю, что при достаточно мощной атаке не только эта фича не поможет, а, в принципе, ничего не спасет. Но, как мне кажется, критичные сервера, на которые проводят мощные атаки, не святят ssh наружу. По крайне мере, не должны светить.


      1. aborouhin
        07.05.2026 17:27

        Вообще бы с этим вопросом разобраться, если уж использовать PerSourcePenalty. Сейчас перечитал man sshd_config и, скорее всего, да, я изначально понял этот механизм неправильно.

        Режим overflow включается, если количество IP (а не подключений!) превышает max-sources4 / max-sources6, которые каждый по дефолту 65536. Что-то у меня подозрения, что при атаке с такого количества адресов средний сервер действительно умрёт куда раньше, чем до этого overflow, какой бы режим ни был выбран, вообще дойдёт дело. Да и вообще, для нас простых людей гораздо реалистичнее сценарий, когда несколько сошедших с ума ботов долбятся с одних и тех же IP каждые N миллисекунд, чем когда идёт DDoS с тысяч IP, да ещё и именно на SSH. Первое у себя видел, от второго Бог пока миловал :)

        А вот есть ли механизм защиты от перегрузки процесса при большом количестве одновременных соединений (а не IP)? Я такого не нашёл. Получается, что если sshd-session захлебнётся, мы теряем пациента. А захлебнётся он, IMHO, всё-таки быстрее, чем nftables, работающий в kernel space. Так что ещё один плюсик в карму fail2ban (помимо того, что он нам, конечно, и для других jail'ов всё равно нужен)


        1. j_larkin Автор
          07.05.2026 17:27

          можно попробовать на тестовом стенде запустить многопоточный брутфорс при чем с нескольких IP. Но это нужно делать когда сервера физические. Намоем стенде все виртуалки, не думаю, что при таком сетапе эксперимент будет чистым. Так как брут сам по себе будет нагружать ЦПУ, и это может сказаться на подопытной ВМ.
          Я, кстати, не уверен, что такую нагрузку можно сгенерировать таким способом. Хотя идея привлекательная. Было бы интересно такое реализовать.
          Ну и как я писал в статье, наличие этой фичи, не означает, что f2b теперь не нужен.


          1. aborouhin
            07.05.2026 17:27

            У меня есть физические (собственно, только на них SSH наружу и торчит), но я не настолько заинтересовался вопросом, чтобы так заморочиться :)


            1. j_larkin Автор
              07.05.2026 17:27

              Понимаю. У меня просто спортивный интерес. Я из пачнота сначала вообще не понял про что речь. Потом погуглил немного и не нашёл чего-то, что объяснило весь функционал. Решил разобраться. Так статья и появилась.


  1. gotch
    07.05.2026 17:27

    Если стоит как в Azure по умолчанию - аутентификация только по ключу, то всё это не актуально?

    И хотел ещё уточнить - а за сколько не продаёте?


    1. aborouhin
      07.05.2026 17:27

      аутентификация только по ключу, то всё это не актуально?

      Так мы ж защищаемся не от того, что кто-то реально подберёт пароль (у тех, кто дошёл до настройки подобных механизмов, в любом случае или только по ключу, или пароль, который подбирать вечность), а от того, чтобы долбящиеся в надежде подобрать пароль боты нам сервер не нагружали. А они всё равно долбятся и пытаются авторизоваться по паролю, даже если сервер принимает только ключи.


      1. gotch
        07.05.2026 17:27

        Понял, спасибо большое


    1. j_larkin Автор
      07.05.2026 17:27

      Нет, аутентификация по ключу спасает от того, что пароль всё таки подберут. Но пытаться всё равно будут, так как снаружи не видно пароль у Вас там или ключи.

      Уточняю, ни за сколько не продаю =)


      1. gotch
        07.05.2026 17:27

        Ясно, спасибо за объяснение и за статью. Есть повод теперь её внимательно прочитать.


      1. Anselm_nn
        07.05.2026 17:27

        снаружи таки видно, но некоторые тупые боты все равно долбятся, даже если ssh сервер явно отвечает, что только по ключу)


      1. mnlt
        07.05.2026 17:27

        А как же:

        KbdInteractiveAuthentication no

        сразу возвращает [Permission denied (publickey)] без возможности ввода пароля


  1. firegurafiku
    07.05.2026 17:27

    Когда количество отслеживаемых источников превышает лимит, сервер переходит в «мягкий» режим: перестаёт добавлять новые IP в таблицу штрафов, но не блокирует их. Это предотвращает DoS-атаку на сам механизм защиты. Эффективность механизма при этом значительно снижается.

    По-моему, у вас тут фактическая ошбка: тут утверждается, что при заполнении таблицы новые соединения автоматически разрешаются. Но давайте посмотрим документацию:

    There are two operating modes: deny-all, which denies all incoming connections other than those exempted via PerSourcePenaltyExemptList until a penalty expires, and permissive, which allows new connections by removing existing penalties early (default: permissive).

    То есть, это нет так — сервер продолжает добавлять новые записи, но чтобы освободить под них место в таблице, выкидывает наименее заштрафованные старые. Можно даже заглянуть в исходники и увидеть там красноречивый комментарий:

    /* Delete the soonest-to-expire penalties. */

    https://github.com/openssh/openssh-portable/blob/ac4a41265a3beccba7dc6f45c657298311280f01/srclimit.c#L311


    1. j_larkin Автор
      07.05.2026 17:27

      Спасибо за уточнение, поправил этот момент в статье. Карму, к сожалению, начислить не дает. По этому могу только плюсануть.


  1. aborouhin
    07.05.2026 17:27

    Ещё одна мысль возникла - это же дело надо как-то мониторить (и просто статистики ради, и чтобы отлавливать аномальные всплески). Для fail2ban есть экспортер, а тут что-то сходу ничего готового из коробки не нашёл. Хотя написать тривиально, если формат лога стабильный. Можно даже не полноценный экспортер, а скриптик, который пишет файл для textfile collector node-exporter'а.


    1. Aule
      07.05.2026 17:27

      Сервис можно, он в journal пишет события, а syslog-ng разбирай как хочешь, ну или rsyslog настроить логика та же

      [Unit]Description=Forward SSH penalty events to central syslogAfter=network.target sshd.service[Service] Type=simple ExecStart=/bin/sh -c 'journalctl -u sshd -f --output=cat -n0 | grep --line-buffered "activating.*penalty" | nc logs.company.ru 514' Restart=always RestartSec=5 User=root[Install] WantedBy=multi-user.target


  1. Kerman
    07.05.2026 17:27

    Fail2Ban нужен. Он более универсален, например у меня он настроен на тех, кто ищет на сайте .env, .git да и просто конфиги от вордпресса. А, ну да, ещё банит всех, кто запрашивает .php файлы.


    1. beerchaser
      07.05.2026 17:27

      Плюс, насколько я понимаю, механизм фильтрации Fail2Ban работает на ядерном уровне, в данном случае фильтрация реализована на уровне приложения. Поэтому я не стал бы так оптимистично оценивать ресурсоемкость этой функции. Фича удобная, если требуется защита только для ssh, так как не требует развёртывания и настройки дополнительного программного обеспечения. Если Fail2Ban уже развёрнут, то лучше (имхо) ограничениями управлять централизованно.


      1. j_larkin Автор
        07.05.2026 17:27

        Нет, сам Fail2Ban не работает на уровне ядра. Это userspace-демон, который крутится как обычный процесс и взаимодействует с системой через уже существующие механизмы ядра. По факту, f2b читает логи и на основании их производит какие-то действия.
        В статье я подвел итог, что "новая" фича не заменяет f2, а дополняет. Тем более, что последний не только с ssh работает.

        Если Fail2Ban уже развёрнут, то лучше (имхо) ограничениями управлять централизованно.

        в поведении по умолчанию поведение достаточно мягкое, если не тюнить "под себя", то стоит хотя бы не выключать. Хуже не будет, а нагрузку на сервер может снизить, при сильном брутфорсе.


        1. beerchaser
          07.05.2026 17:27

          На уровне ядра работают правила блокировки/фильтрации, которые формирует F2B. В данной реализации, насколько я понимаю, блокировка/фильтрация на уровне userspace. За информацию про такую возможность спасибо. Её наличие целесообразно учитывать при принятии решений об организации защиты.


  1. Aelliari
    07.05.2026 17:27

    max-sources6:65536 — то же для IPv6

    Э… а можно лучше подсетями?.. :(


    1. Tanriol
      07.05.2026 17:27

      Можно

      PerSourceNetBlockSize

      Specifies the number of bits of source address that are grouped together for the purposes of applying PerSourceMaxStartups limits. Values for IPv4 and optionally IPv6 may be specified, separated by a colon. The default is 32:128, which means each address is considered individually.

      И на PerSourcePenalties тоже распространяется.


  1. xoolood22
    07.05.2026 17:27

    Спасибо большое, Иван! Узнал из вашей статьи что появилась такая функция. Буду ей пользоваться.


  1. cmyser
    07.05.2026 17:27

    Есть ещё port knoking

    Вот там вообще всем отдается ошибка )


  1. kolabaister
    07.05.2026 17:27

    Заголовок так себе) fail2ban это ведь не только про ssh


  1. LF69ssop
    07.05.2026 17:27

    fail2ban вообще не про это. Он ловит триггер и генерирует действие.
    Например по неверному паролю к ssh блочит доступ источника к вебу.