Хосты: webserver.local, u1.webserver.local, u2.webserver.local. Требования itsec.

Требования

  • Basic Auth на прокси, логины и пароли внешних клиентов в .htpasswd.
  • Запрос уходит на frontend только после успешной авторизации.
  • 5 неудачных попыток с одного IP за 5 минут: IP блокируется.
  • Блокировка только по IP, учётная запись клиента не блокируется.

1. Файл паролей

# htpasswd: httpd-tools (RHEL/RedOS) или apache2-utils (Astra)
htpasswd -c -B /etc/nginx/.htpasswd client1
htpasswd -B /etc/nginx/.htpasswd client2
chown root:nginx /etc/nginx/.htpasswd   # на Astra группа www-data
chmod 640 /etc/nginx/.htpasswd

2. Конфиг NGINX

/etc/nginx/conf.d/webserver.local.conf

server {
    listen 443 ssl;
    server_name webserver.local u1.webserver.local u2.webserver.local;

    access_log /var/log/nginx/webserver.local.access.log;
    error_log  /var/log/nginx/webserver.local.error.log error;

    auth_basic           "Restricted area";
    auth_basic_user_file /etc/nginx/.htpasswd;

    location / {
        proxy_pass http://frontend_backend;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Authorization     "";
    }
}

auth_basic стоит на уровне server, поэтому действует на все три имени.

Если перед wproxysrv1 есть балансировщик, в error.log будет его IP, а не клиентский, и fail2ban забанит не того. Тогда нужен set_real_ip_from <IP>; real_ip_header X-Forwarded-For; (модуль ngx_http_realip_module).

3. Фильтр fail2ban

/etc/fail2ban/filter.d/nginx-basic-auth.conf

[Definition]
failregex = ^\s*\[error\] \d+#\d+: \*\d+ user "[^"]*":? (?:password mismatch|was not found in "[^"]*"), client: <HOST>,
ignoreregex =

Секция [Definition] обязательна, без неё fail2ban ругается на синтаксис. Сообщение no user/password was provided в фильтр не входит: оно пишется при каждом открытии окна ввода и не является неверной попыткой.

4. Jail

/etc/fail2ban/jail.d/nginx-basic-auth.local

[nginx-basic-auth]
enabled  = true
filter   = nginx-basic-auth
logpath  = /var/log/nginx/webserver.local.error.log
port     = http,https
maxretry = 5
findtime = 300
bantime  = 3600
# RedOS/RHEL: banaction = firewallcmd-ipset
# Astra:      banaction = iptables-multiport

Время в секундах: fail2ban 0.9.x не понимает 5m и 1h. Bantime в требованиях не указан, 1 час взят по умолчанию, нужно уточнить у itsec. Свой IP можно защитить через ignoreip.

5. Применение

nginx -t && systemctl reload nginx
systemctl enable --now fail2ban && systemctl restart fail2ban

6. Проверка

# регулярка на реальном логе
fail2ban-regex /var/log/nginx/webserver.local.error.log /etc/fail2ban/filter.d/nginx-basic-auth.conf

# тест: 6 неверных попыток (с тестового хоста)
for i in {1..6}; do curl -k -s -o /dev/null -w "%{http_code}\n" -u bad:bad https://u1.webserver.local/; done

# статус: Currently failed / Total failed / Banned IP list
fail2ban-client status nginx-basic-auth

# события Found / Ban / Unban
tail -f /var/log/fail2ban.log

# бан в файрволе
ipset list | grep -A5 f2b-nginx-basic-auth        # firewalld + ipset
iptables -L -n | grep -A5 f2b-nginx-basic-auth    # iptables

# разбан
fail2ban-client set nginx-basic-auth unbanip <IP>

Первые 5 запросов дают 401, затем таймаут или reset: бан сработал.