Security / Threat model

Threat model.
Controls and their limits.

fail2zig parses attacker-influenced logs and changes kernel firewall state. This STRIDE model documents the threats, the controls in place, and the risks those controls leave behind.

Scope

The system and its assumptions.

The enforcing daemon needs firewall privileges. An all-log-only configuration can run unprivileged. In either mode, the daemon relies on trusted host components and service records.

What we trust

A.01Trusted kernel
The Linux kernel and selected host tools are trusted; kernel compromise is outside this model.
A.02Protected installation
Operators protect the binary, configuration, and filesystem from unauthorized replacement; the daemon does not re-verify its binary.
A.03Authoritative service logs
The recorded source IP drives decisions. fail2zig cannot distinguish a forged service log from a truthful one.
A.04Trusted administrators
Root operators can disable or misconfigure protection; their deliberate actions are outside the adversary model.

Who we model

AD-01Remote attacker
Sends crafted requests or floods to monitored services, without local code execution.
AD-02Log-injection attacker
Influences service-logged fields through requests; a more specific form of the remote attacker.
AD-03Local unprivileged user
Has local shell access without root or daemon-UID authority; may reach loopback HTTP or have read-only monitoring access.
AD-04Compromised service
Can fabricate log content after gaining execution in a monitored service.

Architecture

Follow the input. Identify the trust.

The numbered transitions connect the data flow to the threat register. The administrative and telemetry interfaces form separate boundaries into the daemon.

Detection and enforcement path
  1. Untrusted networkAttacker

    Crafted requests and floods

  2. TB-01 →Service

    sshd, nginx, postfix

  3. TB-02 →Recorded logs

    Files or journal output

  4. TB-03 →fail2zig

    Receipts → detection → durable intent

  5. TB-04 →Kernel firewall

    Selected enforcement backend

TB-05Local client → IPCSocket permissions + peer credentials

TB-06HTTP caller → telemetryLoopback by default; optional in v0.4.0

Trusted configuration governs the daemon (A.02). nftables uses direct netlink; ipset and iptables use argv-based subprocesses. Explicit backend selection is available in v0.4.0.

TB-02

Service → recorded logs

Service interpretation becomes the evidence used for ban decisions.

TB-03

Logs → daemon

Attacker-influenced bytes enter bounded parsing, durable receipts and native policy.

TB-04

Daemon → kernel

CAP_NET_ADMIN authorizes changes to firewall state.

TB-05

Local client → IPC

Peer credentials determine access to daemon administration.

TB-06

HTTP caller → telemetry

Bind restrictions limit access to read-only operational state; they do not authenticate callers.

STRIDE / 6 categories / 18 threats

Threats, controls, and remaining risk.

Every entry preserves its reference ID and affected boundary or assumption. Implementation details remain available beneath the mitigation and limitation.

Mitigated
A control addresses the threat; stated limits still apply.
Partial
Controls exist, with an identified gap remaining.
Accepted
An explicit trust assumption or accepted limitation.

Spoofing

All categories ↑
S-01

Unauthorized IPC access

Mitigated

Boundary TB-05

Mitigation
Socket permissions and peer credentials distinguish monitoring from administration.
Residual risk
Root or the daemon UID can change protection. Monitoring-group access exposes operational data but grants no mutation authority.
Implementation details
The socket is mode 0660. The shipped daemon runs as fail2zig; its configured service/monitor group has read-only access. Mutations require uid 0 or the daemon UID, checked through SO_PEERCRED. The installer can select a custom group.
S-02

Spoofed source addresses

Accepted

Boundary TB-02

Mitigation
Filters use the source address recorded by the service, matched by structural position.
Residual risk
A service that trusts forged proxy headers can record a false address. Operators must configure trusted proxies correctly.
Implementation details
fail2zig operates on the source IP the service records. If the service trusts attacker-supplied proxy headers, the attacker can appear as an ignore-listed IP — operators must configure the service to record the real source address. Filter design uses structural positions, not header substrings.

