Fail2ban для DSM и контейнеров: конкретные джейлы
SSH, Vaultwarden и Nginx Proxy Manager под защитой от перебора паролей — с разбором того, почему бан из контейнера по умолчанию вообще ничего не блокирует.
- Зачем ещё и fail2ban, если в DSM есть Auto Block
- Почему обычный контейнер fail2ban ничего не забанит
- Что понадобится перед стартом
- Шаг 1 — готовим логи сервисов к чтению
- Шаг 2 — docker-compose.yml
- Шаг 3 — джейл для SSH
- Шаг 4 — джейл для Vaultwarden
- Шаг 5 — джейл для Nginx Proxy Manager
- Проверка банов и снятие блокировки
- Переживаем перезагрузку NAS
- Совместимость со встроенным файрволом DSM
- Ограничения подхода и ложные срабатывания
- Частые проблемы
Зачем ещё и fail2ban, если в DSM есть Auto Block
Встроенный Auto Block (Панель управления → Безопасность → Учётная запись) защищает от перебора паролей только сам DSM — вход в панель управления и SSH. Он ничего не знает о том, что происходит внутри Vaultwarden или Nginx Proxy Manager: для DSM это просто трафик на порт 8080 или 443, а не конкретная неудачная попытка входа в чужой сейф с паролями. Дальше — как прикрыть именно эти сервисы, оставив Auto Block работать своей стороной для самого DSM.
Почему обычный контейнер fail2ban ничего не забанит
Самая частая ошибка в гайдах "fail2ban в Docker" — запустить его в обычном bridge-режиме и удивляться, почему бан не действует. Причина в изоляции сети: правила iptables, которые fail2ban добавляет изнутри контейнера, применяются только к сетевому namespace этого контейнера — а реальный входящий трафик идёт через namespace хоста и эти правила попросту не видит.
network_mode: host с capabilities NET_ADMIN и NET_RAW — то есть модифицировать iptables самого NAS, а не свою изолированную копию. Без этого контейнер будет честно писать в лог "Ban 203.0.113.5", но пакеты от этого IP продолжат доходить до Vaultwarden как ни в чём не бывало.
Что понадобится перед стартом
- Уже развёрнутые Vaultwarden и Nginx Proxy Manager из предыдущих статей — джейлы ниже написаны конкретно под их форматы логов.
- SSH-доступ к NAS с правами на просмотр
/var/log. - Понимание, что это — правки на уровне сетевого стека хоста, и тестировать банadd-правила стоит не с единственного устройства, с которого вы же администрируете NAS.
Шаг 1 — готовим логи сервисов к чтению
01Включаем файловый лог у Vaultwarden
По умолчанию Vaultwarden пишет логи только в stdout контейнера, без отдельного файла на диске. Добавьте переменную окружения в его compose-файл, чтобы получить постоянный лог-файл внутри уже примонтированного тома data:
environment:
LOG_FILE: "/data/vaultwarden.log"
LOG_LEVEL: "warn"
После перезапуска сервиса лог появится по хостовому пути /volume1/docker/vaultwarden/data/vaultwarden.log — том уже примонтирован из оригинальной статьи, ничего пересоздавать не нужно.
02Логи NPM уже готовы
Nginx Proxy Manager и так пишет access/error логи каждого Proxy Host в /volume1/docker/nginx-proxy-manager/data/logs/ — отдельным файлом на хост, без дополнительной настройки.
Шаг 2 — docker-compose.yml
services:
fail2ban:
image: crazymax/fail2ban:latest
container_name: fail2ban
restart: unless-stopped
network_mode: host
cap_add:
- NET_ADMIN
- NET_RAW
environment:
TZ: "Europe/Moscow"
F2B_LOG_LEVEL: "INFO"
F2B_DB_PURGE_AGE: "30d"
volumes:
- /volume1/docker/fail2ban/data:/data
- /var/log:/host-var-log:ro
- /volume1/docker/vaultwarden/data:/var/log/vaultwarden:ro
- /volume1/docker/nginx-proxy-manager/data/logs:/var/log/npm:ro
Каталог /data хранит конфигурацию джейлов, фильтров и SQLite-базу состояния банов — именно его нужно бэкапить и именно в нём редактируются файлы из следующих шагов.
Шаг 3 — джейл для SSH
[sshd] enabled = true port = ssh filter = sshd logpath = /host-var-log/auth.log maxretry = 5 findtime = 600 bantime = 3600
Фильтр sshd идёт в комплекте с образом — стандартный, менять не нужно. Единственное, что важно проверить самостоятельно — реальный путь к логу авторизации на вашей версии DSM (иногда это /var/log/auth.log, в других сборках — часть общего messages).
Шаг 4 — джейл для Vaultwarden
Vaultwarden пишет неудачные попытки входа в предсказуемом формате, включающем IP клиента — но только если он получает реальный IP от NPM через заголовки X-Real-IP, настроенные ещё в оригинальной статье про Vaultwarden. Фильтр:
[Definition] failregex = ^.*Username or password is incorrect\. Try again\. IP:\. ignoreregex =
[vaultwarden] enabled = true filter = vaultwarden logpath = /var/log/vaultwarden/vaultwarden.log maxretry = 5 findtime = 600 bantime = 86400 action = iptables-allports[name=vaultwarden]
vaultwarden.log — регулярное выражение должно совпадать с тем, что там действительно написано, а не с тем, что написано в этой статье.
Шаг 5 — джейл для Nginx Proxy Manager
Панель администратора NPM аутентифицируется через эндпоинт /api/tokens — повторяющиеся неудачные попытки входа оставляют в access-логе соответствующего Proxy Host характерные строки со статусом 401:
[Definition] failregex = ^.* "POST /api/tokens HTTP/\d\.\d" 401 ignoreregex =
[npm-auth] enabled = true filter = npm-auth logpath = /var/log/npm/*_access.log maxretry = 10 findtime = 600 bantime = 3600
Порог здесь выше, чем у Vaultwarden (10 попыток вместо 5) — панель администратора NPM видит фоновый шум ботов, сканирующих типовые пути на любом веб-сервере в интернете, и слишком чувствительный порог будет банить IP за случайный автоматический скан, а не за целенаправленный перебор.
Проверка банов и снятие блокировки
docker exec fail2ban fail2ban-client status vaultwarden Status for the jail: vaultwarden |- Filter | |- Currently failed: 1 | |- Total failed: 7 | `- Journal matches: n/a `- Actions |- Currently banned: 1 |- Total banned: 1 `- Banned IP list: 203.0.113.5 # снять бан, если случайно забанили себя docker exec fail2ban fail2ban-client set vaultwarden unbanip 203.0.113.5
Переживаем перезагрузку NAS
После перезагрузки NAS сетевой стек хоста поднимается заново, и правила iptables, добавленные fail2ban, обнуляются вместе с ним — база банов в /data сохраняется, но реальные правила блокировки нужно применить заново. Контейнер с restart: unless-stopped поднимется сам и переприменит правила при старте — но только если Docker-демон и сетевой стек DSM к этому моменту уже полностью готовы.
Совместимость со встроенным файрволом DSM
Если в Панели управления → Безопасность → Файрвол у вас включены собственные правила DSM, они тоже управляют iptables того же хоста. На практике оба механизма обычно сосуществуют — fail2ban добавляет свою цепочку и джамп-правило, не трогая остальные, — но после любого сохранения настроек файрвола DSM стоит проверить командой из предыдущего шага, что банlist по-прежнему на месте, а не был случайно сброшен переприменением правил DSM.
Ограничения подхода и ложные срабатывания
- Провайдеры с CGNAT и офисные сети за одним IP — один реальный нарушитель за общим IP может забанить весь офис или всех клиентов одного мобильного оператора. Держите бан по конкретным сервисам разумным (часы, не недели) для jail'ов, которые смотрят в открытый интернет.
- Занесите свой собственный статический IP (если он есть) в whitelist через параметр
ignoreipвjail.local, чтобы случайная опечатка в пароле пять раз подряд не заблокировала вас самих. - Fail2ban — не замена нормальным паролям и 2FA, а дополнительный барьер, снижающий шум от автоматических ботов, а не защита от целенаправленной атаки на конкретный аккаунт.
Частые проблемы
| Симптом | Вероятная причина |
|---|---|
| В логе "Ban", но IP всё равно достукивается до сервиса | контейнер запущен не в network_mode: host — банится изолированный namespace, а не хост |
| Джейл vaultwarden не видит ни одной неудачной попытки | не задан LOG_FILE в переменных окружения Vaultwarden, лог пуст |
| Себя же и забанили с одного IP | не настроен ignoreip для собственной локальной подсети/статического адреса |
| После перезагрузки NAS баны как будто исчезли | iptables хоста сброшен вместе с сетевым стеком — контейнер должен переприменить правила при старте |
| Джейл npm-auth банит слишком агрессивно | maxretry слишком низкий для фонового шума ботов — увеличьте порог |
После того как SSH, Vaultwarden и NPM у каждого под своим джейлом, а собственный IP в whitelist — фоновый шум от автоматических сканеров паролей перестаёт быть заметен в логах вообще, и там остаются только вещи, которые действительно стоит смотреть глазами.