guard/docs/research/ddos.md
loki5512344 269daa071f
v0.3: 6-layer architecture complete
Layers:
  Layer 1: XDP/eBPF — TCP state machine, SYN throttle, blacklist, ringbuf
  Layer 2: PoW Challenge — SHA-256 hashcash, dynamic difficulty, constant-time verify
  Layer 3: Rust Core — HMAC handshake, rate limit, death code (existing)
  Layer 4: Velocity — Physics check, CAPTCHA, protocol verification
  Layer 5: Paper — Heartbeat, auto-registration (existing)
  Layer 6: Traffic Intel — EWMA, 168h profiling, reputation, alerts

Infra: XDP→Prometheus metrics, ClickHouse + Grafana dashboard, Docker Compose
Testing: 100-IP DDoS simulation, MHDDoS ref analysis, load test report
Fixes: VarInt sign extension UB, pure ACK deadlock, RST/FIN cleanup
Ref: MHDDoS, Sonar, LimboFilter, AtomGuard, Infrarust, MC-XDP-eBPF, PowGo
2026-07-21 15:47:36 +02:00

9.5 KiB
Raw Blame History

DDoS — Векторы атак и защита

Актуально: v0.2+


Как трафик проходит защиту

Атакующий (ботнет)
    |
    v
┌──────────────────┐
│  XDP/eBPF        │  L3/L4: TCP state machine, SYN throttle,
│  (ядро)          │  IP blacklist, invalid TCP flags, UDP drop
│                  │  CPU < 30%, пропускная способность ~10M pps
└────────┬─────────┘
         v (чистый TCP, прошёл state machine)
┌──────────────────┐
│  PoW Challenge   │  SHA256 hashcash, dynamic difficulty
│  (Rust)          │  Анти-handshake-flood: CPU затраты на боте
└────────┬─────────┘
         v (валидный PoW)
┌──────────────────┐
│  Rust Core       │  L7: handshake parse, HMAC, rate limit,
│  (userspace)     │  death code auto-ban, blacklist
│                  │  CPU < 50%, пропускная способность ~85k conn/s
└────────┬─────────┘
         v (валидный MC handshake + HMAC)
┌──────────────────┐
│  Velocity        │  Domain whitelist, HMAC verify,
│  (Java)          │  Физика (falling + vehicle), CAPTCHA
│                  │  TPS-aware load balancer, circuit breaker
└────────┬─────────┘
         v (верифицированный игрок)
┌──────────────────┐
│  Game Server     │  Чистый трафик, без DDoS нагрузки
└──────────────────┘

Каждый слой отрабатывает и дропает до перехода к следующему. XDP — L3/L4, PoW — anti-handshake-flood, Rust — L7, Velocity — верификация.


L3/L4 атаки (объёмные)

Атака Механизм Защита Слой
UDP Flood Миллионы UDP пакетов MC = TCP, UDP дроп XDP
SYN Flood Миллионы TCP SYN без ACK SYN throttle per-IP + SYN cookies XDP + sysctl
ACK Flood Пакеты с ACK без SYN TCP state machine (ACK без SYN → вне state → дроп) XDP
ICMP Flood Ping flood icmp_echo_ignore_all=1 sysctl
Amplification DNS/NTP усиление Фильтрация у провайдера Upstream
Invalid flags SYN+FIN, SYN+RST, URG detect_tcp_bypass() XDP
IP Spoof Поддельный src IP uRPF + conntrack seq check XDP
Fragmented Разбитые TCP пакеты Дроп first fragment с MF XDP
RST flood Миллионы RST Игнорировать RST без matching state XDP
FIN flood Миллионы FIN FIN без matching state → дроп XDP

sysctl для L3/L4

# SYN flood
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2

# ICMP
net.ipv4.icmp_echo_ignore_all = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Буферы
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 1024 65535

L7 атаки (Minecraft-специфичные)

Handshake Flood

Боты коннектятся тысячами, шлют валидный handshake, дропают.

Детект: connections/sec с одного IP > threshold
Защита: PoW Challenge (Layer 2) + rate limit (Layer 3)
  PoW difficulty повышается при CPS > 50/100/500
  Rate limit: token bucket 5 conn/IP/sec, burst 10

Уязвимость других решений: Sonar/LimboFilter/AtomGuard
  не имеют PoW — handshake flood упирается только в
  rate limit, который обходится через ботнет

Bot Join Flood

Тысячи фейковых логинов с разных IP, каждый с разных IP.

Детект: CPS глобально > threshold (учитываем baseline 168h)
Защита: PoW (дорого для бота) + falling check (нужна MC физика)
  + verified DB (прошедшие не проверяются снова)