Tampering

All categories ↑
T-01

Configuration tampering

Mitigated

Assumption A.02

Mitigation
Unsafe write permissions are rejected; configuration is validated before startup proceeds.
Residual risk
A non-root owner produces a warning. Root access and filesystem integrity remain trust assumptions.
Implementation details
Config is mode 0640, owned root:fail2zig — an unprivileged attacker cannot write it, and the daemon enforces it: a world-writable or non-root-group-writable config is refused at load with its mode and the chmod 0640 fix (SEC-012); a non-root owner warns. Modifying it requires root, which is outside this model's attacker (A.04) and covered by the filesystem-integrity assumption (A.02). Config validation runs at startup, before any firewall or event-loop state change, so a malformed config fails closed rather than partially applying.
T-02

Persisted-state tampering

Mitigated

Assumption A.02

Mitigation
Private native state and transactional SQLite commits preserve recovery information; unusable state is refused.
Residual risk
Database integrity checks are not authentication against an attacker who can rewrite the store. State-directory access and coherent backups remain trusted.
Implementation details
SQLite holds receipts, checkpoints, retries, ownership, effect intent and confirmed history. The native parent and database must belong to the service UID. Corrupt or incompatible state causes startup refusal, not an empty-state reset. Preserve the database and WAL/SHM coherently; repair access or restore original state before recovery.
T-03

Forged service logs

Accepted

Boundary TB-02

Mitigation
The model explicitly treats service-produced logs as authoritative input.
Residual risk
A compromised service can fabricate failures or suppress them; fail2zig cannot establish their truth.
Implementation details
A compromised service can forge any log content. This is covered by A.03 — fail2zig cannot distinguish a forged log from a real one. Operators should treat a compromised service as a precondition to investigate host integrity, not a fail2zig failure mode.

Repudiation

All categories ↑
R-01

Incomplete decision history

Partial

Boundary TB-05

Mitigation
Durable confirmed history and operational outcomes expose protection changes and uncertainty.
Residual risk
History retention is bounded and is not a cryptographically authenticated audit archive.
Implementation details
fail2zig history provides paginated confirmed history. Administrative mutations return structured outcomes and revisions, distinguishing installed, partial and uncertain effects. Required recovery anchors stay pinned during retention. External audit retention needs its own protected storage and policy.

Information disclosure

All categories ↑
I-01

Exposed operational endpoints

Mitigated

Boundary TB-06

Mitigation
HTTP defaults to loopback; metrics_enabled = false removes HTTP and WebSocket listeners.
Residual risk
Loopback is not authentication, and HTTP shares the privileged daemon process.
Implementation details
Read-only HTTP serves metrics, status, bans and readiness. /api/health reports HTTP 200 when ready and 503 otherwise. Remote exposure needs operator access controls; disabling HTTP leaves IPC monitoring available.
I-02

Unauthorized ban-list access

Mitigated

Boundary TB-05

Mitigation
IPC authorizes read-only monitoring separately from protection mutations.
Residual risk
Monitoring reveals operational data. Limit membership even though it grants no ban/unban authority.
Implementation details
The configured service/monitor group can read status. Root or the daemon UID is required for mutations. The configuration query redacts paths and address lists for non-administrators.
I-03

Sensitive operational logs

Mitigated

Boundary TB-03

Mitigation
Journal access controls restrict operational logs; info-level output excludes full log content.
Residual risk
Ban IPs remain operational data. Debug output and journal-reader permissions need appropriate handling.
Implementation details
Operational logs go to journald under the daemon's unit. Access follows journald's access model (per-user journal, systemd-journal group). fail2zig redacts log content in info-level output; full per-line decisions are only logged at debug.

Denial of service

All categories ↑
D-01

State exhaustion from log floods

Mitigated

Boundary TB-03

