On this page
fail2zig — administration
Status, ban origins, runtime exclusions, firewall observation and withdrawal, online source repair, support bundles and jail controls in 0.4.8.
Administration uses the same fail2zig executable as the daemon. This page keeps
its existing URL so bookmarks continue to work. The separate client was used
before v0.4.0; those releases and their Git tags are retired. See the
historical changelog for the old release history.
Synopsis
fail2zig [--socket <path>] [--output table|json|plain] [--no-color] [--timeout <ms>] <command> [args]The default socket is /run/fail2zig/fail2zig.sock; the default timeout is 5000 ms.
table is for people, json for scripts and plain is tab-separated.
ban --duration and ignore add --for accept seconds or the configuration’s
duration syntax (2m, 1h30m); --timeout remains an integer.
The socket is mode 0660. Members of the configured service/monitor group can
read status. Root or the daemon UID can mutate protection, authenticated through
SO_PEERCRED. Group membership alone does not grant ban, unban, ignore or reload
authority. The shipped service uses fail2zig:fail2zig; the installer supports a
custom group. Every mutation, the online repair-source included, writes one
audit line to the daemon log with the command, target, caller UID and outcome.
Every command has a help page: fail2zig help <command> or <command> --help,
the offline tools included. help <typo> and fail2zig <typo> exit 2 with a
suggestion. The daemon’s --help and the client’s help list the same commands.
Status and inspection
fail2zig status
fail2zig status --details
fail2zig jails --details
fail2zig list --sort
fail2zig list --jail sshd --origin manual --sort
fail2zig list --origin config
fail2zig stats
fail2zig stats --details
fail2zig firewall show
fail2zig --output json history --jail sshd --limit 20
fail2zig config
fail2zig version| Command | What it reports |
|---|---|
status [--details] | Version, protection, storage, backend, state file and tracking pool, paused sources, loss events, counters. |
jails [--details] | Enabled/paused state, source health, configured mode, observed protection and bans per jail. |
list [--jail <name>] [--origin detection|manual|config] [--details] [--sort] | Active bans with their origin, optionally restricted to one jail or origin and sorted numerically by address. |
stats [--details] | Published worker snapshot, snapshot/readback ages and daemon-start counters. |
history [--jail <name>] [--limit 1..256] [--cursor <token>] | Confirmed history with bounded pagination; each event carries the ban’s origin. |
firewall show [--limit 1..256] [--cursor <token>] [--details] | Latest retained observation of fail2zig-owned kernel protection, with the owning ban’s origin per entry. |
config | Effective configuration; paths and address lists are redacted for non-administrators. |
version | Running daemon version. --version instead reports the local executable. |
A policy decision, installed protection and an uncertain effect are different states. Inspect storage and enforcement causes when the daemon is degraded. Check protection knowledge age, pending bans and overdue removals. A stale or degraded status is not confirmation of current kernel protection.
status table output adds State file: <bytes> (<pages> pages of <max_pages>)
against the 1 GiB ceiling and Tracking pool: <tracked> of <limit> tracked, <banned> of <limit> banned against the shared pool of 65,536 tracked addresses
and 65,536 live bans. A paused file source appears as a Paused: block with the
jail, path, cause and the exact repair-source command; a source that resumed
under on_truncation = "restart" shows a Loss events: row, and --details
lists each retained event. JSON adds state_file_bytes, state_file_pages,
state_file_max_pages, pool_tracked, pool_banned, pool_tracking_limit,
pool_ban_limit, paused_sources and a sources array. Use
status --output json; there is no --json flag. status does not yet list a
missing log file; jails does.
jails shows SOURCE HEALTH per jail: ok, missing (FileNotFound),
waiting (<cause>), paused (<cause>) or broken (<cause>). missing is a
healthy state: the log is absent and reading resumes when it appears. waiting (RetryCapacity) means the jail is waiting for room in the tracking pool and
resumes as room frees. In jails, MODE is configured enforcement and PROTECTION
is observed protection. A plain-output ban count of - is unknown; 0 means
known empty. See the log file lifecycle for the
source states.
--details expands table output for status, jails, list, stats and
firewall show. It does not change plain or JSON output. Detailed jail output
switches to complete stacked blocks on narrow terminals. History counts retained
confirmations, not every ban ever issued.
Ban origins
Every ban carries one origin: detection (a filter matched), manual (ban) or
config (banip). list, history and firewall show show it in an ORIGIN
column and as JSON origin. list --origin keeps only bans of that origin;
--origin config also lists timed banip entries whose period has run out, with
expired set to true. History events written before 0.4.8 show detection.
list --sort is optional in table, plain and JSON output; it sorts addresses
numerically, IPv4 before IPv6. Without the flag, listing order is unchanged.
stats reads the worker’s published snapshot without requesting a fresh firewall
inspection or database scan. Show snapshot age separately from firewall readback
age. Its counters accumulate since daemon start and reset on restart; they are not
rates or lifetime totals.
Firewall observation
fail2zig firewall show
fail2zig firewall show --details
fail2zig --output json firewall show --limit 64firewall show reads the daemon’s latest retained observation of fail2zig-owned
kernel protection. It does not run a new inspection, repair rules or mutate firewall
state. Native nftables observation communicates directly with the kernel and does
not require the nft executable; that property does not apply to iptables or ipset.
On nftables the inventory is kept current by the kernel’s acknowledgement of each
install batch and by nf_tables change events, with a full audit of the owned set
every 60 seconds; ipset and iptables keep their readback cadence.
list reports ban intent and confirmation. firewall show reports a sampled kernel
observation. Neither proves packet reachability through the complete host or network
ruleset. Inspection is limited to fail2zig-owned protection in the daemon’s current
network namespace; it does not enumerate UFW, firewalld, container rules or another
application’s firewall state.
Check observation age and the latest attempt together. A failed inspection retains the previous observation and marks the latest attempt failed. Unavailable observation, absent installation and a verified installation with zero entries are separate states. A successful command exit means the query was answered, not that protection is healthy.
At most 256 entries are retained. Pages default to 64 entries, accept at most 256 and
use a 60-second cursor tied to the retained observation. --details adds verified
owned tables, chains, sets and attachment rules plus sampled-entry placement. Those
structures appear only for a complete, stable, exactly verified owned readback; older
or incomplete observations report them as unavailable. JSON exposes additive nullable
structure and placement fields independently of --details.
Withdraw enforcement
sudo fail2zig firewall withdrawA healthy stop leaves the daemon’s installed rules in the kernel, where they keep
their timeouts; the next start reconciles them with durable state. A stop does not
unban anyone. firewall withdraw removes every rule the running daemon realized,
in batches with a kernel readback after each, and holds enforcement off until the
next start. Owners and ban deadlines are kept; new decisions are recorded but not
installed; reload does not lift the hold. While held, status reports
DEGRADED (FirewallWithdrawn) and the daemon logs a warning every hour. Run it
before stopping the daemon when the table must be empty. Outcomes are applied
with the count removed, partial when interrupted (run it again), uncertain
when a kernel failure leaves the remainder unknown, or rejected.
Ban and unban
sudo fail2zig ban 203.0.113.7 --jail sshd --duration 1h
sudo fail2zig ban 203.0.113.7 --jail sshd --force
sudo fail2zig unban 203.0.113.7 --jail sshdBoth commands require exactly one jail. Without --duration a manual ban runs
for the jail’s bantime. --scope host is the default; --scope net <cidr>
requires nftables or ipset. Unsupported scope is rejected. A manual ban on a
log-only jail is rejected rather than recorded as installed. Removing one jail’s
owner does not remove protection still owned by another jail.
A manual ban has origin manual. ban on an address that holds a detection ban
extends it to the requested duration and marks it manual. ban or unban on
a config ban is refused with exit 1 and banned by config: edit banip; edit the
configuration and reload instead. After an unban, the address’s failure count
starts from zero, so maxretry new failures inside findtime ban it again; its
escalation count is kept.
Before it opens the socket, ban refuses a target that is a loopback address, an
address of this host, or the client address of the SSH session it runs in, or a
--scope net network containing one. The refusal exits 1 and names the matched
address and --force. --force applies the ban; it is a guard against a typing
mistake, not a security boundary, and the daemon’s own authorization is unchanged.
Manual bans are never checked against the ignore list; a ban of an ignored address
is applied with a warning naming the ignore source.
JSON mutation reports include outcome, enforced, generation/revision and
reasons. Exit 4 means partial and exit 5 means uncertain; neither is success.
There is no unban --all command. Review list --output json and unban specific
address/jail owners deliberately.
Runtime exclusions
sudo fail2zig ignore add 198.51.100.20 --jail sshd
sudo fail2zig ignore add 203.0.113.0/24 --jail sshd --unban
sudo fail2zig ignore add 198.51.100.20 --jail sshd --for 2h
sudo fail2zig ignore remove 198.51.100.20 --jail sshd
fail2zig ignore list
fail2zig ignore list --jail sshdignore add records a runtime exclusion for one jail. The entry is stored in the
state database, survives restart and reload, and merges with the configured
ignoreip. From the next event after the commit a matching event counts as an
ignored detection. An existing ban stays until its deadline unless --unban is
given; the output names the ban and the exact command that lifts it. --unban
lifts that jail’s bans inside the entry, at most 256, and never a wider network
ban or another jail’s ban. --for <duration> gives the entry a life; at the
deadline the daemon deletes it. Without --for the entry takes the jail’s
ignoretime when set and is permanent otherwise.
ignore remove removes runtime entries only. A configured entry is edited in
ignoreip and applied with reload; the refusal names the file. ignore list
shows JAIL, ADDRESS, SOURCE (config, runtime, config+runtime or self) and
EXPIRES (-, the time left, or expired). An expired runtime entry is removed;
an expired configured entry stays listed until it is removed from the file.
Monitoring-group members may run ignore list with addresses redacted.
Each jail holds at most 128 entries, configuration and runtime combined; self
entries do not count. A jail with custom rules (rule_files) keeps its exclusions
in ignore_file; the commands refuse it and name the file. An exclusion never
unbans anything by itself.
Jail controls and history
sudo fail2zig jail pause sshd
sudo fail2zig jail resume sshd
sudo fail2zig jail disable sshd
sudo fail2zig jail enable sshd
sudo fail2zig history reset 203.0.113.7 --jail sshdUse pause/resume for processing control and enable/disable for jail state. Inspect
the returned outcome and current owners; do not assume a control command is an
unban. history reset <ip> --all resets that address’s history across jails.
History reset is separate from removing active protection.
Repair a paused source
sudo fail2zig repair-source --jail portsentry \
--source /var/log/portsentry.log --token r1 --acknowledge-truncationUnder on_truncation = "refuse" a file source that lost continuity pauses while
the daemon keeps running. status prints the exact command. Since 0.4.8 the
command runs through the daemon’s socket while it is running, with the same
token, refusals and durable outcome as the offline form, and no jail’s rules are
withdrawn. The offline form runs on a stopped daemon’s state when the socket is
absent. Repeating the command replays the committed outcome; the newest 256
outcomes are kept. Lost records are recorded, not recovered. See the
log file lifecycle.
Support bundle
sudo fail2zig support-bundle
sudo fail2zig support-bundle --out - --include-addresses > bundle.jsonsupport-bundle writes one redacted JSON file for a bug report: versions, status,
jails and stats payloads, retained loss events, the effective configuration and
the SHA-256 of the configuration file, state-file page and per-table sizes, kernel
readback counts, memory, host basics and the last warnings and errors from the
daemon log (default 200 lines, at most 2,000). Run it as root; it also works while
the daemon is stopped. Addresses become v4-<hash> or v6-<hash> unless
--include-addresses is given; paths other than configured log paths become
<path>; the bundle carries no ban list. The default output is
./fail2zig-support-<UTC time>.json, mode 0600, never overwritten. Exit 4 means
the bundle was written with sections that record an error. Read the file before
attaching it. See the tagged support-bundle guide.
Reload
sudo fail2zig reloadReload validates a complete proposed generation before publishing it. An invalid
proposal leaves the running generation intact. Outcomes are noop, applied,
rejected or restart_required, with reasons identifying the boundary.
| Live changes | Require restart |
|---|---|
Per-jail maxretry, bantime/permanent policy, escalation and decay; literal ignoreip and banip entries and their durations; ignoreself and ignoretime; adding or removing a jail; global log level; a custom jail’s ignore file | Findtime, source/filter/log paths, on_truncation, timestamp/timezone settings, journal executable list, rule files, log target and format |
An ignoreip edit applies from the next event and never alters an existing ban;
a banip edit bans or lifts at once. An added enabled jail starts in the same
reload. A removed jail’s bans are lifted and its elements removed last, after
every other change has committed; its history is kept, so history --jail <name>
still shows it. One jail that fails validation rejects the whole reload with a
message naming it, and every running jail is unchanged.
Edit configuration for policy changes. fail2ban’s runtime set API, arbitrary
regex mutation and dynamic actions are not supported. SIGHUP uses the same
reload boundary. A restart-required outcome is not confirmation that your edit
has taken effect.
Exit classes
| Code | Class | Operator meaning |
|---|---|---|
| 0 | success | The operation completed or the read-only query was answered; inspect the returned state. |
| 1 | rejected | Valid request refused or object absent. |
| 2 | usage | Invalid arguments or local validation failure. |
| 3 | unavailable | Socket missing, refused or timed out. |
| 4 | partial | Durable effect applied but confirmation incomplete. |
| 5 | uncertain | Effect may have applied; outcome not established. |
Use JSON for automation and branch on these classes, not formatted table text. Refusals that are not outcome objects are printed on standard error.
Prometheus and HTTP alternatives
HTTP defaults to 127.0.0.1:9100 and is read-only. It shares daemon privileges;
loopback binding is not authentication. Keep remote access behind appropriate
controls. metrics_enabled = false disables HTTP and WebSocket together while
IPC remains available.
| Endpoint | Purpose |
|---|---|
/metrics | Native storage, worker, clock, expiry, per-jail and state-file metrics. |
/api/status | JSON operational status. |
/api/bans | Ban snapshot. |
/api/health | Readiness: HTTP 200 when ready, 503 when a component withholds readiness. |
/events | WebSocket event stream. |
Per enabled jail, labelled jail="<name>": fail2zig_jail_bans_active (gauge),
fail2zig_jail_bans_total, fail2zig_jail_failures_counted_total,
fail2zig_jail_ignored_detections_total and fail2zig_jail_pool_evictions_total
(counters), and fail2zig_jail_source_state (gauge: 0 ok, 1 paused,
2 missing, 3 blocked). Daemon-wide gauges: fail2zig_state_file_bytes,
fail2zig_state_file_pages, fail2zig_pool_tracked, fail2zig_pool_banned,
fail2zig_pool_tracking_limit and fail2zig_pool_ban_limit. A jail’s series
appear when it starts or is added by a reload and stop when it is removed.
Readiness covers configuration, storage, sources, clock, enforcement and admin components. An open connection or a recent metrics frame proves neither readiness nor recent attack activity.
curl --fail http://127.0.0.1:9100/api/healthShell completions
fail2zig completions bash
fail2zig completions zsh
fail2zig completions fishThese print completion scripts. Install the output in your shell’s completion
location under the fail2zig name.