Why fail2zig

A security control.
With less to trust.

A daemon that reads attacker-influenced logs and changes firewall rules deserves a deliberately small runtime. fail2zig rebuilds host intrusion prevention around that requirement: constrained parsing, bounded tracking, and direct kernel enforcement.

Explore the design decisions

Built on a good idea. fail2ban established the jail model operators know and trust. fail2zig keeps that vocabulary and takes a different implementation path.

jailfiltermaxretryfindtimebantimeignoreip

The architectural difference

Same job. Different security decisions.

Each choice removes a runtime dependency, constrains input handling, or limits resource growth.

A smaller privileged runtime

fail2zig ships a static executable with Zig application code, embedded SQLite C and musl. Daemon, administration and migration share that artifact, with no Python runtime or database server required.

One compiled artifact to deploy, inventory, and replace.

Examine the trust surface

Matching with explicit boundaries

Built-in filters become specialized matching functions at compile time. Custom jails use bounded native JSON rules, with offline rule testing. Arbitrary regexes, Python extensions and shell actions are not executed.

A constrained parser for input an attacker can influence.

Inspect the parser

Durability within resource limits

Native receipts, retries, source checkpoints and protection owners persist in SQLite. Explicit resource limits bound admission; storage failure pauses affected ingestion while retaining protection and reporting degraded health.

Critical protection state stays durable under resource pressure.

Understand the memory bound

Kernel enforcement without a shell

The default nftables backend sends structured netlink messages directly to Linux and confirms installed protection by owned-kernel readback. It needs neither a shell-action template nor the nft executable to install bans.

No command-string interpretation in the nftables ban path.

Follow a kernel operation

fail2ban’s shell actions use the system shell; Python actions are also supported. Its filter guidance explicitly addresses hostile log input. fail2zig’s distinction is the constrained runtime it chooses to ship.

Operational confidence

Protection should be inspectable.

Knowing that a process is running is only the beginning. Operators also need to see what each jail reads, whether that source is healthy, and whether the jail enforces.

What the daemon makes explicitstatus · jails · /metrics

Source and read health

The resolved file, journal, or internal feed, with its reported read health.

Enforce or observe

Log-only decisions stay separate from firewall operations, including after restart.

Failure with a cause

No usable backend means exit by default. Explicit observe-only fallback reports DEGRADED.

Status describes the daemon’s operating state. Verifying packet drops still requires checking enforcement on the host. Verification steps

This distinction shaped development: real-system checks exposed gaps in filter wiring, log rotation, and restart enforcement. Fixes gained regression coverage alongside the deployment tests.

Threat modeling with STRIDE

The reasoning is part
of the product.

The published model maps adversaries and trust boundaries to six STRIDE categories. Each threat names its controls and the risk that remains, so you can judge the design against your own environment.

Read the full threat model

Six perspectives on the same system

  • Spoofing
  • Tampering
  • Repudiation
  • Information disclosure
  • Denial of service
  • Elevation of privilege

Published design analysis. Independent security audit remains planned.

The tradeoffs are deliberate

Choose it with the limits in view.

A narrower extension model
Built-ins require a rebuild to change; bounded native JSON rules provide a custom route. Arbitrary fail2ban regexes and actions are not translated into equivalent protection.
A smaller runtime still needs maintenance
Static components need binary updates. The kernel remains trusted; journald uses journalctl, and the ipset/iptables backends execute their tools with discrete arguments.
Controls have boundaries
ReleaseSafe checks help catch faults; they do not prove whole-process memory safety. Admission budgets are not a total RSS guarantee. HTTP shares daemon capabilities, and forged authoritative service logs can still cause false bans.
Focused on the Linux host
fail2zig reacts to service logs. It complements secure authentication, patching, and firewall policy; it does not replace them or provide volumetric DDoS protection.

Evaluate it on your host

A familiar model.
A smaller implementation.

Import your jails, review the differences, and verify enforcement before you switch.