Уязвимость AtomGuard:
  SynFloodDetector.effectiveCPS = 0 при < 15 unique IP
  → атака 14 IP с 100 conn/s каждый = не детектится
Фикс: не обнулять, per-IP fallback

Ping Flood (Status Request)

Тысячи пакетов с next_state=1 (статус, не логин).

Детект: status requests/sec > threshold
Защита: отдельный rate limit для статуса (next_state=1)
  max 2 status/IP/10sec, burst 5

Slow Loris (MC вариант)

Открывают TCP, отправляют handshake по 1 байту — занимают слоты.

Детект: время на полный handshake > 5 сек
Защита: tokio::timeout на чтение handshake
  Rust: tokio::time::timeout(Duration::from_secs(5), read_handshake())

Fake Forge Flood

Бесконечный поток Forge handshake с мусорными mod list.

Детект: mod list > 500 модов или > 4096 байт
Защита: max_hostname = 4096, max_mods = 500, bounds check на VarInt

VarInt Overflow

Специально сформированные VarInt для integer overflow.

Детект: VarInt > 5 байт (по MC протоколу)
Защита: строгий bounds check, паника = DROP, не crash
  Result<T, Error>, не unwrap()

AI-боты (2026)

Что обходится

Решение Обход
Sonar gravity AI симулирует MC физику
Sonar vehicle AI шлёт правильные пакеты лодки
LimboFilter falling AI вычисляет parabola (0.98^t-1)*3.92
Map CAPTCHA (3-4 символа) OCR/ML (Sonar #531)
Timing check AI имитирует human distribution

Что работает

✓ PoW (SHA256)        вычислительная стоимость, GPU не асится
✓ HMAC                криптография, ключ на edge ноде
✓ Многослойность      6 слоёв вместо 1
✓ Dynamic difficulty  при атаке повышаем PoW до 12+
✓ ASN reputation      датацентры = повышенная строгость

Circuit Breaker

CLOSED (нормально)
  ↓ TPS < 12 или timeout > 3 сек → OPEN
OPEN (сервер выведен из ротации)
  ↓ через 30 сек → HALF_OPEN (пробный трафик)
HALF_OPEN
  ↓ успешно → CLOSED
  ↓ снова плохо → OPEN
pub enum CircuitState { Closed, Open(Instant), HalfOpen }

impl CircuitBreaker {
    pub fn should_route(&mut self, server: &ServerEntry) -> bool {
        match &self.state {
            CircuitState::Closed => {
                if server.tps < 12.0 { self.trip(); false }
                else { true }
            }
            CircuitState::Open(tripped_at) => {
                if tripped_at.elapsed() > Duration::from_secs(30) {
                    self.state = CircuitState::HalfOpen;
                    true
                } else { false }
            }
            CircuitState::HalfOpen => true,
        }
    }
}

ASN Reputation

pub enum AsnCategory {
    Residential,  // обычный провайдер → 1.0 rate limit
    Datacenter,   // Hetzner/AWS/OVH → 0.2 rate limit
    Mobile,       // мобильный NAT → 0.5 (но не блокировать!)
    Tor,          // Tor exit → 0.05
    Vpn,          // известный VPN → настраивается
    Unknown,      // новый IP → 0.5
}

fn rate_multiplier(cat: &AsnCategory) -> f64 {
    match cat {
        AsnCategory::Residential => 1.0,
        AsnCategory::Mobile      => 0.5,
        AsnCategory::Datacenter  => 0.2,
        AsnCategory::Vpn         => 0.3,
        AsnCategory::Tor         => 0.05,
        AsnCategory::Unknown     => 0.5,
    }
}

⚠️ Мобильные NAT: один IP = много игроков. Не блокировать, только снижать лимит.


Timing Analysis

pub struct TimingAnalyzer {
    response_times: Vec<f64>,   // ms
}

impl TimingAnalyzer {
    pub fn is_bot(&self, response_ms: f64) -> f64 {
        // Слишком быстро → скрипт
        if response_ms < 200.0 { return 0.9; }

        // Слишком ровно → паттерн
        if self.response_times.len() >= 5 {
            let variance = self.variance();
            if variance < 10.0 { return 0.85; }
        }

        // Нормальное распределение → человек
        0.1
    }

    fn variance(&self) -> f64 {
        let mean = self.response_times.iter().sum::<f64>()
            / self.response_times.len() as f64;
        self.response_times.iter()
            .map(|v| (v - mean).powi(2))
            .sum::<f64>() / self.response_times.len() as f64
    }
}