Как мы защитили VDS стартапа от 53 000 попыток взлома за 5 дней

Исходная ситуация

Стартап «СтартТех» арендовал VDS для своего MVP — SaaS-платформы для управления проектами. Стандартная конфигурация: 4 ядра, 8 GB RAM, Ubuntu 22.04, один публичный IP. Разработчики развернули приложение, открыли порты 22, 80, 443 и ушли тестировать фичи.

Через 5 дней основатель обратился к нам в itfresh.ru: «Сервер тормозит, CPU на 100%». Мы подключились и обнаружили картину, знакомую каждому, кто выставлял голый сервер в интернет:

# Количество неудачных попыток входа за 5 дней
$ grep 'Failed password' /var/log/auth.log | wc -l
53732

# Топ-10 атакующих IP
$ grep 'Failed password' /var/log/auth.log | grep -oP '\d+\.\d+\.\d+\.\d+' | sort | uniq -c | sort -rn | head -10
   4521 218.92.0.xxx
   3847 61.177.172.xxx
   2918 222.186.15.xxx
   2341 49.88.112.xxx
   1876 116.31.116.xxx
   1654 103.99.0.xxx
   1432 185.224.128.xxx
   1298 45.141.84.xxx
   1187 92.255.85.xxx
   1043 31.184.198.xxx

# Динамика: первые 7 часов — 2847, через сутки — 42799
$ grep 'Failed password' /var/log/auth.log | awk '{print $1, $2}' | uniq -c | sort -rn | head

53 732 попытки перебора паролей. Ботнеты не спят и не устают — каждый новый IP в интернете сканируется в течение минут после появления.

Анализ атак: что именно ломают

Прежде чем защищаться, мы изучили, как именно атакуют. Это важно для выбора правильных контрмер.

# Какие имена пользователей пробуют
$ grep 'Failed password' /var/log/auth.log | grep -oP 'for \K\w+' | sort | uniq -c | sort -rn | head -15
  12847 root
   4521 admin
   3218 test
   2654 ubuntu
   1987 user
   1543 postgres
   1321 oracle
   1198 mysql
    987 ftpuser
    876 deploy
    654 git
    543 jenkins
    432 www-data
    321 pi
    298 nagios

Паттерны атак:

  • 75% — SSH brute force на порт 22. Перебор стандартных логинов (root, admin, test) со словарными паролями.
  • 15% — веб-сканеры на порты 80/443. Поиск уязвимых CMS (WordPress, Joomla), открытых phpMyAdmin, .env файлов.
  • 10% — сканирование портов. Поиск открытых Redis, MongoDB, Elasticsearch без аутентификации.

География атак (по GeoIP):

# Установка и использование GeoIP
$ apt install geoip-bin
$ grep 'Failed password' /var/log/auth.log | grep -oP '\d+\.\d+\.\d+\.\d+' | sort -u | while read ip; do
    echo "$(geoiplookup $ip | cut -d: -f2) $ip"
  done | awk '{print $1, $2}' | sort | uniq -c | sort -rn | head
   847 CN, China
   432 US, United
   298 RU, Russian
   187 IN, India
   143 BR, Brazil
   121 KR, Korea
    98 VN, Vietnam

Уровень 1: базовая защита SSH

Первый шаг — закрыть самые очевидные дыры. Каждая из этих мер реализуется за 5 минут:

# 1. Смена порта SSH (снижает атаки на 80-98%)
# /etc/ssh/sshd_config
Port 2244

# 2. Запрет входа под root
PermitRootLogin no

# 3. Ограничение списка пользователей
AllowUsers deploy

# 4. Таймаут и ограничение попыток
LoginGraceTime 30
MaxAuthTries 3
MaxSessions 3

# 5. Отключение парольной аутентификации (только ключи)
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no

# Применяем
systemctl restart sshd

Генерация SSH-ключа:

# На рабочей машине разработчика
ssh-keygen -t ed25519 -C "developer@starttech.ru" -f ~/.ssh/starttech

# Копирование на сервер (пока пароль ещё работает)
ssh-copy-id -i ~/.ssh/starttech.pub -p 2244 deploy@server-ip

