Свой fail2ban на Python

Решил поделиться скриптом, который является мини аналогом известного fail2ban. Использую его на маленьких проектах. Логика простая 1) Скрипт читает логи (/var/log/auth.log). 2) Если у IP слишком много неудачных попыток входа — блокирует его через iptables или ufw 3) White-list (список IP, которые никогда не банятся). 4) Авторазбан через заданное время (например, 1 час). Всё это будет работать на Python 3.9 и под управлением Debian 12

#!/usr/bin/env python3
import re
import subprocess
import json
from datetime import datetime, timedelta

# -------------------
# Настройки
# -------------------
LOG_FILE = "/var/log/auth.log"       # путь к логам SSH
STATE_FILE = "banned_state.json"     # база забаненных IP с временем
MAX_ATTEMPTS = 5                     # сколько неудачных попыток до блокировки
BAN_TIME = 60 * 60                   # время бана (в секундах)  - 1 час
USE_UFW = True                       # True - ufw, False - iptables

# Белый список IP
WHITE_LIST = {"127.0.0.1", "192.168.0.1"}

# -------------------
# Работа с состоянием
# -------------------
def load_state():
    try:
        with open(STATE_FILE, "r") as f:
            return json.load(f)
    except FileNotFoundError:
        return {}

def save_state(state):
    with open(STATE_FILE, "w") as f:
        json.dump(state, f)

# -------------------
# Поиск атакующих IP
# -------------------
def parse_failed_attempts():
    failed_ips = {}
    pattern = re.compile(r"Failed password.*from (\d+\.\d+\.\d+\.\d+)")
    with open(LOG_FILE, "r") as f:
        for line in f:
            match = pattern.search(line)
            if match:
                ip = match.group(1)
                failed_ips[ip] = failed_ips.get(ip, 0) + 1
    return failed_ips

# -------------------
# Бан / Разбан IP
# -------------------
def ban_ip(ip):
    print(f"Блокирую IP: {ip}")
    if USE_UFW:
        subprocess.run(["ufw", "deny", "from", ip], check=False)
    else:
        subprocess.run(["iptables", "-A", "INPUT", "-s", ip, "-j", "DROP"], check=False)

def unban_ip(ip):
    print(f"Разбан IP: {ip}")
    if USE_UFW:
        subprocess.run(["ufw", "delete", "deny", "from", ip], check=False)
    else:
        subprocess.run(["iptables", "-D", "INPUT", "-s", ip, "-j", "DROP"], check=False)

# -------------------
# Основная логика
# -------------------
def main():
    state = load_state()
    failed_ips = parse_failed_attempts()

    now = datetime.now()
    changed = False

    # Проверяем новые атаки
    for ip, count in failed_ips.items():
        if ip in WHITE_LIST:
            continue  # никогда не баним белые IP

        if count >= MAX_ATTEMPTS and ip not in state:
            ban_ip(ip)
            state[ip] = (now + timedelta(seconds=BAN_TIME)).isoformat()
            print(f"[{now}] IP {ip} заблокирован (попыток: {count})")
            changed = True

    # Проверяем срок действия бана
    to_unban = []
    for ip, until_str in state.items():
        until = datetime.fromisoformat(until_str)
        if now >= until:
            unban_ip(ip)
            to_unban.append(ip)
            changed = True

    for ip in to_unban:
        del state[ip]

    if changed:
        save_state(state)

if __name__ == "__main__":
    main()

Белый список (WHITE_LIST): туда можно добавить свои IP. Авторазбан через BAN_TIME (по умолчанию 1 час). Состояние хранится в banned_state.json. Работает как с ufw, так и с iptables (выбирается через USE_UFW). Обычно на Deb ставят ufw, поэтому оставляем как есть

Осталось добавить в crontab -e, лучше проверять каждые 10 минут

*/10 * * * * /usr/bin/python3 /path/to/firewall_autoblocker.py

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

Оставить комментарий

Пока нет комментариев. Будьте первым!