Mitigation
Explicit admission, queue, retry and storage limits bound native processing without evicting critical protection state.
Residual risk
Exhaustion can pause ingestion or require intervention. Admission budgets are not a guarantee against host OOM or a total RSS limit.
Implementation details
Native resource plans cover sources, detection, retries and administration; SQLite has separate heap/page limits. Durable receipts, active owners and unresolved effect intent are retained. Storage failure pauses affected ingestion and exposes degraded health while preserving protection.
D-02

Pathological parser input

Mitigated

Boundary TB-03

Mitigation
Compiled matchers and bounded native JSON rules limit record size and matching work.
Residual risk
Finite input and work limits do not eliminate sustained CPU/I/O pressure or guarantee that every service log is truthful.
Implementation details
Built-ins and custom native rules produce typed subjects without arbitrary regex execution or shell evaluation. The custom-rule path caps rule documents at 4 KiB and records at 2 KiB, with bounded fields, segments and work. These are path-specific limits, not a whole-daemon allocation guarantee.
D-03

Authorized-client IPC flooding

Partial

Boundary TB-05

Mitigation
A bounded connection backlog limits queued connections.
Residual risk
Per-client rate limiting is not implemented; an authorized client can degrade control-plane responsiveness.
Implementation details
The connection backlog is bounded; per-client rate limiting remains future work. An authorized client can degrade control-plane responsiveness.
D-04

Slow or malformed HTTP requests

Mitigated

Boundary TB-06

Mitigation
Request deadlines and bounded HTTP buffers/connections limit listener work.
Residual risk
Clients can occupy finite slots. HTTP parsing remains in the privileged daemon process.
Implementation details
HTTP and WebSocket are optional: metrics_enabled = false removes their listeners while retaining IPC. Keep loopback binding or apply external access controls. A connected stream is not proof of enforcement readiness.
D-05

Unavailable firewall protection

Mitigated

Boundary TB-04

Mitigation
Persisted intent and owned-kernel readback distinguish installed protection from partial or uncertain effects.
Residual risk
Observe-only mode blocks no traffic. Namespace and backend scope limits still apply, and uncertainty can require recovery.
Implementation details
global.firewall selects the backend; an explicit selection never falls back. Automatic detection stops on permission denial. Unusable required enforcement refuses startup by default; explicit on_no_backend = "log-only" permits degraded observation. Overlapping jail owners retain independent protection. Effects stay in the current network namespace; custom selectors are unsupported.

Elevation of privilege

All categories ↑
E-01

Parser compromise with daemon privileges

Partial

Boundary TB-03

Mitigation
ReleaseSafe checks, bounded parsing, a dedicated UID and the shipped systemd sandbox reduce exposure.
Residual risk
SQLite C and HTTP share process privileges. These controls neither prove whole-process memory safety nor eliminate privileged compromise.
Implementation details
The unit retains CAP_NET_ADMIN, CAP_NET_RAW and CAP_DAC_READ_SEARCH. NET_RAW permits raw IP sockets; AF_PACKET is excluded. Filesystem/syscall restrictions and NoNewPrivileges narrow authority, but there is no separate unprivileged HTTP process.
E-02

Log injection causes wrongful bans

Mitigated

Boundary TB-02

Mitigation
Structural source-IP matching, byte-class restrictions, and ignoreip rules constrain ban decisions.
Residual risk
Protection depends on trustworthy service log fields; forged authoritative fields remain outside the parser’s guarantee.
Implementation details
Filters match on structural positions in well-known log formats — the "source IP" field, not arbitrary substrings. Byte-class restrictions prevent crafted payloads from spanning fields. ignoreip CIDRs always win. These controls depend on the service keeping attacker-controlled text separate from authoritative source-IP fields.
E-03

Unauthorized unban requests

Mitigated

Boundary TB-05