# Конфиг для удобного подключения
# ~/.ssh/config
Host starttech
    HostName server-ip
    Port 2244
    User deploy
    IdentityFile ~/.ssh/starttech

Базовый файрвол через UFW:

# Настройка UFW
ufw default deny incoming
ufw default allow outgoing
ufw allow 2244/tcp comment 'SSH'
ufw allow 80/tcp comment 'HTTP'
ufw allow 443/tcp comment 'HTTPS'
ufw enable

# Проверка
ufw status verbose

Результат: после смены порта с 22 на 2244 количество попыток упало с 53 732 до 2 757 за следующие 5 дней — снижение на 95%.

Уровень 2: Fail2ban — умная блокировка

Fail2ban мониторит логи и автоматически блокирует IP-адреса после нескольких неудачных попыток. Мы настроили его с агрессивными параметрами:

# /etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600      # Бан на 1 час
findtime = 600      # Окно наблюдения 10 минут
maxretry = 3        # 3 попытки — бан
banaction = iptables-multiport
action = %(action_mwl)s  # Бан + письмо с whois и логами

# Рецидивисты — бан на неделю
[recidive]
enabled = true
logpath = /var/log/fail2ban.log
bantime = 604800    # 7 дней
findtime = 86400    # Окно 24 часа
maxretry = 3        # 3 бана за сутки = бан на неделю

# SSH — кастомный jail
[sshd]
enabled = true
port = 2244
logpath = /var/log/auth.log
maxretry = 3
findtime = 300
bantime = 7200      # 2 часа за SSH brute force

# Nginx — защита от перебора паролей
[nginx-http-auth]
enabled = true
logpath = /var/log/nginx/error.log
maxretry = 5

# Nginx — защита от сканеров (404 flood)
[nginx-botsearch]
enabled = true
logpath = /var/log/nginx/access.log
maxretry = 10
findtime = 60
bantime = 3600

Проверка работы:

# Статус всех jail-ов
fail2ban-client status

# Детали по SSH jail
fail2ban-client status sshd
# Status for the jail: sshd
# |- Filter
# |  |- Currently failed: 2
# |  |- Total failed:     847
# |  `- File list:        /var/log/auth.log
# `- Actions
#    |- Currently banned: 34
#    |- Total banned:     287
#    `- Banned IP list:   218.92.0.xxx 61.177.172.xxx ...

# Ручная разблокировка (если заблокировали себя)
fail2ban-client set sshd unbanip 1.2.3.4

Уровень 3: iptables rate limiting

Fail2ban реагирует на неудачные попытки. Но что если атакующий угадает пароль с первой попытки? Для этого мы добавили rate limiting на уровне iptables — ограничение количества новых соединений:

# Rate limiting для SSH: максимум 4 новых соединения за 60 секунд с одного IP
iptables -A INPUT -p tcp --dport 2244 -m state --state NEW -m recent --set --name SSH
iptables -A INPUT -p tcp --dport 2244 -m state --state NEW -m recent --update \
  --seconds 60 --hitcount 4 --name SSH -j DROP

# Rate limiting для HTTP: максимум 30 соединений/сек с одного IP
iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 30 -j DROP
iptables -A INPUT -p tcp --dport 443 -m connlimit --connlimit-above 30 -j DROP

# Защита от SYN flood
iptables -A INPUT -p tcp --syn -m limit --limit 50/s --limit-burst 100 -j ACCEPT
iptables -A INPUT -p tcp --syn -j DROP

# Логирование заблокированных пакетов
iptables -A INPUT -j LOG --log-prefix "IPTables-Dropped: " --log-level 4

# Сохранение правил
netfilter-persistent save

Дополнительно мы заблокировали целые подсети, из которых шли массовые атаки:

# Блокировка подсетей с помощью ipset (эффективнее отдельных правил)
ipset create blacklist hash:net
ipset add blacklist 218.92.0.0/16
ipset add blacklist 61.177.0.0/16
ipset add blacklist 222.186.0.0/16

iptables -A INPUT -m set --match-set blacklist src -j DROP

Уровень 4: Port Knocking и CrowdSec

Для максимальной защиты SSH мы внедрили port knocking — SSH-порт открывается только после «секретной» последовательности обращений к закрытым портам:

