Как использовать /opt/fail2ban для конфигов\\ Самый универсальный способ — создать символические ссылки из /etc/fail2ban/jail.d и /etc/fail2ban/filter.d на соответствующие файлы и папки в /opt/fail2ban: sudo ln -s /opt/fail2ban/config/jail.d/* /etc/fail2ban/jail.d/ sudo ln -s /opt/fail2ban/config/filter.d/* /etc/fail2ban/filter.d/ ====== Настройка Fail2ban с Nginx Proxy Manager ====== ===== Описание ===== Fail2ban - это система защиты от брутфорс атак и автоматизированных сканирований. В связке с Nginx Proxy Manager (nginxpm) обеспечивает автоматическую блокировку подозрительных IP-адресов на основе анализа логов доступа. ===== Требования ===== * Docker и docker-compose * Nginx Proxy Manager (nginxpm) * Ubuntu/Debian система * Права root или sudo ===== Установка Fail2ban ===== # Обновление пакетов sudo apt update # Установка fail2ban sudo apt install fail2ban -y # Включение автозапуска sudo systemctl enable fail2ban sudo systemctl start fail2ban ===== Структура конфигурации ===== /opt/fail2ban/ ├── scripts/ │ ├── manage-fail2ban.sh # Скрипт управления и мониторинга │ └── parse-nginx-logs.sh # Парсер логов nginxpm ├── logs/ │ ├── nginx-access.log # Обработанные логи для fail2ban (последний месяц) │ └── parser.log # Логи работы парсера └── config/ ├── jail.local # Конфигурация jail'ов └── filters/ # Кастомные фильтры ===== Docker Compose конфигурация Nginx Proxy Manager ===== services: app: image: 'jc21/nginx-proxy-manager:latest' container_name: nginxpm restart: always ports: - '80:80' # HTTP - '443:443' # HTTPS environment: DB_MYSQL_HOST: "db" DB_MYSQL_PORT: 3306 DB_MYSQL_USER: "npm" DB_MYSQL_PASSWORD: "YOUR_PASSWORD" DB_MYSQL_NAME: "npm" volumes: - ./data:/data # данные NPM - ./letsencrypt:/etc/letsencrypt # сертификаты depends_on: - db networks: - webproxy db: image: 'mariadb:10.11' container_name: nginxpm_db restart: always environment: MYSQL_ROOT_PASSWORD: "YOUR_ROOT_PASSWORD" MYSQL_DATABASE: "npm" MYSQL_USER: "npm" DB_MYSQL_PASSWORD: "YOUR_PASSWORD" volumes: - ./mysql:/var/lib/mysql # база MariaDB networks: - webproxy networks: webproxy: external: true ===== Создание директорий ===== # Создание структуры папок sudo mkdir -p /opt/fail2ban/{scripts,logs,config,config/filters} ===== Скрипт парсинга логов parse-nginx-logs.sh ===== Создать файл **/opt/fail2ban/scripts/parse-nginx-logs.sh**: #!/bin/bash # Скрипт парсинга логов nginx-proxy-manager для fail2ban # Обрабатывает все access логи из папки nginxpm (данные за последний месяц) LOG_DIR="/opt/nginxpm/data/logs" LOG_FILE="/opt/fail2ban/logs/nginx-access.log" CONTAINER_NAME="nginxpm" PARSER_LOG="/opt/fail2ban/logs/parser.log" # Создаем директории если они не существуют mkdir -p "$(dirname "$LOG_FILE")" "$(dirname "$PARSER_LOG")" # Проверяем, что контейнер запущен if ! docker ps --format "table {{.Names}}" | grep -q "^${CONTAINER_NAME}$"; then echo "$(date): Контейнер $CONTAINER_NAME не найден или не запущен" >> "$PARSER_LOG" exit 1 fi # Проверяем существование директории с логами if [ ! -d "$LOG_DIR" ]; then echo "$(date): Директория логов $LOG_DIR не найдена" >> "$PARSER_LOG" exit 1 fi # Очищаем целевой файл (файл пересоздается полностью при каждом запуске) > "$LOG_FILE" # Счетчик обработанных файлов processed_files=0 # Дата отсечки (30 дней назад) для фильтрации логов CUTOFF_DATE=$(date -d '30 days ago' '+%d/%b/%Y') CUTOFF_TIMESTAMP=$(date -d '30 days ago' '+%s') # Обрабатываем все файлы с окончанием *_access.log for logfile in "$LOG_DIR"/*_access.log; do # Проверяем, что файл существует (на случай если паттерн не найден) if [ -f "$logfile" ]; then # Читаем файл и фильтруем записи не старше 30 дней awk -v cutoff_ts="$CUTOFF_TIMESTAMP" ' { # Извлекаем дату из строки лога (формат: DD/MMM/YYYY) match($0, /\[([0-9]{2}\/[A-Za-z]{3}\/[0-9]{4})/, date_match) if (date_match[1]) { # Преобразуем дату в timestamp для сравнения cmd = "date -d \"" date_match[1] "\" +%s 2>/dev/null" cmd | getline log_timestamp close(cmd) # Если дата лога >= даты отсечки, выводим строку if (log_timestamp >= cutoff_ts) { print $0 } } else { # Если не удалось извлечь дату, оставляем запись print $0 } }' "$logfile" >> "$LOG_FILE" processed_files=$((processed_files + 1)) echo "$(date): Обработан файл: $(basename "$logfile") - отфильтрованы записи за последний месяц" >> "$PARSER_LOG" fi done # Если не найдено ни одного файла логов if [ $processed_files -eq 0 ]; then echo "$(date): Не найдено файлов логов *_access.log в директории $LOG_DIR" >> "$PARSER_LOG" # Создаем пустой файл чтобы fail2ban не ругался touch "$LOG_FILE" fi # Устанавливаем права доступа chmod 644 "$LOG_FILE" # Логируем статистику каждый час (когда минуты = 00) MINUTE=$(date +%M) if [ "$MINUTE" = "00" ]; then final_lines=$(wc -l < "$LOG_FILE" 2>/dev/null || echo "0") echo "$(date): Итого обработано $processed_files файлов, $final_lines записей (за последний месяц) из контейнера $CONTAINER_NAME" >> "$PARSER_LOG" fi # Ротация логов parser.log (если больше 5MB) if [ -f "$PARSER_LOG" ] && [ $(stat -c%s "$PARSER_LOG" 2>/dev/null || echo 0) -gt 5242880 ]; then mv "$PARSER_LOG" "$PARSER_LOG.old" touch "$PARSER_LOG" chmod 644 "$PARSER_LOG" fi # Удаление записей старше 30 дней из лог-файла (дополнительная очистка) if [ -f "$LOG_FILE" ] && [ -s "$LOG_FILE" ]; then TEMP_FILTERED="/tmp/nginx-final-filtered" # Финальная фильтрация собранного файла awk -v cutoff_ts="$CUTOFF_TIMESTAMP" ' { # Извлекаем дату из строки лога (формат: DD/MMM/YYYY) match($0, /\[([0-9]{2}\/[A-Za-z]{3}\/[0-9]{4})/, date_match) if (date_match[1]) { # Преобразуем дату в timestamp для сравнения cmd = "date -d \"" date_match[1] "\" +%s 2>/dev/null" cmd | getline log_timestamp close(cmd) # Если дата лога >= даты отсечки, выводим строку if (log_timestamp >= cutoff_ts) { print $0 } } else { # Если не удалось извлечь дату, оставляем запись print $0 } }' "$LOG_FILE" > "$TEMP_FILTERED" # Заменяем оригинальный файл отфильтрованным if [ -f "$TEMP_FILTERED" ]; then mv "$TEMP_FILTERED" "$LOG_FILE" chmod 644 "$LOG_FILE" fi fi # Ротация основного лог-файла (если больше 10MB) if [ -f "$LOG_FILE" ] && [ $(stat -c%s "$LOG_FILE" 2>/dev/null || echo 0) -gt 10485760 ]; then mv "$LOG_FILE" "$LOG_FILE.old" touch "$LOG_FILE" chmod 644 "$LOG_FILE" echo "$(date): Выполнена ротация лог-файла $LOG_FILE" >> "$PARSER_LOG" fi exit 0 ===== Настройка прав доступа и cron ===== # Сделать скрипт исполняемым sudo chmod +x /opt/fail2ban/scripts/parse-nginx-logs.sh # Создать задачу cron для автоматического запуска каждые 5 минут sudo crontab -e # Добавить строку: */5 * * * * /opt/fail2ban/scripts/parse-nginx-logs.sh >/dev/null 2>&1 ===== Скрипт управления manage-fail2ban.sh ===== Создать файл **/opt/fail2ban/scripts/manage-fail2ban.sh**: #!/bin/bash # Скрипт управления и мониторинга fail2ban case "$1" in status) echo "=== Статус fail2ban ===" fail2ban-client status echo "" echo "=== Детальная информация ===" for jail in $(fail2ban-client status | grep "Jail list:" | cut -d: -f2 | tr ',' '\n' | xargs); do echo "--- $jail ---" fail2ban-client status "$jail" done ;; reload) echo "Перезагрузка fail2ban..." systemctl reload fail2ban ;; restart) echo "Перезапуск fail2ban..." systemctl restart fail2ban ;; unban) if [ -z "$2" ]; then echo "Использование: $0 unban " exit 1 fi echo "Разблокировка IP: $2" fail2ban-client unban "$2" ;; *) echo "Использование: $0 {status|reload|restart|unban }" exit 1 ;; esac # Сделать скрипт исполняемым sudo chmod +x /opt/fail2ban/scripts/manage-fail2ban.sh ===== Команды управления ===== ==== Управление службой fail2ban ==== # Проверка статуса службы sudo systemctl status fail2ban # Запуск службы sudo systemctl start fail2ban # Остановка службы sudo systemctl stop fail2ban # Перезапуск службы sudo systemctl restart fail2ban # Включить автозапуск sudo systemctl enable fail2ban ==== Использование скрипта управления ==== # Показать статус всех jail'ов sudo /opt/fail2ban/scripts/manage-fail2ban.sh status # Перезагрузить конфигурацию sudo /opt/fail2ban/scripts/manage-fail2ban.sh reload # Перезапустить службу sudo /opt/fail2ban/scripts/manage-fail2ban.sh restart # Разблокировать IP sudo /opt/fail2ban/scripts/manage-fail2ban.sh unban 192.168.1.100 ===== Просмотр логов ===== ==== Логи fail2ban ==== # Основные логи fail2ban sudo tail -f /var/log/fail2ban.log # Логи через systemd sudo journalctl -u fail2ban -f # Просмотр последних 100 записей sudo journalctl -u fail2ban -n 100 # Поиск конкретных событий sudo grep "Ban " /var/log/fail2ban.log | tail -20 sudo grep "Unban " /var/log/fail2ban.log | tail -20 ==== Логи парсера nginx ==== # Лог работы парсера sudo tail -f /opt/fail2ban/logs/parser.log # Просмотр обработанных логов nginx sudo tail -f /opt/fail2ban/logs/nginx-access.log # Статистика размера логов sudo ls -lah /opt/fail2ban/logs/ # Проверка последних записей в исходных логах nginxpm sudo ls -la /opt/nginxpm/data/logs/ sudo tail -10 /opt/nginxpm/data/logs/*_access.log # Подсчет строк в логах sudo wc -l /opt/fail2ban/logs/nginx-access.log sudo wc -l /opt/nginxpm/data/logs/*_access.log ===== Диагностика и тестирование ===== ==== Проверка работы парсера ==== # Запуск парсера вручную sudo /opt/fail2ban/scripts/parse-nginx-logs.sh # Проверка размера результирующего лога sudo wc -l /opt/fail2ban/logs/nginx-access.log # Проверка даты самых старых записей (должно быть не более месяца) sudo head -5 /opt/fail2ban/logs/nginx-access.log sudo tail -5 /opt/fail2ban/logs/nginx-access.log # Проверка что контейнер nginxpm запущен sudo docker ps | grep nginxpm ==== Тестирование фильтров ==== # Тест фильтра на соответствие логу sudo fail2ban-regex /opt/fail2ban/logs/nginx-access.log /etc/fail2ban/filter.d/nginx-scan-block.conf # Проверка конкретного jail sudo fail2ban-client status nginx-scan-block # Тест с выводом совпадений sudo fail2ban-regex --print-all-matched /opt/fail2ban/logs/nginx-access.log /etc/fail2ban/filter.d/nginx-404-flood.conf ===== Мониторинг системы ===== ==== Ежедневные проверки ==== # Проверка статуса системы sudo /opt/fail2ban/scripts/manage-fail2ban.sh status # Размер логов sudo du -sh /opt/fail2ban/logs/ sudo du -sh /opt/nginxpm/data/logs/ # Количество заблокированных IP sudo fail2ban-client status | grep "Currently banned" # Статистика по jail'ам sudo fail2ban-client status sshd sudo fail2ban-client status nginx-404-flood sudo fail2ban-client status nginx-scan-block sudo fail2ban-client status nginx-dos-block ==== Просмотр iptables правил ==== # Просмотр всех правил fail2ban sudo iptables -L -n | grep f2b # Просмотр правил конкретного jail sudo iptables -L f2b-nginx-scan-block -n # Подсчет заблокированных IP sudo iptables -L f2b-nginx-scan-block -n | grep -c "REJECT" ===== Особенности работы ===== * **Полное пересоздание файла**: Файл ''nginx-access.log'' полностью пересоздается каждые 5 минут * **Фильтрация по времени**: Скрипт автоматически отбирает только записи за последний месяц (30 дней) * **Ротация логов**: Автоматическая ротация при превышении размера в 10MB * **Множественные хосты**: Обработка всех файлов ''*_access.log'' в папке nginxpm * **Регулярное обновление**: Запуск каждые 5 минут через cron * **Логирование процесса**: Подробные логи работы парсера в ''/opt/fail2ban/logs/parser.log'' ===== Устранение неполадок ===== ==== Типичные проблемы ==== # Проверка что директория nginxpm существует sudo ls -la /opt/nginxpm/data/logs/ # Проверка прав доступа sudo ls -la /opt/fail2ban/logs/ # Проверка cron задач sudo crontab -l # Проверка запуска Docker контейнера sudo docker logs nginxpm | tail -20 # Проверка работы fail2ban sudo systemctl is-active fail2ban sudo systemctl is-enabled fail2ban ==== Если нет блокировок ==== 1. **Проверить наличие данных в логах**: sudo cat /opt/fail2ban/logs/nginx-access.log | wc -l 2. **Проверить работу фильтров**: sudo fail2ban-regex /opt/fail2ban/logs/nginx-access.log /etc/fail2ban/filter.d/nginx-scan-block.conf 3. **Проверить cron задачу**: sudo systemctl status cron sudo tail -f /var/log/syslog | grep CRON Регулярно проверяйте работу системы командой ''sudo /opt/fail2ban/scripts/manage-fail2ban.sh status'' и следите за размером логов. Для тестирования можно добавить тестовую запись в лог и проверить срабатывание фильтров: ''echo '192.168.1.100 - - [01/Sep/2025:10:00:00 +0000] "GET /.env HTTP/1.1" 404 162 "-" "curl"' | sudo tee -a /opt/fail2ban/logs/nginx-access.log''