opened image

DDoS-защита на уровне сервера: nginx + iptables + fail2ban

 

Сервер без защиты от DDoS ложится при первой же серьёзной атаке. Полную защиту от объёмных атак (10+ Gbps) даёт только CDN или специализированный scrubbing-центр, но от прикладных атак (HTTP-flood, slowloris, SYN-flood) можно защититься на уровне самого сервера. Разберём трёхуровневую защиту: iptables на сетевом уровне, nginx на уровне приложения и fail2ban для автоматической блокировки.

 

 

Уровни защиты

 

Атаки бывают разных типов, и каждый требует своего инструмента:

  • Объёмные атаки (volumetric) - UDP flood, ICMP flood, заполнение канала. Против них помогает только CDN (Cloudflare, DDoS-Guard) или BGP-anycast. Сервер здесь бессилен.

  • SYN flood - полуоткрытые TCP-соединения, переполнение очереди. Защита: iptables + sysctl.

  • Connection flood - тысячи реальных TCP-соединений. Защита: iptables limit, nginx limit_conn.

  • HTTP-flood - легитимные HTTP-запросы с высокой частотой. Защита: nginx limit_req, fail2ban.

  • Slowloris - медленная отправка заголовков, удержание соединений. Защита: client_header_timeout в nginx.

 

 

Первая линия: CDN

 

До настройки сервера - важный шаг. Поставьте DNS за Cloudflare (бесплатный план) или DDoS-Guard. Тогда реальный IP сервера скрыт, и объёмные атаки поглощает CDN.

 

Проверка, не утёк ли реальный IP:

 

# Ищем IP в исторических DNS-записях
# Проверяем MX-записи - часто раскрывают реальный IP
dig MX yourdomain.com

 

Если IP уже известен атакующим - смените его у хостера.

 

 

Защита iptables от SYN-flood и connection flood

 

Настройка sysctl для TCP

 

sudo nano /etc/sysctl.conf

 

Добавляем:

# SYN cookies - защита от SYN flood
net.ipv4.tcp_syncookies = 1

# Уменьшаем время ожидания для полуоткрытых соединений
net.ipv4.tcp_syn_retries = 3
net.ipv4.tcp_synack_retries = 2

# Размер очереди для входящих соединений
net.ipv4.tcp_max_syn_backlog = 4096
net.core.somaxconn = 4096

# Защита от спуфинга
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Игнорировать ICMP broadcast (Smurf attack)
net.ipv4.icmp_echo_ignore_broadcasts = 1

 

Применяем:

sudo sysctl -p

 

 

Правила iptables

 

# Ограничение новых соединений - не более 20 новых TCP в секунду с одного IP
sudo iptables -A INPUT -p tcp --syn -m limit --limit 20/s --limit-burst 50 -j ACCEPT
sudo iptables -A INPUT -p tcp --syn -j DROP

# Ограничение числа одновременных соединений с одного IP (max 50)
sudo iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 50 -j REJECT --reject-with tcp-reset
sudo iptables -A INPUT -p tcp --dport 443 -m connlimit --connlimit-above 50 -j REJECT --reject-with tcp-reset

# Блокировка INVALID-пакетов
sudo iptables -A INPUT -m state --state INVALID -j DROP

# Ограничение ICMP (пинг) - не более 1 в секунду
sudo iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j DROP

# Защита SSH от брутфорса - не более 4 попыток в минуту
sudo iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set
sudo iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 -j DROP

 

Сохраняем правила:

 

sudo apt install iptables-persistent -y
sudo netfilter-persistent save

 

 

 

 

Защита на уровне Nginx

 

limit_req - ограничение частоты запросов

 

Директива limit_req_zone определяет зону ограничения в блоке http:

 

http {
    # Зона для ограничения запросов: ключ - IP, память - 10 МБ, лимит - 10 req/s
    limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s;

    # Зона для API - строже
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=2r/s;

    # Логировать превышения как warning, не error
    limit_req_log_level warn;

    # Код ответа при превышении (429 = Too Many Requests)
    limit_req_status 429;
}

 

В блоке server или location:

