-
Notifications
You must be signed in to change notification settings - Fork 0
DDoS Protection
Type: Module Layer: L1 — Traffic pressure (L3/L4) Since: v1.80.x Config:
conf.d/ddos/main.conf(.localoverride) Daemon dependency: NO — kernel-only enforcement
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.
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. |
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)
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.
| 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).
| 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).
| 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).
| 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).
| 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).
| 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).
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)
| 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 |
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).
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# 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 residualSymptom: 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
Symptom: Finding VAL-CHAIN-004 for a DDoS chain.
Cause: Failed enable or partial rebuild.
Fix: nftban ddos enable
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.
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.
-
No per-chain packet counter. Individual chain traversal (how many packets
entered
ddos_sanityvsddos_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
nftbandto be RUNNING. If the daemon stops, no new penalty escalations occur. -
SYN meter and DDoS counters overlap.
input_syn_rate_exceededfires 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.
NFTBan Wiki
Getting Started
Architecture
Modules
- BotGuard (HTTP L7)
- DDoS Protection (L3/L4)
- Portscan Detection
- Login Monitoring
- Blacklist & Threat Intelligence
- Suricata IDS Integration
- DNS Tunnel Suspicion
Operator Reference
- CLI Commands Reference
- Configuration Reference
- Systemd Units & Timers
- Optimization & Tuning
- Security Operations Guide
- GeoIP Database Guide
- FHS Compliance
- Troubleshooting: Smoke & Selftest
Verification & Trust
- Glossary & Vocabulary
- Known Limitations
- Metrics & Evidence Model
- Binary Verification (SLSA)
- Security Architecture
Reference
Legal