-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture Overview
Type: Architecture Scope: System structure, component wiring, and truth authority Since: v1.80.x Terminology: Glossary & Vocabulary
This page describes what exists and how it is wired. It covers the structural components of NFTBan, their relationships, and the authority model that determines which component is trusted for what.
For how health is derived from this structure, see the Health Model. For term definitions, see the Glossary.
NFTBan is a hybrid Go + Shell system with four distinct layers:
┌────────────────────────────────────────────────────────┐
│ KERNEL LAYER (nftables) AUTHORITY │
│ tables, chains, sets, counters, rules │
│ packet acceptance and drop decisions are enforced here │
└────────────────────────────┬───────────────────────────┘
│ reads (nft -j list ruleset)
┌────────────────────────────┴───────────────────────────┐
│ GO LAYER (nftband daemon + nftban-validate binary) │
│ daemon: ban/unban execution, loginmon, IPC, scoring │
│ validator: kernel state verification, health output │
└────────────────────────────┬───────────────────────────┘
│ reads validator JSON
┌────────────────────────────┴───────────────────────────┐
│ SHELL LAYER (nftban CLI) │
│ schema generation, configuration, operator interface │
│ presentation only — does NOT compute truth │
└────────────────────────────┬───────────────────────────┘
│ reads config files
┌────────────────────────────┴───────────────────────────┐
│ CONFIG LAYER (/etc/nftban/) │
│ operator intent — what SHOULD happen │
│ does NOT represent what IS happening │
└────────────────────────────────────────────────────────┘
| Layer | What it does | What it does NOT do |
|---|---|---|
| Kernel | Enforces packet decisions. Holds sets, counters, chains. | Does not interpret or report. |
Go daemon (nftband) |
Writes to kernel (ban/unban, set population). Runs loginmon pipeline, BotGuard scoring. | Does not own kernel truth — it writes, kernel holds. |
Go validator (nftban-validate) |
Reads kernel state. Derives health. Emits JSON. Zero side effects. | Does not modify kernel, config, or runtime. |
Shell CLI (nftban) |
Operator interface. Schema generation. Config management. | Does not compute health truth independently (INV-CONS-001). For truth-critical commands (status, health), it renders validator output only. |
Config (/etc/nftban/) |
Stores operator intent. .local overrides base. |
Does not represent runtime state. |
NFTBan uses a strict truth hierarchy. When sources disagree, higher priority wins.
| Priority | Source | Role |
|---|---|---|
| 1 (highest) |
Kernel (nft list ruleset) |
What is actually enforcing right now |
| 2 |
Validator (nftban-validate --json) |
Derives health state from kernel data. Authoritative for health interpretation, not for underlying truth. |
| 3 |
CLI output (nftban status, nftban health truth) |
Renders validator output. Never computes independently. |
| 4 (lowest) |
Config files (/etc/nftban/conf.d/) |
Operator intent. May disagree with kernel (see residual state). |
Invariant INV-CONS-001: CLI output must agree with validator output. If the validator says DEGRADED, the CLI must say DEGRADED. No softening, no reinterpretation, no cosmetic overrides.
These rules hold at all times and cannot be overridden by userspace components:
-
Kernel state is the single source of truth. What
nft list rulesetreturns is what is actually enforcing. No daemon, CLI, or config file can change this fact — they can only change kernel state through nft commands. - All packet decisions are enforced in kernel. Accept, drop, and bypass verdicts happen in nftables. Userspace cannot intercept or override a kernel packet decision after it is made.
- Userspace writes, kernel holds. The daemon and CLI can add/remove set elements and reload the schema, but once loaded, the kernel owns the state. If the daemon crashes, kernel state persists.
- Set timeouts are kernel-managed. Timeout-based set entries (bans, BotGuard classifications) expire in kernel regardless of daemon state.
NFTBan guarantees the following invariants when the system is healthy:
-
Kernel contains nftables schema:
- Tables:
ip nftban,ip6 nftban - Base chains: input (drop policy), forward, output
- Required sets: whitelist, blacklist (both types), service ports
- Tables:
-
Input chain follows the 7-anchor pipeline: HYGIENE → TRUSTED → BAN → ESTABLISHED → DETECT → SERVICE → FINAL
-
Validator agrees with kernel:
nftban-validatereports PROTECTED or IDLE when structure is correct -
CLI reflects validator truth:
nftban healthoutput matchesnftban-validateoutput (INV-CONS-001) -
Config expresses intent, kernel expresses reality: ENABLED in config + MISSING in kernel = DEGRADED (VAL-CONS-001)
If any invariant is violated, the system state is DEGRADED or DOWN.
NFTBan exposes three metric classes that serve different purposes:
| Class | Source | What it proves | Examples |
|---|---|---|---|
| Structural | Validator | System integrity (objects exist, ordered correctly) | Tables, chains, anchors, sets present |
| Enforcement | Kernel counters | Real protection activity (packets processed) |
input_blacklist_drop, input_syn_rate_exceeded
|
| Operational | Timers / services | Data freshness and system maintenance | Feed sync, GeoIP updates, exporter runs |
- Counters prove enforcement happened
- Validator proves structure is correct
- Metrics exporter exposes both for monitoring systems (Prometheus, JSON)
For the full counter catalog, see Metrics & Evidence Model. For evidence interpretation rules, see Glossary.
NFTBan uses two nftables tables, one per address family:
| Table | Family | Purpose |
|---|---|---|
ip nftban |
IPv4 | All IPv4 enforcement |
ip6 nftban |
IPv6 | All IPv6 enforcement (mirrors IPv4 structure) |
Both tables are required on dual-stack hosts. On IPv4-only hosts, only the
ip nftban table is required.
Each table contains:
- Base chains with hooks (input, forward, output)
- Helper chains for module-specific logic (ddos_sanity, http_bot_guard, etc.)
- Named sets for IP lists (whitelist, blacklist, BotGuard classification sets)
- Named counters for enforcement evidence
- Meters for per-IP rate tracking
The input chain follows a fixed 7-phase pipeline. Every inbound packet traverses these phases in order. Anchor counters mark phase boundaries.
Packet arrives
│
▼
ANCHOR_HYGIENE ─── Phase 1: Sanity
│ Invalid state → DROP (input_invalid_drop)
│ ddos_sanity chain → malformed packet filtering
│
▼
ANCHOR_TRUSTED ─── Phase 2: Trust
│ Loopback → ACCEPT
│ Whitelist → ACCEPT (input_whitelist_accept)
│
▼
ANCHOR_BAN ──────── Phase 3: Ban Enforcement
│ Manual blacklist → DROP (input_blacklist_manual_drop)
│ Feed/geoban blacklist → DROP (input_blacklist_drop)
│ Per-IP port access → ACCEPT
│ ddos_penalty chain → repeat offender ladder
│
▼
ANCHOR_ESTABLISHED Phase 4: Connection Tracking
│ Established/related → ACCEPT (input_established_accept)
│ ICMP essential → ACCEPT
│
▼
ANCHOR_DETECT ───── Phase 5: Detection
│ Portscan detection chain → LOG
│ SSH connection limit → DROP (input_ct_ssh_drop)
│ HTTP connection limit → DROP (input_ct_http_drop)
│ Mail connection limit → DROP (input_ct_mail_drop)
│ SYN rate meter → ACCEPT (rate OK) or DROP (exceeded)
│ ddos_prefix chain → IPv6 prefix aggregation
│ ddos_protection chain → classic DDoS protection
│
▼
ANCHOR_SERVICE ──── Phase 6: Service Admission
│ TCP service ports → ACCEPT (input_service_tcp_accept)
│ UDP service ports → ACCEPT (input_service_udp_accept)
│
▼
ANCHOR_FINAL ────── Phase 7: Default Drop
│ Everything else → DROP (policy drop)
│
▼
Packet dropped (default policy)
- ANCHOR_FINAL must always be last. If missing, the pipeline is incomplete → DOWN.
- Anchors must be in correct order. Misordering means packets skip phases.
-
Each anchor is a named counter.
anchor_hygiene > 0proves traffic entered the pipeline. -
Flow gaps between anchors indicate shadowing. If
anchor_hygiene > 0butanchor_detect = 0, something between phases 2-4 is intercepting all traffic (may be correct under high established-traffic ratio).
| Phase | Modules active | Enforcement type |
|---|---|---|
| Hygiene (1) | DDoS sanity | Kernel-only |
| Trust (2) | Whitelist | Kernel-only |
| Ban (3) | Blacklist (manual + feeds + geoban), DDoS penalty | Kernel-only |
| Established (4) | Connection tracking | Kernel-only |
| Detect (5) | Portscan, DDoS (connection limits + SYN rate + prefix + classic) | Kernel-only |
| Service (6) | Service admission | Kernel-only |
| Final (7) | Default drop policy | Kernel-only |
BotGuard operates via a helper chain (http_bot_guard) jumped to from the
detect phase for TCP ports 80/443. It is present only when BotGuard is ENABLED.
LoginMon does not have a kernel-side phase. It operates entirely in the Go
daemon, writing bans to the blacklist_manual_ipv4/ipv6 sets which are
enforced at phase 3 (Ban Enforcement).
Portscan detection chain logs traffic for analysis but has no dedicated kernel counter. Its effective state is reported as IDLE due to lack of observable evidence — the chain may be actively logging, but without a counter there is no kernel evidence to prove enforcement.
Helper chains implement module-specific logic. They are jumped to from the input chain and return after processing.
| Chain | Module | Required when |
|---|---|---|
ddos_sanity |
DDoS | Module ENABLED (or residual) |
ddos_penalty |
DDoS | Module ENABLED (or residual) |
ddos_prefix |
DDoS | Module ENABLED (or residual) |
ddos_protection |
DDoS | Module ENABLED (or residual) |
portscan_detection |
Portscan | Module ENABLED (or residual) |
http_bot_guard |
BotGuard | Module ENABLED only |
Helper chains are module-scoped: they are required only when their module is ENABLED. Their absence when the module is DISABLED is the correct structural state, not a failure.
A helper chain that exists but has zero rules is DEGRADED (B80-3). The chain is present but non-functional.
Sets hold IP addresses, port numbers, and enforcement state.
| Set | Type | Purpose |
|---|---|---|
whitelist_ipv4/ipv6 |
hash | Trusted IPs — bypass all restrictions |
blacklist_ipv4/ipv6 |
interval, timeout | Feed + geoban enforcement |
blacklist_manual_ipv4/ipv6 |
timeout | Manual bans + LoginMon bans |
tcp_ports_in / udp_ports_in
|
hash | Allowed service ports |
port_allow_tcp_ipv4 / port_allow_udp_ipv4
|
hash (concat) | Per-IP port access |
| Set | Module | Purpose |
|---|---|---|
http_bot_suspect/6 |
BotGuard | IPs under classification |
http_bot_pending/6 |
BotGuard | IPs awaiting verification |
http_bot_allow/6 |
BotGuard | Verified non-bots |
http_bot_grey/6 |
BotGuard | Throttled IPs |
http_bot_ban/6 |
BotGuard | Banned bots |
http_bot_emergency/6 |
BotGuard | Emergency blocks |
syn_meter_v4/v6 |
DDoS | Per-IP SYN rate tracking |
Every counter has one defined meaning. Counters are the primary enforcement evidence used by the validator.
| Category | Examples | Evidence class |
|---|---|---|
| Enforcement drops |
input_blacklist_drop, input_ct_ssh_drop, input_syn_rate_exceeded
|
ENFORCEMENT |
| Enforcement accepts | input_whitelist_accept |
ENFORCEMENT (bypass) |
| Observational |
input_established_accept, input_service_tcp_accept
|
OBSERVATIONAL |
| Pipeline anchors |
anchor_hygiene through anchor_final
|
STRUCTURAL (existence) + OBSERVATIONAL (value) |
| Aggregates |
total_input_accept, total_input_drop
|
DERIVED (not for module claims) |
For the full counter catalog, see the Metrics & Evidence Model.
The Go daemon is a long-running service that writes to kernel sets and provides runtime services.
| Component | Purpose |
|---|---|
| Ban/unban executor | Adds/removes IPs from blacklist sets |
| LoginMon pipeline | Parses auth logs, scores IPs, issues bans |
| BotGuard module | HTTP bot classification, set population |
| IPC socket | Communication between CLI and daemon |
| Watchdog integration | System pressure monitoring |
The daemon is a writer, not an authority. It writes to kernel sets, but kernel state (what the sets actually contain) is the authority. If the daemon crashes, existing kernel bans persist (timeout sets are kernel-managed).
Daemon dependency varies by module:
- BotGuard and LoginMon require the daemon for new protection actions
- DDoS and Portscan enforce entirely in kernel without the daemon
The validator is a read-only binary that derives health from kernel state.
| Property | Value |
|---|---|
| Binary | /usr/lib/nftban/bin/nftban-validate |
| Execution | ~1ms |
| Side effects | None (pure read-only) |
| Input |
nft -j list ruleset + systemctl is-active + config files |
| Output | Frozen JSON schema (M81-6) |
The validator implements the Health Model — the 4-axis evaluation that produces PROTECTED / IDLE / DEGRADED / DOWN.
The CLI is the operator interface. It handles:
- Schema generation (
nftban firewall rebuild) - Configuration management (
nftban config) - Module enable/disable (
nftban ddos enable) - Status display (
nftban status,nftban health truth)
The CLI is a presentation layer. For truth-critical commands (status,
health), it reads the Go validator's JSON output and renders it. It does
not independently query kernel state for health derivation.
Configuration expresses operator intent.
| Path | Purpose |
|---|---|
/etc/nftban/main.conf |
Master config |
/etc/nftban/conf.d/{module}/main.conf |
Per-module base config |
/etc/nftban/conf.d/{module}/main.conf.local |
Per-module override (survives upgrades) |
/etc/nftban/whitelist.d/ |
Whitelist IP files |
/etc/nftban/blacklist.d/ |
Blacklist IP files |
Config load order: .local overrides base. If both exist, .local wins.
Config is intent, not truth. A module can be ENABLED in config but MISSING in kernel (consistency mismatch → DEGRADED). A module can be DISABLED in config but PRESENT in kernel (valid residual state).
The nftables schema (all chains, sets, rules) is generated by the shell CLI and loaded atomically:
nftban firewall rebuild
│
├── 1. Generate schema in temp namespace
├── 2. Validate generated schema (Go validator)
├── 3. If valid → atomic flush + load
└── 4. If invalid → abort, keep existing ruleset
The schema is a logical contract that defines required kernel objects.
It is not tied to a specific implementation language. Runtime authority
resides in the kernel — what nft list ruleset returns is the actual enforced
state. Current implementation: schema generated via shell (nft_schema.sh),
migrating to Go in 1.90.x. The contract remains identical regardless of
implementation. The Go validator reads the generated
schema via schema_generated.go (codegen from nft_schema.sh).
Rebuild invariant: If rebuild validation fails, the existing ruleset is preserved. No partial state. This was hardened in v1.70.0 (rebuild failure is FATAL — no fallback reload).
# Verify table structure
nft list tables
# Expected: "table ip nftban" and "table ip6 nftban"
# Verify anchor pipeline order
nft list chain ip nftban input | grep ANCHOR
# Expected: HYGIENE → TRUSTED → BAN → ESTABLISHED → DETECT → SERVICE → FINAL
# Verify helper chains exist
nft list chains ip nftban | grep chain
# Shows all base + helper chains
# Verify validator reads kernel correctly
nftban-validate --json | jq '.status'
# Expected: "protected", "idle", "degraded", or "down"
# Verify CLI agrees with validator (INV-CONS-001)
nftban health truth
# Must show same status as validator JSON-
Single table per family. NFTBan uses
ip nftbanandip6 nftban. It does not use theinetfamily (which would combine both). This is by design — separate tables allow per-family validation and independent set types. -
No runtime rule modification. The daemon writes to sets, not to rules.
Rule changes require a full schema rebuild (
nftban firewall rebuild). -
Schema generation is currently shell-based. The schema contract is
language-agnostic. Current implementation uses
nft_schema.sh. Migration to Go is part of the 1.90.x direction. The contract is unchanged by this. - No cluster mode. NFTBan operates on a single host. There is no distributed state synchronization between hosts.
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