Как использовать /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''