Skip to content

DDoS Protection

Antonios Voulvoulis edited this page Apr 14, 2026 · 1 revision

DDoS Protection

Type: Module Layer: L1 — Traffic pressure (L3/L4) Since: v1.80.x Config: conf.d/ddos/main.conf (.local override) Daemon dependency: NO — kernel-only enforcement


Purpose

See also: Glossary | Health Model | Architecture | Known Limitations

DDoS protection is a L3/L4 module that filters malformed packets, enforces per-IP connection limits, applies SYN rate limiting, and implements a graduated penalty ladder for repeat offenders. All enforcement is kernel-only via nftables rules — the daemon is not required for packet-level protection.


How It Works

DDoS protection operates through four helper chains in the nftables input pipeline. Each chain handles a distinct class of traffic pressure:

Chain Pipeline Phase Purpose
ddos_sanity Phase 1 (Hygiene) Drops malformed packets (Xmas, NULL, non-SYN new, fragmented)
ddos_penalty Phase 3 (Ban) Graduated penalty ladder for IPs in penalty sets
ddos_prefix Phase 5 (Detect) Per /24 prefix SYN and connection rate limiting (IPv4). Per /64 prefix in IPv6.
ddos_protection Phase 5 (Detect) Per-IP connection limits for SMTP, DNS, ICMP, UDP. Rate limiting.

Traffic flow

Packet arrives at input chain
    │
    ├── Phase 1: jump ddos_sanity
    │   Xmas/NULL/non-SYN-new/frag → DROP
    │   Clean → return to input chain
    │
    ├── Phase 3: jump ddos_penalty
    │   IP in ban set → DROP
    │   IP in rate-limit set + exceeds limit → DROP
    │   IP in rate-limit set + within limit → return
    │   IP not in penalty sets → return
    │
    ├── Phase 5: (after established/ICMP accept)
    │   SSH: > 15 concurrent per IP → DROP (input_ct_ssh_drop)
    │   HTTP: > 200 concurrent per IP → DROP (input_ct_http_drop)
    │   Mail: > 30 concurrent per IP → DROP (input_ct_mail_drop)
    │   SYN to service port: rate meter → ACCEPT or DROP (input_syn_rate_exceeded)
    │   jump ddos_prefix → /24 SYN + conn rate limiting
    │   jump ddos_protection → SMTP/DNS/ICMP/UDP flood protection
    │
    ▼
  Continue to service admission (Phase 6)

Kernel-only enforcement

Once rules are loaded, DDoS protection works without the daemon. If nftband stops, all rate limits, connection limits, and penalty rules continue enforcing. The daemon manages penalty set population and event-driven escalation. Schema generation and reload are handled by the CLI. Packet-level decisions are entirely kernel-side.

This is fundamentally different from BotGuard and LoginMon, which require the daemon for scoring and set population.


Kernel Objects

Chains (4 required when ENABLED)

Chain Rules (typical) Purpose
ddos_sanity 5 Malformed packet filtering
ddos_penalty 6 Graduated penalty ladder (ban/drop/rate-limit tiers)
ddos_prefix 4 Per-prefix SYN and connection flood protection
ddos_protection 8 Per-service connection limits and flood protection

Each chain must have > 0 rules. A chain with 0 rules is DEGRADED (B80-3).

Named Counters (enforcement evidence)

Counter Family Evidence Meaning
input_ct_ssh_drop ip + ip6 ENFORCEMENT SSH per-IP connection limit exceeded (> 15 concurrent)
input_ct_http_drop ip + ip6 ENFORCEMENT HTTP per-IP connection limit exceeded (> 200 concurrent)
input_ct_mail_drop ip + ip6 ENFORCEMENT Mail per-IP connection limit exceeded (> 30 concurrent)
input_syn_rate_exceeded ip + ip6 ENFORCEMENT SYN rate exceeded or non-service port SYN
input_syn_prefix_drop ip6 only ENFORCEMENT IPv6 /64 prefix SYN rate exceeded (anti-rotation)

Each counter maps to exactly one enforcement decision. Unlike the shared blacklist counter, DDoS counters are independently attributable.

Counter > 0 = ENFORCING (positive evidence). Counter = 0 = NEUTRAL (not failure).

Penalty Sets (managed by daemon when running)

Set Purpose
ddos_ban_1h IPs banned for 1 hour (repeat offenders)
ddos_drop_5m IPs dropped for 5 minutes
ddos_limit_5m IPs rate-limited for 5 minutes
ddos_limit_10s IPs rate-limited for 10 seconds

Penalty sets are timeout-based. The daemon populates them based on scoring. If the daemon stops, existing penalty entries continue enforcing until they expire (kernel-managed timeouts).

Meters (per-IP rate tracking)

Meter Purpose
syn_meter_v4 / syn_meter_v6 Per-IP SYN rate tracking for service ports
ddos_prefix_syn Per /24 prefix SYN rate
ddos_prefix_conn Per /24 prefix connection rate
ddos_dns_udp Per-IP DNS/UDP rate
ddos_icmp_flood Per-IP ICMP rate
ddos_udp_flood Per-IP UDP rate

