-
Notifications
You must be signed in to change notification settings - Fork 0
Login Monitoring
Type: Module Layer: L2 — Authentication brute-force detection Since: v1.36.0 (Go pipeline since v1.80.x) Config:
conf.d/login_alert.conf(.localoverride) Daemon dependency: YES — daemon required for all module operation
See also: Glossary | Health Model | Architecture | Known Limitations
LoginMon observes authentication log events via daemon file watchers for brute-force patterns, scores
source IPs based on failure frequency and behavior, and issues kernel-level
bans via the manual blacklist sets. Every step in the detection and response
chain requires the Go daemon (nftband). If the daemon stops, LoginMon
stops detecting, scoring, and banning entirely.
LoginMon operates entirely in the Go daemon with kernel enforcement:
Log files → daemon file watchers → parser pipeline → scorer → ban → kernel set
- File watchers bind to authentication log paths at daemon startup
-
Parsers extract failed login events from each log source:
- SSH (
/var/log/secureor/var/log/auth.log) - Dovecot (
/var/log/maillog) - Exim (
/var/log/exim/mainlog) - DirectAdmin (
/var/log/directadmin/login.log,security.log) - FTP (via distroconf resolution)
- SSH (
- Scorer aggregates per-IP failure counts in a time window
-
Ban decision: when score threshold is exceeded, the daemon adds the IP
to
blacklist_manual_ipv4/ipv6with a timeout -
Kernel enforcement: the manual blacklist set is checked at pipeline
phase 3 (Ban Enforcement) by the
input_blacklist_manual_droprule
LoginMon uses distroconf to resolve log paths per distribution:
| Source | Typical path | Resolver |
|---|---|---|
| SSH |
/var/log/secure (EL) or /var/log/auth.log (Debian) |
distroconf |
| Exim | /var/log/exim/mainlog |
distroconf |
| Dovecot | /var/log/maillog |
distroconf |
| DirectAdmin | /var/log/directadmin/login.log |
distroconf |
Path resolution is logged at daemon startup: [LOGINMON] <source>: <path> resolved_by=distroconf. A resolved_by=fallback or warning=hardcoded_probe
indicates degraded path resolution.
LoginMon does not have its own chains, sets, or counters. It produces bans that land in the shared manual blacklist infrastructure:
| Object | Owner | Purpose |
|---|---|---|
blacklist_manual_ipv4/ipv6 |
Base schema (always present) | Ban enforcement set |
input_blacklist_manual_drop |
Base schema (always present) | Drop counter for manual blacklist matches |
These objects are part of the base nftables schema — they exist regardless of whether LoginMon is enabled. LoginMon writes to them; it does not create them.
input_blacklist_manual_drop counts drops from ALL sources that write to the
manual blacklist:
- LoginMon auto-bans (auth brute-force detection)
- Operator manual bans (
nftban ban <ip>) - Portscan detector bans
Kernel counters confirm enforcement at the blacklist family level. Attribution
to LoginMon requires journal evidence ([EVENT] banned with source field).
The kernel counter alone cannot attribute drops to LoginMon specifically.
| Priority | Source | What it proves |
|---|---|---|
| 1 (primary) | Journal events ([EVENT] banned, [EVENT] login_failed) |
LoginMon-specific detection and enforcement |
| 2 (secondary) | Kernel set presence (blacklist_manual_ipv4 elements) |
Bans exist (shared, not attributable) |
| 3 (tertiary) | Kernel counter (input_blacklist_manual_drop > 0) |
Family enforcement occurring (non-attributable) |
| Key | File | Default | Meaning |
|---|---|---|---|
NFTBAN_LOGIN_ALERT_ENABLED |
conf.d/login_alert.conf |
"false" |
Master enable/disable |
NFTBAN_LOGIN_ALERT_SSH |
conf.d/login_alert.conf |
"true" |
Enable SSH detection |
Override via login_alert.conf.local (survives upgrades).
Per-source configuration controls which parsers activate. Sources are enabled individually (SSH, Dovecot, Exim, DirectAdmin, FTP).
This module follows the 4-axis model:
- Config: ENABLED / DISABLED
- Structural: PRESENT (inherited from base manual blacklist infrastructure; LoginMon has no dedicated kernel objects)
- Runtime: RUNNING / STOPPED / ERROR (daemon required for all module operation)
- Effective: ENFORCING / OBSERVING / IDLE (from journal evidence)
| Evidence | Effective state |
|---|---|
[EVENT] banned entries in daemon journal |
ENFORCING (ban issued) |
[EVENT] login_failed entries in journal |
OBSERVING (detecting failures, no ban yet) |
| No journal events in observation window | IDLE (NEUTRAL — no auth attacks, valid state) |
Validator limitation: The Go validator is a point-in-time snapshot tool.
Journal event queries for ENFORCING/OBSERVING evidence are outside the
current validator scope. The validator reports effective state as IDLE because
it does not evaluate journal evidence. This is a limitation of the validator,
not a statement about actual module activity. Real effective state is
observable via nftban login status and journal queries.
| Config | Structural | Runtime | Effective | System Contribution |
|---|---|---|---|---|
| DISABLED | — | — | — | skip |
| ENABLED | PRESENT | RUNNING | ENFORCING | PROTECTED |
| ENABLED | PRESENT | RUNNING | OBSERVING | PROTECTED |
| ENABLED | PRESENT | RUNNING | IDLE | IDLE |
| ENABLED | PRESENT | STOPPED | — | DEGRADED (daemon required) |
| ENABLED | PRESENT | RUNNING + binding failure | — | DEGRADED (consistency: source path missing) |
Note on binding failure: Source binding status is a consistency/integrity check, not a runtime axis value. The daemon is RUNNING but one or more log sources could not be resolved. This is a separate finding from daemon state.
When nftband stops:
-
Existing bans persist — entries in
blacklist_manual_ipv4/ipv6have kernel-managed timeouts and continue enforcing - No new bans possible — detection, scoring, and ban issuance halt entirely
- System is DEGRADED, not DOWN — kernel enforcement of existing bans continues
If a parser cannot find its log path:
-
resolved_by=fallback→ RUNTIME_INTEGRITY warning (using non-preferred path) -
resolved_by=hardcoded_probe→ RUNTIME_INTEGRITY warning (guessing) - Path entirely missing → source is MISSING (DEGRADED for that source)
nftban login status # Show module state, sources, event counts
nftban login enable # Enable login monitoring
nftban login disable # Disable login monitoring
nftban login restart # Restart the loginmon pipeline# Check if daemon is running (runtime axis — required)
systemctl is-active nftband
# Expected: "active". If not → LoginMon is non-functional.
# Check source bindings
journalctl -u nftband | grep "LOGINMON.*resolved_by" | tail -5
# Expected: "resolved_by=distroconf" for each source
# "resolved_by=fallback" = degraded path resolution
# Check for recent detection events
journalctl -u nftband --since "1 hour ago" | grep "login_failed" | tail -5
# Events present = OBSERVING. No events = NEUTRAL (not failure).
# Check for recent bans
journalctl -u nftband --since "1 hour ago" | grep "EVENT.*banned" | tail -5
# Events present = ENFORCING. No events = NEUTRAL.
# Check shared manual blacklist set
nft list set ip nftban blacklist_manual_ipv4
# Elements > 0 may include LoginMon bans + operator bans + portscan bans
# Attribution requires journal evidence, not kernel alone
# Check shared counter
nft list counter ip nftban input_blacklist_manual_drop
# Counter > 0 = manual-blacklist-family enforcement (not LoginMon-specific)
# Counter = 0 = NEUTRAL
# Check module state via validator
nftban-validate --json | jq '.modules.loginmon'
# Expected: {"config":"enabled","structural":"present","runtime":"running","effective":"idle"}
# Note: effective is "idle" by default — validator does not query journalSymptom: Validator reports runtime: "stopped".
Impact: No new auth failure detection or banning. Existing bans persist.
Fix: systemctl start nftband
Symptom: Journal shows resolved_by=fallback or source path missing.
Impact: Some auth sources not monitored. Attackers targeting unmonitored
services (e.g., Dovecot) will not be detected.
Fix: Verify log paths match distribution defaults. Check distroconf
resolution. May need manual path configuration.
1 login_failed event per hour on a low-traffic host is normal. This
represents OBSERVING (activity detected), not IDLE. IDLE means zero events
in the observation window.
After daemon restart, pipeline source counters reset to 0. This is a transient IDLE state, not DEGRADED. Events begin accumulating as new auth failures occur.
- No dedicated kernel objects. LoginMon structural presence is inferred from base schema sets (always present) + daemon runtime + source bindings. The validator cannot verify LoginMon-specific kernel structure independently.
-
Shared enforcement counter.
input_blacklist_manual_dropis shared with operator manual bans and portscan bans. LoginMon-specific enforcement proof requires daemon journal evidence ([EVENT] bannedwith source attribution). -
Validator effective state is always IDLE. The validator uses point-in-time
kernel snapshots. Journal queries for login events are outside its current
scope. Real effective state is visible via
nftban login statusand journal queries. -
Daemon required for everything. Unlike DDoS (kernel-only) or Portscan
(kernel logging without daemon), LoginMon cannot function at all without
nftband. File watchers, parsing, scoring, and ban issuance all require the daemon. - Log path dependency. If the authentication log file is rotated, moved, or its format changes, LoginMon loses visibility. The distroconf resolver handles standard distributions, but custom log configurations may require manual path setup.
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