# /etc/knockd.conf
[options]
    UseSyslog
    logfile = /var/log/knockd.log

[openSSH]
    sequence    = 7000,8000,9000
    seq_timeout = 10
    command     = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 2244 -j ACCEPT
    tcpflags    = syn

[closeSSH]
    sequence    = 9000,8000,7000
    seq_timeout = 10
    command     = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 2244 -j ACCEPT
    tcpflags    = syn

# Подключение с клиента
knock server-ip 7000 8000 9000 && ssh -p 2244 deploy@server-ip

CrowdSec — коллективная защита. В отличие от Fail2ban, CrowdSec использует общую базу вредоносных IP со всех участников сети:

# Установка CrowdSec
curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | bash
apt install crowdsec crowdsec-firewall-bouncer-iptables

# Подключение парсеров и сценариев
cscli parsers install crowdsecurity/sshd-logs
cscli parsers install crowdsecurity/nginx-logs
cscli scenarios install crowdsecurity/ssh-bf
cscli scenarios install crowdsecurity/http-crawl-non_statics
cscli scenarios install crowdsecurity/http-bad-user-agent

# Статистика
cscli metrics
# ┌───────────────────────────┬──────────┬──────────┐
# │ Scenario                  │ Current  │ Total    │
# ├───────────────────────────┼──────────┼──────────┤
# │ crowdsecurity/ssh-bf      │ 12       │ 347      │
# │ crowdsecurity/http-crawl  │ 5        │ 89       │
# └───────────────────────────┴──────────┴──────────┘

cscli decisions list  # Текущие баны

Преимущество CrowdSec: когда ботнет атакует другой сервер в сети CrowdSec, ваш сервер узнаёт об этом IP ещё до того, как он доберётся до вас.

Защита веб-приложения: Cloudflare WAF и ModSecurity

SSH — только часть проблемы. Веб-приложение «СтартТех» тоже нуждалось в защите:

Cloudflare WAF — первая линия обороны:

  • Проксирование трафика через Cloudflare (реальный IP сервера скрыт).
  • Managed Rules блокируют SQLi, XSS, RCE, LFI автоматически.
  • Rate Limiting: не больше 60 запросов/мин на endpoint логина.
  • Bot Management: блокировка автоматических сканеров.
# Nginx — доверять IP от Cloudflare, блокировать прямой доступ
# /etc/nginx/conf.d/cloudflare.conf
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
real_ip_header CF-Connecting-IP;

# Блокировка прямого доступа по IP (без домена через CF)
server {
    listen 80 default_server;
    listen 443 ssl default_server;
    return 444;  # Закрыть соединение
}

ModSecurity — вторая линия на самом сервере:

# Установка ModSecurity для Nginx
apt install libmodsecurity3 libnginx-mod-http-modsecurity

# /etc/nginx/modsecurity/modsecurity.conf
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess Off
SecRequestBodyLimit 10485760