server {
    listen 80;
    server_name example.com;

    # Применяем к всему сайту
    limit_req zone=req_limit burst=20 nodelay;

    location /api/ {
        limit_req zone=api_limit burst=5;
        proxy_pass http://backend;
    }

    location /wp-login.php {
        limit_req zone=api_limit burst=2 nodelay;
        # Или сразу закрыть для всех, кроме вашего IP
        allow 1.2.3.4;
        deny all;
    }
}

 

Параметры burst и nodelay:

  • burst=20 - разрешает кратковременный пик до 20 запросов сверх лимита

  • nodelay - обрабатывает burst сразу, без задержки; без этого nginx добавляет задержку

 

limit_conn - ограничение числа соединений

 

http {
    # Зона для ограничения соединений
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
    limit_conn_status 503;
}

server {
    # Не более 20 одновременных соединений с одного IP
    limit_conn conn_limit 20;
}

 

 

Защита от Slowloris

 

server {
    # Таймауты для предотвращения Slowloris
    client_header_timeout 10s;
    client_body_timeout 10s;
    send_timeout 10s;

    # Максимальный размер тела запроса
    client_max_body_size 10m;

    # Keepalive - ограничиваем время и количество запросов
    keepalive_timeout 65s;
    keepalive_requests 100;
}

 

 

Блокировка по User-Agent и заголовкам

 

# Блокировка пустых User-Agent (характерно для ботов)
if ($http_user_agent = "") {
    return 444;
}

# Блокировка известных ботов-атакующих
map $http_user_agent $bad_bot {
    default 0;
    ~*python-requests 1;
    ~*zgrab 1;
    ~*masscan 1;
    ~*nikto 1;
}

server {
    if ($bad_bot) {
        return 444;  # 444 - nginx закрывает соединение без ответа
    }
}

 

 

 

 

Fail2ban для HTTP-flood

 

Fail2ban анализирует логи и блокирует IP через iptables при превышении порогов.

 

Установка

 

sudo apt install fail2ban -y
sudo systemctl enable fail2ban

 

 

Jail для nginx

 

Создаём файл /etc/fail2ban/jail.d/nginx.conf:

 

[nginx-http-auth]
enabled = true
port    = http,https
logpath = /var/log/nginx/error.log
maxretry = 3
bantime = 3600

[nginx-limit-req]
enabled  = true
port     = http,https
logpath  = /var/log/nginx/error.log
maxretry = 10
findtime = 60
bantime  = 600
filter   = nginx-limit-req

[nginx-botsearch]
enabled  = true
port     = http,https
logpath  = /var/log/nginx/access.log
maxretry = 2
findtime = 60
bantime  = 86400
filter   = nginx-botsearch

 

Фильтр для limit_req - /etc/fail2ban/filter.d/nginx-limit-req.conf:

[Definition]
failregex = limiting requests, excess:.* by zone .*, client: <HOST>
ignoreregex =

 

Фильтр для поиска ботов - /etc/fail2ban/filter.d/nginx-botsearch.conf:

[Definition]
failregex = ^<HOST> -.*"(GET|POST).*(\.php|\.asp|\.env|\.git).*" (404|444|403)
ignoreregex =

 

Перезапуск и проверка:

sudo systemctl restart fail2ban
sudo fail2ban-client status
sudo fail2ban-client status nginx-limit-req

 

Ручная блокировка IP:

sudo fail2ban-client set nginx-limit-req banip 1.2.3.4

 

Ручная разблокировка:

sudo fail2ban-client set nginx-limit-req unbanip 1.2.3.4

 

 

Мониторинг во время атаки

 

Посмотреть топ IP по числу запросов:

 

awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

 

Текущие соединения по IP:

ss -tn | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head -20

 

Число SYN_RECV (признак SYN flood):

 

ss -s | grep SYN
# или
cat /proc/net/tcp | awk '$4 == "02"' | wc -l

 

Заблокированные IP в iptables:

 

sudo iptables -L -n | grep DROP | wc -l

 

 

 

 

Итог

 

Трёхуровневая защита: sysctl + iptables блокируют атаки на уровне TCP, nginx limit_req и limit_conn отсекают HTTP-flood до попадания в приложение, fail2ban автоматически банит злоупотребляющие IP по данным из логов. Для WordPress и API устанавливайте лимиты строже, чем для обычного контента. Главный принцип - чем раньше по стеку отклоняется атакующий трафик, тем меньше нагрузка на сервер.