-
Notifications
You must be signed in to change notification settings - Fork 0
Blacklist and Threat Intelligence
Type: Module (composite) Domain: IP reputation and policy enforcement (manual, feeds, geographic policy) Since: v1.80.x Config: multiple (see per-source configuration below) Daemon dependency: Source-dependent — manual blacklist enforcement is kernel-self-contained; feed and geoban updates require the daemon Last verified: v1.218.7
See also: Glossary | Health Model | Architecture | Known Limitations
Blacklist & Threat Intelligence is a composite module covering three distinct IP enforcement sources that converge on shared kernel sets:
| Source | Origin | Enforcement path |
|---|---|---|
| Manual | Operator nftban ban commands + auto-detect (LoginMon, portscan) |
Dedicated set + dedicated counter |
| Feeds | External threat intelligence (e.g., TOR exit nodes) | Shared set + shared counter |
| GeoBan | Country/region blocking policy via GeoIP database | Shared set + shared counter |
Each source has its own lifecycle, freshness model, and configuration. They share kernel enforcement surfaces but have different evidence models.
| Set | Type | Shared by | Purpose |
|---|---|---|---|
blacklist_ipv4 / blacklist_ipv6
|
interval, timeout, auto-merge | Feeds + GeoBan | CIDR range enforcement |
blacklist_manual_ipv4 / blacklist_manual_ipv6
|
hash, timeout | Manual bans + LoginMon + Portscan | Per-IP ban enforcement |
Critical distinction: Feeds and GeoBan share blacklist_ipv4/ipv6.
Manual bans use a separate blacklist_manual_ipv4/ipv6. This means:
- Manual-blacklist-family enforcement is independently provable (separate counter). Attribution to operator bans, LoginMon, or portscan is not.
- Feed vs GeoBan enforcement is NOT independently provable (shared counter)
| Priority | Source | What it proves |
|---|---|---|
| 1 (strongest) | Manual blacklist set + manual counter | Manual-family enforcement (shared across sources) |
| 2 | Feed/GeoBan shared set presence | Data availability (ranges loaded) |
| 3 | Feed/GeoBan shared counter > 0 | Family-level enforcement (non-attributable) |
| 4 (weakest) | Sync logs / GeoIP file freshness | Pipeline provenance (data pipeline state) |
| Counter | Shared by | What it proves |
|---|---|---|
input_blacklist_drop |
Feeds + GeoBan | Blacklist-family enforcement (not attributable to feeds OR geoban) |
input_blacklist_manual_drop |
Manual + LoginMon + Portscan | Manual-blacklist-family enforcement (not attributable to specific source) |
| Set type | Characteristic | Used by |
|---|---|---|
| Interval (auto-merge) | Stores CIDR ranges, kernel merges overlapping ranges |
blacklist_ipv4/ipv6 (feeds + geoban) |
| Hash (timeout) | Stores individual IPs with per-entry expiry |
blacklist_manual_ipv4/ipv6 (manual bans) |
Interval sets are efficient for large IP range lists (thousands of CIDRs). Hash sets are efficient for individual IP lookups with timeout-based expiry.
blacklist.d/*.conf entries (operator hand-edited bans and persisted
permanent/persistent-offender bans) are routed by entry shape — symmetric for
IPv4 and IPv6:
blacklist.d entry |
Routes to | Set type |
|---|---|---|
Single IP (e.g. 203.0.113.88) |
blacklist_manual_ipv4 / blacklist_manual_ipv6
|
hash |
CIDR / range (e.g. 203.0.113.0/24) |
blacklist_ipv4 / blacklist_ipv6
|
interval |
Why single IPs go to the hash set (fail-open fix). Before v1.203.0,
blacklist.d single-IP entries were merged into the feed/geoban-owned interval
set, which is then atomically flushed-and-replaced on every feed/geoban load —
so a file-backed ban stopped being enforced once feeds loaded
(BUG-BLACKLIST-FILE-ENTRY-FAIL-OPEN-ON-FEED-RELOAD). As of v1.203.0 single IPs
are added to the manual hash sets, which the feed/geoban replace never touches,
so a blacklist.d single-IP ban survives a feed reload.
Unified replace (no source clobber). As of v1.203.0 the interval sets
blacklist_ipv4/_ipv6 are populated by a single atomic flush-and-replace whose
input is the union of feed CIDRs + geoban CIDRs + blacklist.d CIDR/range
entries. Because all interval sources go through one replace, they coexist and
no source overwrites another (earlier, sequential per-source replaces could
let a later source wipe an earlier one's ranges). Enforcement counters are
unchanged — no new per-source counters were added (see Shared Counter
Attribution Rules).
Removal:
nftban unban <ip>remains the canonical removal path — it clears the entry fromblacklist.d/*.confand from the live kernel set(s). Deleting a single-IP line fromblacklist.dby hand and re-syncing does not by itself remove a live hash-set entry; usenftban unban.
Operator issues nftban ban <ip> or an auto-detect module (LoginMon,
portscan) adds an IP. The daemon writes the IP to blacklist_manual_ipv4/ipv6
with a timeout. The kernel drops matching traffic at pipeline Phase 2 (BAN).
Manual blacklist infrastructure is always present in the base schema. There is no config switch that removes the manual blacklist sets from the firewall design.
| Set exists | Elements > 0 | Drops > 0 | State |
|---|---|---|---|
| YES | YES | YES | ENFORCING |
| YES | YES | NO | PRIMED (entries loaded, awaiting matching traffic) |
| YES | NO | — | IDLE |
| NO | — | — | DEGRADED (base schema violation) |
input_blacklist_manual_drop is the enforcement counter. It proves
manual-blacklist-family enforcement but cannot attribute drops to a specific
source (operator ban vs LoginMon vs portscan).
Feed configuration files in conf.d/feeds/ define external IP lists (e.g.,
TOR exit nodes, known botnets). The daemon periodically syncs feed data,
converts IP lists to CIDR ranges, and loads them into blacklist_ipv4/ipv6.
Sync is logged (as of v1.203.0, feed CIDRs are queued into the unified interval
replace — see "File-backed bans and unified replace"):
[SYNC] Feeds IPv4: 1270 input CIDRs queued for unified replace, followed by
[SYNC] Unified blacklist_ipv4 replace: N input CIDRs (feeds+geoban+blacklist.d).
| Key | File | Meaning |
|---|---|---|
| Feed config files | conf.d/feeds/*.conf |
Each .conf file defines one feed source |
Feeds are configured by file presence. No master enable/disable switch exists —
if feed config files exist, feeds are configured. If the conf.d/feeds/
directory is empty or absent, feeds are DISABLED.
| Feeds configured | Sync status | State |
|---|---|---|
| YES | Sync success, data loaded | LOADED |
| YES | Sync failed | DEGRADED |
| YES | Data stale (feed file mtime > TTL) | STALE |
| NO | — | DISABLED |
These are feed pipeline states, not direct enforcement states. LOADED means data is available in the shared blacklist set. Actual enforcement depends on matching traffic and is only provable at the shared blacklist-family level.
LOADED is NOT ENFORCING. Feed sync loading ranges into the kernel set is a
data pipeline operation. Whether those ranges match any traffic is determined
by the kernel. The shared input_blacklist_drop counter proves blacklist-family
enforcement but cannot attribute it to feeds specifically.
Feed files are stored in /var/lib/nftban/feeds/. File mtime indicates last
successful sync. Stale feed data (mtime exceeding the feed's configured TTL)
is flagged as STALE — the data may still be correct but is not verified against
the current upstream source.
GeoBan blocks traffic from entire countries based on a GeoIP database. The
daemon reads the DB-IP Country Lite database (or MaxMind GeoLite2), resolves
configured country codes to IP ranges, and loads them into blacklist_ipv4/ipv6
alongside feed data. As of v1.203.0 geoban ranges are contributed to the single
unified interval replace (with feeds and blacklist.d CIDRs), so geoban and feed
ranges coexist without overwriting each other.
| Key | File | Default | Meaning |
|---|---|---|---|
GEOBAN_ENABLED |
conf.d/geoban/main.conf |
"true" |
Master enable/disable. Enabled by default, but ships with GEOBAN_DEFAULT_POLICY="allow" (block only listed countries). With no countries configured, GeoBan is enabled-but-IDLE and blocks nothing; add countries to activate blocking. |
Country selection is configured in the geoban config files. geoban: 0/0 in
sync output means geoban is enabled but no countries are configured for
blocking. GeoBan enabled with zero configured countries is valid IDLE for
policy scope, not a failure.
| Config | Database | State |
|---|---|---|
| ENABLED | Present + fresh | LOADED |
| ENABLED | Missing or empty | STALE (finding VAL-GEOBAN-001 emitted) |
| ENABLED | Stale (> 45 days) | STALE |
| DISABLED | — | DISABLED |
| Property | Value |
|---|---|
| Default database | DB-IP Country Lite (dbip-country-lite.mmdb) |
| Location | /var/lib/nftban/geoip/dbip-country-lite.mmdb |
| Update frequency | Monthly (auto-sync via nftban-core-geoip.timer) |
| Freshness threshold | 45 days (older = STALE) |
The GeoIP database is a data dependency for GeoBan, not an enforcement object. Its presence proves data availability, not enforcement.
This is the most important evidence constraint for the blacklist module.
| Claim | Counter | Valid? |
|---|---|---|
| "Blacklist-family enforcement is happening" |
input_blacklist_drop > 0 |
YES |
| "Manual-blacklist-family enforcement is happening" |
input_blacklist_manual_drop > 0 |
YES |
| Claim | Why invalid |
|---|---|
| "Feed enforcement is happening" |
input_blacklist_drop is shared with GeoBan — cannot attribute |
| "GeoBan enforcement is happening" | Same shared counter — cannot attribute |
| "LoginMon enforcement is happening" (from counter alone) |
input_blacklist_manual_drop is shared with operator bans + portscan |
Per-source attribution requires daemon-side provenance: ban log CLASS field
(BLC-1/BLC-2 from v1.80.x) or journal source= metadata.
nftban ban <ip> [duration] # Add manual ban
nftban unban <ip> # Remove manual ban
nftban blacklist list # List manual blacklist entries
nftban feeds list # List configured feeds
nftban feeds update # Update feeds (all or a specific feed)
nftban geoban status # Show GeoBan state
nftban geoban update # Resolve countries and load geoban ranges
nftban geoip status # Show GeoIP database state# Check manual blacklist set (structural)
nft list set ip nftban blacklist_manual_ipv4
# Elements > 0 = PRIMED or ENFORCING (depends on counter)
# Elements = 0 = IDLE
# Check manual blacklist counter (enforcement)
nft list counter ip nftban input_blacklist_manual_drop
# Counter > 0 = manual-blacklist-family ENFORCING
# Counter = 0 = NEUTRAL
# Check feed/geoban shared set
nft list set ip nftban blacklist_ipv4
# Shows interval set with CIDR ranges from feeds + geoban
# Element count = combined feeds + geoban ranges (not separable)
# Check feed/geoban shared counter
nft list counter ip nftban input_blacklist_drop
# Counter > 0 = blacklist-family ENFORCING (not attributable)
# Counter = 0 = NEUTRAL
# Check feed sync status (as of v1.203.0)
journalctl -u nftband | grep -E "SYNC.*(Feeds|Geoban|Unified)" | tail -5
# Expected: "[SYNC] Feeds IPv4: N input CIDRs queued for unified replace"
# "[SYNC] Unified blacklist_ipv4 replace: N input CIDRs (feeds+geoban+blacklist.d)"
# Check GeoIP database
ls -la /var/lib/nftban/geoip/dbip-country-lite.mmdb
# File exists + size > 0 + mtime < 45 days = fresh
# Missing or empty = STALE (VAL-GEOBAN-001)
# Check module states via validator
nftban validate --json | jq '.modules.blacklist'
# Shows blacklist-related state as exposed by the current validator schema
# Confirm manual/feeds/geoban sub-state fields against live outputSymptom: Validator reports blacklist manual state as degraded.
Cause: Base schema violation — the manual blacklist set should always exist.
Fix: nftban firewall rebuild
Symptom: Feeds state reported as degraded. Journal shows sync errors.
Cause: Network issue, upstream feed unavailable, or daemon not running.
Fix: nftban feeds update (manual retry). Check network connectivity.
Symptom: Finding VAL-GEOBAN-001. GeoBan state = STALE.
Cause: Database file missing, empty, or older than 45 days.
Fix: nftban geoban update or nftban geoip update
Symptom: input_blacklist_drop = 0 despite feeds loaded with 847 ranges.
Meaning: NEUTRAL — no inbound traffic matched any feed range. This is
valid on hosts that don't receive traffic from blacklisted sources. It does
NOT mean feeds are broken.
All counters reset to 0 after nftban firewall rebuild. Zero counters
immediately after rebuild = NEUTRAL (expected). Feed data is reloaded into
the set as part of the rebuild process.
-
Shared set for feeds + geoban (+
blacklist.dCIDRs).blacklist_ipv4/ipv6contains ranges from all interval sources. As of v1.203.0 they are written by a single unified replace, so sources coexist (no clobber), but the kernel set alone cannot determine whether a given entry came from feeds, geoban, orblacklist.d. Daemon sync logs provide provenance. -
Shared counter for feeds + geoban.
input_blacklist_dropcannot attribute enforcement to feeds or geoban individually. -
Shared counter for manual sources.
input_blacklist_manual_dropcannot attribute enforcement to LoginMon, portscan, or operator bans individually. - Feed data is not versioned. No mechanism to detect whether a feed upstream changed its content between syncs. Freshness is tracked by file mtime only.
-
Interval set auto-merge. The kernel merges overlapping CIDR ranges for
efficiency. This means the element count in
blacklist_ipv4may differ from the number of input CIDRs reported by the sync log. This is correct kernel behavior, not a data loss issue. - GeoBan depends on external database. DB-IP Country Lite updates monthly. If the update timer fails or the upstream is unavailable, the database becomes stale. The database may still be usable (country-to-IP mapping changes slowly) but is not guaranteed accurate.
NFTBan Wiki
Getting Started
Architecture
- Architecture Overview
- Firewall Anchor Architecture
- NFT Schema & Validator Model
- Health & Validation
- Metrics & Evidence Model
- Watchdog & Resource Profiles
- Security Architecture
Protection & Monitoring
- Protection & Monitoring Modules
- BotGuard — HTTP Guard
- BotScan — HTTP Exploit Scanner
- DDoS Protection
- Portscan Detection
- Login Monitoring
- Blacklist & Threat Feeds
- Suricata IDS Integration
- RBL Monitoring
- DNS Tunnel Detection
Operator Reference
- CLI Commands Reference
- Configuration Reference
- Systemd Units & Timers
- Optimization & Tuning
- Security Operations Guide
- GeoIP Database Guide
- FHS Compliance
- Troubleshooting & Selftest
Operations, Communications & Reporting
- Maintenance & Scheduled Operations
- Logging, Rotation & Retention
- Communications & Notifications
- Notification & Report Templates
- Audit Reports & Compliance
Verification & Trust
Reference
Legal