Mitigation
Ban and unban mutations require root or the daemon UID, independently of monitoring-group membership.
Residual risk
Trusted administrators can deliberately remove protection; the daemon does not defend against their authority.
Implementation details
Peer credentials authorize mutation. A monitoring-group member cannot unban merely by connecting to the socket. Removing one jail owner does not release protection still owned by another jail.
E-04

Compromised release artifacts

Mitigated

Assumption A.02

Mitigation
Release checksums and build-provenance attestations support verification; CI actions are pinned to commit SHAs.
Residual risk
Provenance does not prove source or toolchain safety. Byte-identical reproducible builds are not claimed.
Implementation details
Each release publishes SHA256SUMS and a SLSA build-provenance attestation, verifiable with gh attestation verify. GitHub Actions are pinned to full commit SHAs. These provide artifact integrity and provenance checks; byte-identical rebuilds are not claimed.

Operational behavior

When a control reaches its limit.

Startup refusal, explicit degradation, and state recovery are different outcomes. These are the behaviors an operator should expect.

No usable firewall backend

Exit with a cause by default. Explicit on_no_backend = "log-only" permits DEGRADED observation.

D-05 ↗

Corrupted persisted state

Refuse unusable native state; restore coherent state and source continuity before healthy admission.

T-02 ↗

Unsafe configuration permissions

Refuse unsafe write permissions before startup proceeds.

T-01 ↗

Native resource limit reached

Pause affected admission or report degraded health; retain critical receipts, owners and unresolved intent.

D-01 ↗

Unknown configuration key

Reject the configuration with line and column information.

T-01 ↗

Firewall operation fails

Report failed, partial or uncertain effects; confirm installed protection through owned-kernel readback.

D-05 ↗

Outside this model

Kernel compromise
The Linux kernel and its security boundaries are trusted.
Physical host access
Physical protection and disk integrity belong to the host security model.
Service compromise
fail2zig cannot repair a compromised service or authenticate the truth of its logs.
Volumetric network attacks
Packet floods require kernel or upstream network defenses; detection here begins at the application log.
Web application protection
Request-body inspection, SQL injection prevention, XSS, and CSRF defenses belong to the application or WAF.
Host side channels
Timing, cache, and microarchitectural side-channel defenses are outside this model.
Toolchain compromise
Build provenance does not establish that the compiler, libc, or build inputs are free of compromise.

Assurance

Verification and review status.

Engineering checks and release provenance provide evidence. An independent security audit remains planned.

16 Sep 2026

0.4.0 native runtime and service identity

Durable SQLite state, owned-kernel readback and read-only monitoring-group access. Selected live checks used Debian 13 x86_64 before the version-only release update; rebuilt artifacts passed native/emulated checks. Keep default loopback ignores: matching loopback SSH failures can lead to storage intervention in this release.

Released
24 Apr 2026

Input-testing harnesses

Parser, IP, IPC, and TOML fuzz harnesses are part of the test suite. Allocator-failure testing exercises failure paths; test coverage is not proof of absence of defects.

Implemented
31 May 2026

Release integrity and provenance

Published checksums and SLSA attestations support artifact verification. The release verification job re-downloads and checks artifacts; byte-identical rebuilds are not claimed.

Implemented
Pre-1.0 target

Independent security audit

Planned scope: parsing, allocator boundaries, privileges, firewall backends, and IPC authorization. Sponsor and schedule remain unconfirmed.

Planned

Published findings and disclosure records: GitHub security advisories ↗. The engine repository publishes its own threat model document ↗ with each threat’s severity, control and the decision record behind it.

Responsible disclosure

Report a vulnerability privately.

Use GitHub’s private advisory form so maintainers can assess the issue and coordinate a fix before public disclosure.

48 hours
Acknowledge valid reports.
14 days
Initial assessment, best effort.
Coordinated disclosure
Fix before disclosure for high-severity issues, or an agreed 90-day timeline.
Reporter credit
Credit unless declined. No bounty; no legal action for good-faith reporting within scope.