# Подключение OWASP Core Rule Set
Include /etc/nginx/modsecurity/crs/crs-setup.conf
Include /etc/nginx/modsecurity/crs/rules/*.conf

Мониторинг и автоматический аудит

Защита — это не одноразовое действие. Мы настроили ежедневный cron-скрипт для мониторинга безопасности:

#!/bin/bash
# /opt/security-audit.sh — ежедневный отчёт безопасности

REPORT="/tmp/security-report-$(date +%F).txt"

echo "=== ОТЧЁТ БЕЗОПАСНОСТИ $(date) ===" > "$REPORT"

# SSH: неудачные попытки за сутки
echo -e "\n--- SSH Failed Logins (24h) ---" >> "$REPORT"
grep 'Failed password' /var/log/auth.log | \
  awk -v d="$(date -d '24 hours ago' '+%b %e')" '$0 ~ d' | wc -l >> "$REPORT"

# Fail2ban: заблокированные IP
echo -e "\n--- Fail2ban Bans (24h) ---" >> "$REPORT"
fail2ban-client status sshd >> "$REPORT" 2>&1

# CrowdSec: решения
echo -e "\n--- CrowdSec Decisions ---" >> "$REPORT"
cscli decisions list --limit 20 >> "$REPORT" 2>&1

# Открытые порты
echo -e "\n--- Open Ports ---" >> "$REPORT"
ss -tnlp >> "$REPORT"

# Обновления безопасности
echo -e "\n--- Security Updates ---" >> "$REPORT"
apt list --upgradable 2>/dev/null | grep -i security >> "$REPORT"

# AIDE: проверка целостности файлов
echo -e "\n--- File Integrity (AIDE) ---" >> "$REPORT"
aide --check 2>&1 | tail -20 >> "$REPORT"

# Отправка в Telegram
curl -s -X POST "https://api.telegram.org/bot/sendDocument" \
  -F chat_id="" \
  -F document=@"$REPORT" \
  -F caption="Security Report $(date +%F)"

Дополнительно мы настроили AIDE (Advanced Intrusion Detection Environment) для отслеживания изменений в критичных системных файлах:

# Установка и инициализация
apt install aide
aideinit
mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db

# Проверка (обнаружит изменения в /etc, /bin, /sbin)
aide --check

# При легитимных изменениях — обновление базы
aide --update
mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db

Результаты и выводы

Через месяц после внедрения всех уровней защиты статистика «СтартТех» выглядит так:

МетрикаДо защитыПосле защиты
SSH brute force попытки/день10 74612 (через port knocking)
Заблокированные IP (Fail2ban)0~150/день
Заблокированные IP (CrowdSec)0~400/день (превентивно)
Веб-атаки (Cloudflare WAF)Не отслеживались~2000 блокировок/день
Утилизация CPU100% (ботнет)5-15% (легитимная нагрузка)

Ключевые принципы защиты VDS:

  • Defense in depth — многоуровневая защита. Каждый уровень останавливает свой класс атак. Прорыв одного уровня не означает компрометацию.
  • Автоматизация — Fail2ban, CrowdSec, AIDE работают без участия человека. Администратор получает отчёты и реагирует только на аномалии.
  • Минимальная поверхность атаки — закрыть всё, что не нужно. Если сервис не используется — его порт не должен быть открыт.
  • Мониторинг — атаки неизбежны. Важно знать о них и реагировать до того, как они станут успешными.

Каждый сервер в интернете подвергается сканированию в течение минут после появления. Это не вопрос «если», а вопрос «когда». Мы в itfresh.ru рекомендуем настраивать защиту до развёртывания приложения, а не после первого инцидента.

Часто задаваемые вопросы

Смена порта снижает количество автоматических атак на 80-98%, но не защищает от целенаправленного сканирования (nmap найдёт ваш SSH за минуту). Это первый шаг, но не единственный. Обязательно добавляйте SSH-ключи, Fail2ban и файрвол.
Мы рекомендуем оба. Fail2ban проще в настройке и работает локально — он блокирует IP после атаки на ваш сервер. CrowdSec использует коллективную базу данных и блокирует известные вредоносные IP превентивно, до того как они начнут атаку. Вместе они дают лучший результат.
При настройке maxretry=3 и findtime=600 — маловероятно. Обычный пользователь не вводит пароль неправильно 3 раза за 10 минут. Но добавьте свой IP и IP офиса в ignoreip в jail.local, чтобы исключить случайную блокировку администраторов.
Используйте ModSecurity + OWASP CRS прямо на сервере. Это WAF, который работает как модуль Nginx/Apache и фильтрует вредоносные запросы без внешних сервисов. Производительность снижается на 5-10%, но защита от SQL-инъекций, XSS и других атак стоит этого.
Да, если сервер имеет публичный IP. Ботнетам всё равно, тестовый это сервер или продакшен. Компрометация тестового сервера позволяет атакующему майнить криптовалюту, рассылать спам или атаковать другие серверы от вашего имени. Минимум — смена порта SSH, SSH-ключи и UFW. Это 10 минут работы.

Нужна помощь с проектом?

Специалисты АйТи Фреш помогут с архитектурой, DevOps, безопасностью и разработкой — 15+ лет опыта

📞 Связаться с нами
#защита сервера#fail2ban#iptables#ssh brute force#port knocking#crowdsec#cloudflare waf#modsecurity
Комментарии 0

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

загрузка...