Meters are nftables dynamic sets, not named counters. They do not provide direct evidence. Enforcement from meter-triggered rules must be inferred from the associated drop counters (e.g., input_syn_rate_exceeded).


Configuration

Key File Default Meaning
DDOS_ENABLED conf.d/ddos/main.conf "false" Master enable/disable
DDOS_MODE conf.d/ddos/main.conf "auto" Mode: auto, classic, hybrid

Override via main.conf.local (survives upgrades).

Connection limits (in nftables rules, not config)

Service Limit Counter
SSH (port 22) 15 concurrent per IP input_ct_ssh_drop
HTTP/HTTPS (ports 80, 443) 200 concurrent per IP input_ct_http_drop
Mail (ports 25, 465, 587) 30 concurrent per IP input_ct_mail_drop
SYN rate (service ports) 25/second burst 50 input_syn_rate_exceeded

These limits are set in the nftables schema at rebuild time. Changing them requires a schema rebuild (nftban firewall rebuild).


State Model (v1.81)

This module follows the 4-axis model:

  • Config: ENABLED / DISABLED
  • Structural: PRESENT / MISSING (4 chains exist + jump rules in input chain)
  • Runtime: Always PASS (kernel-only module — daemon not required for enforcement)
  • Effective: ENFORCING (any DDoS counter > 0) / IDLE (all counters = 0, NEUTRAL)

Truth table

Config Structural Runtime Effective System Contribution
DISABLED skip
DISABLED PRESENT (residual) PASS ENFORCING informational (kernel still enforcing)
ENABLED PRESENT (chains exist) PASS ENFORCING PROTECTED
ENABLED PRESENT (chains exist) PASS IDLE IDLE
ENABLED PRESENT (chains exist, rules = 0) PASS DEGRADED (empty chain, B80-3)
ENABLED MISSING PASS DEGRADED

Residual enforcement

When DDoS is DISABLED in config but chains persist in kernel from a prior enable, traffic is still being rate-limited and connection-limited. This is a valid operational state — config lags kernel reality. The validator reports this as informational, not DEGRADED.

To remove residual chains: nftban firewall rebuild (regenerates schema without DDoS chains when DISABLED).


CLI Commands

nftban ddos status          # Show DDoS module state
nftban ddos enable          # Enable DDoS protection
nftban ddos disable         # Disable DDoS protection
nftban ddos show            # Show current rules and counters

Verification (MANDATORY)

# Check if chains exist (structural)
nft list chain ip nftban ddos_sanity
nft list chain ip nftban ddos_protection
# Expected: chain with rules. Empty chain = DEGRADED.

# Check enforcement counters
nft list counter ip nftban input_ct_ssh_drop
nft list counter ip nftban input_ct_http_drop
nft list counter ip nftban input_syn_rate_exceeded
# Counter > 0 = ENFORCING. Counter = 0 = NEUTRAL (not failure).

# Check module state via validator
nftban-validate --json | jq '.modules.ddos'
# Expected: {"config":"enabled","structural":"present","effective":"enforcing"}
# or effective:"idle" if all counters are zero

# Verify chain has rules (not empty)
nft list chain ip nftban ddos_protection | grep -c "comment"
# Expected: > 0. Zero = DEGRADED (B80-3).

# Check residual state (when disabled)
nft list chains ip nftban | grep ddos
# If chains exist while DDOS_ENABLED="false" → valid residual

Failure Modes

DEGRADED: ENABLED but chains missing

Symptom: Validator reports structural: "missing" for DDoS. Cause: Module enabled in config but chains not loaded in kernel. Fix: nftban ddos enable or nftban firewall rebuild

DEGRADED: Chain exists but has 0 rules (B80-3)

Symptom: Finding VAL-CHAIN-004 for a DDoS chain. Cause: Failed enable or partial rebuild. Fix: nftban ddos enable

Counter at zero after long uptime

Symptom: All DDoS counters show 0 packets after days of uptime. Meaning: NEUTRAL — no traffic has exceeded the configured limits. This is valid behavior on a low-traffic or well-configured host. It does NOT mean the module is broken.

Counters reset after rebuild

All named counters reset to 0 after nftban firewall rebuild. Zero counters immediately after rebuild = NEUTRAL (expected), not evidence of failure. Anchor counters will begin incrementing as traffic flows; enforcement counters will increment when limits are exceeded.


Limitations

  • No per-chain packet counter. Individual chain traversal (how many packets entered ddos_sanity vs ddos_protection) is not measurable. Only the named enforcement counters are queryable.
  • Connection limits are in the schema, not config. Changing SSH/HTTP/Mail connection limits requires editing the schema generator and rebuilding. There is no runtime config variable for these limits.
  • Penalty set population requires daemon. While kernel enforcement of existing penalty entries works without the daemon, adding new IPs to penalty sets requires nftband to be RUNNING. If the daemon stops, no new penalty escalations occur.
  • SYN meter and DDoS counters overlap. input_syn_rate_exceeded fires both for legitimate SYN floods AND for SYN to non-service ports. It proves enforcement occurred, not that an attack was blocked.
  • IPv6 prefix counter is separate. input_syn_prefix_drop (IPv6 only) tracks /64 prefix SYN rate violations. This counter is checked by the validator alongside IPv4 counters.

Clone this wiki locally