Browse documentation
On this page

Configuration reference

Native TOML settings, durations, timed entries, sources, storage, enforcement and reload boundaries for fail2zig 0.4.8.

The default file is /etc/fail2zig/config.toml. Use the worked example as a starting point and validate before starting or reloading. This reference describes 0.4.8 native operation.

File format and permissions

The parser accepts a strict TOML subset: [global], [defaults] and [jails.<name>]. Unknown keys/sections, duplicate keys, mixed array types and invalid values are rejected. Use section headers, not dotted keys on the left. Comments begin with #; strings must be quoted. Jail names use ASCII letters, digits, hyphens or underscores.

Keep configuration administrator-owned and readable by the service. World-writable or non-root-group-writable configuration is refused. A non-root owner warns. The installer preserves existing operator configuration; check its permissions when changing the service group.

Duration strings

bantime, findtime, bantime_increment_max_bantime, bantime_increment_jitter, bantime_increment_decay and ignoretime accept integer seconds or quoted durations in both defaults and jail overrides. The duration part of a banip or ignoreip entry uses the same terms as one word inside the entry string, for example "198.51.100.0/24 1d".

[defaults]
bantime = "1h30m"
findtime = "10m"
bantime_increment_max_bantime = "1w"
bantime_increment_jitter = "30s"
bantime_increment_decay = "30d"
UnitSeconds
s1
m, mm, min60
h3,600
d86,400
w604,800
mo2,629,800
y31,557,600

Months and years are fixed intervals, not calendar arithmetic. Each term is a nonnegative integer followed by a unit; terms can touch or have intervening whitespace ("1h 30m"). Fractions, signed terms, arithmetic, unknown units and overflow are rejected. Bare 24h is not valid TOML.

Finite bantime, findtime and the escalation cap must be positive. Jitter permits zero and must not exceed the cap. 0 disables bantime_increment_decay and makes ignoretime permanent. An entry’s duration must be finite, at least 1s and at most 100 × 366 days. Only bantime accepts "permanent". On the command line, ban --duration and ignore add --for accept seconds or this syntax; --timeout and migration window arguments remain numeric.

[global]

Logging and administration

KeyType / defaultMeaning
log_levelString, "info"debug, info, warn or err. Live reload supported.
log_targetString, "stderr"Stderr or an absolute log-file path. Changing the target requires restart; SIGUSR1 reopens it after rotation.
log_formatString, "text""json" writes every daemon log line as one JSON object (ts, level, scope, message fields). Restart-only.
socket_pathString, "/run/fail2zig/fail2zig.sock"Local administration and monitoring socket.
pid_fileString, "/run/fail2zig/fail2zig.pid"Deprecated compatibility key; warns when explicitly set and is not written. Use systemd MainPID.

The socket is mode 0660. The configured service/monitor group has read-only access. Mutations require root or the daemon UID, verified with peer credentials. Every mutation writes one audit line to the daemon log with the command, target, caller UID and outcome. Use fail2zig --socket <path> status when overriding the default socket.

State and resource limits

KeyType / defaultMeaning
state_fileAbsolute path, "/var/lib/fail2zig/state.bin"Native SQLite store for receipts, checkpoints, policy, retries, owners, effect intent, origins and confirmed history.
memory_ceiling_mbInteger, 64, minimum 16Configured memory budget; do not interpret it as a whole-process RSS cap.
native_memory_ceiling_mbPositive integer, 512Native admission budget in MiB. The plan for 65,536 live bans is about 337 MiB with two enforcing jails, 438 MiB with sixteen.
native_fd_ceilingPositive integer, 2048Native descriptor admission budget.
history_retentionNonnegative integer seconds, 86400Age policy for confirmed-history cleanup; required recovery anchors stay pinned.
history_max_matchesInteger 0–1024, 10Confirmed-history match-retention policy.

The database and its parent must belong to the daemon UID. The historical .bin extension does not make it the legacy binary format. Native startup refuses corrupt, incompatible or inaccessible required state instead of starting empty. The file cannot grow past 1 GiB; a file already past that size is refused at startup. When a commit finds the file full, ingestion pauses and retries while rules already in the kernel stay enforced. The file never shrinks; rows removed by retention free pages for reuse inside it, and status shows the size and page use. A configuration that sets native_memory_ceiling_mb = 256 explicitly must raise it or the daemon refuses with NativeMemoryAdmission. Storage failure pauses affected ingestion and reports degraded health while retaining protection. Preserve the database and WAL/SHM coherently; do not delete them to clear an error. See upgrades and the memory model.

HTTP and WebSocket

KeyType / defaultMeaning
metrics_enabledBoolean, truefalse disables both HTTP and WebSocket; IPC and protection remain available.
metrics_bindIP string, "127.0.0.1"Listen address. Loopback is not authentication.
metrics_portInteger 1–65535, 9100Port remains validated even when disabled. Zero does not disable HTTP.
websocket_max_clientsInteger 1–1024, 16Maximum simultaneous WebSocket clients.

Endpoints are /metrics, /api/status, /api/bans, /api/health and /events. Health reports readiness with HTTP 200/503. HTTP shares the privileged daemon process. Keep external access controlled or disable it if unused. The per-jail metric names are listed in the administration reference.

Firewall

firewall is a global string, default "auto".

ValueSelection behavior
"auto"Reuse the backend recorded for an existing installation; otherwise probe nftables, then ipset and iptables.
"nftables"Probe only nftables; direct netlink.
"ipset"Probe only ipset; needs both ipset and iptables tools.
"iptables"Probe only iptables.

An explicit backend never silently switches to another. With existing state, auto keeps the recorded backend across restarts, including when no bans are active. An explicit backend or namespace selector that conflicts with the saved installation is rejected rather than migrating ownership. Restore the recorded setting or use auto; do not delete the database to switch backends. Jail enforce selects enforcement policy, not a backend. If every enabled jail is log-only, enforcement backend detection is skipped.

on_no_backend is "fail-closed" by default: unusable required enforcement causes startup refusal. "log-only" explicitly permits degraded observe-only operation; status reports the cause. It does not install protection.

firewall_namespace is an ownership anchor, not a request to enter another namespace. Effects remain in the daemon’s current network namespace. Custom selectors and unit overrides that move the daemon between namespaces are unsupported; leave this setting at its built-in default.

DNS and timezone data

dns_server is an optional resolver IP; dns_port defaults to 53 and accepts 1–65535. Native hostname rules use bounded resolution and retain retry state; a hostname is not an arbitrary command. timezone_root defaults to "/usr/share/zoneinfo" for named timezone data. Leave these defaults unless the chosen input contract requires an explicit deployment setting.

[defaults]

KeyType / defaultMeaning
bantimeSeconds/duration, 600, or "permanent"Initial ban duration.
findtimePositive seconds/duration, 600Window for repeated failures.
maxretryInteger 1–128, 5Failures required within findtime.
sourceString, "auto"auto, file, journald or internal.
enforceBoolean, truetrue enforces through the selected global backend; false observes without enforcing.
banactionDeprecated string, "nftables"Compatibility spelling; nftables, iptables, ipset mean enforce, and log-only means observe.
ignoreipString array, []IPs/CIDRs never banned, each optionally followed by a duration. The shipped example includes loopback ignores.
banipString array, []IPs/CIDRs banned while listed, permanent or timed ("198.51.100.0/24 1d"). Applied at start and on reload.
ignoreselfBoolean, trueAdds the host’s own IPv4/IPv6 addresses, loopback included, to the effective ignore list with source self.
ignoretimeSeconds/duration, unset (0, permanent)Default life of an exclusion with no duration of its own, configured or ignore add without --for.

Prefer enforce = true/false in new configurations. banaction remains accepted for compatibility and emits a deprecation warning when explicitly set. Do not combine the two in one section. Neither setting selects the firewall backend or enables arbitrary shell actions.

Configured bans and exclusions

A banip entry is a live ban with origin config in that jail, permanent for a permanent entry and ending at the stored deadline for a timed one. It takes over an existing ban of the same address in the jail and replaces its expiry with its own. Removing an entry and reloading lifts its ban at once. While a config ban is live, fail2zig ban or unban of the address is refused with banned by config: edit banip. A jail holds at most 1,024 effective banip entries. Host names are not accepted.

An address in both the effective ignoreip and the effective banip of one jail, as equal entries or one containing the other, is a configuration error: the daemon refuses to start, and reload reports rejected naming the jail and the address.

ignoreself reads the host’s addresses from the kernel at start and at every reload; an address assigned later counts from the next reload. self entries do not count against the 128-entry ignore limit. A jail list for ignoreip or banip replaces, rather than appends to, the [defaults] list.

ignoretime is unset or 0 for permanent. A jail value overrides [defaults], and 0 in a jail restores permanent under a [defaults] value. A duration written on the entry, or ignore add --for, wins. A non-zero value on a rule_files jail is refused.

Timed entries

A banip or ignoreip entry written with a duration, and an ignoreip entry that takes ignoretime, is timed. The period starts when the daemon first applies the entry, at start or at a reload, and runs once: a reload or a restart does not restart it. Changing the entry’s duration, or the ignoretime it takes, starts a fresh period, as does removing the entry, reloading and adding it back. When the period runs out the entry stays in the file and does nothing; list or ignore list shows it as expired until it is removed from the file. A timed ignoreip entry is refused on a jail with rule_files.

Escalation

KeyDefaultMeaning
bantime_increment_enabledfalseEnable escalating durations.
bantime_increment_multiplier1.0Positive base multiplier.
bantime_increment_factor1.0Nonnegative growth factor; integer or decimal.
bantime_increment_formula"linear"linear or exponential, not an arbitrary expression.
bantime_increment_scope"per-jail"per-jail or overall recurrence scope.
bantime_increment_max_bantime604800Positive seconds/duration cap.
bantime_increment_jitter0Seconds/duration jitter bound, at most the cap.
bantime_increment_decay2592000Escalation resets to the base bantime when the last ban ended longer ago than this.

Linear uses bantime × multiplier × (1 + factor × n); exponential uses bantime × multiplier × factor^n, where n is the prior ban count used by the policy. For doubling with no jitter, set multiplier 1, factor 2 and formula "exponential". A multiplier of 2 alone does not select doubling growth.

Decay is measured from the end of the last ban, not its start, so nothing decays during a live ban and a permanent ban never decays. The reset is complete, never a step down; 0 disables it. After a manual unban, failures count from zero again while the escalation count is kept.

Escalation settings inherit field by field. A jail overrides only the keys it explicitly sets; omitted keys retain [defaults] values.

[jails.<name>]

A jail declares a source, matcher and policy. Enabled jails must have admitted protection; unknown built-in filters without native rule files fail validation.

KeyDefault / contract
enabledtrue; disabled jails are not started.
filterBuilt-in service name, or the service name for a custom rule-file jail. Built-ins accept hyphen/underscore forms.
logpathArray of absolute file paths, default empty. Use file-source paths that the service can read.
sourceInherits defaults. Prefer explicit source selection for reviewed configurations.
maxretry, findtime, bantime, enforceOverride the corresponding default. banaction remains a deprecated compatibility input.
ignoreip, banipEach replaces, rather than appends to, the defaults list. Repeat loopback entries when overriding ignoreip.
ignoreself, ignoretimeOverride the [defaults] values for this jail.
on_truncation"refuse" or "restart"; file sources only, rejected in [defaults]. See log file lifecycle.
bantime_increment_*Field-by-field overrides as described above.

on_truncation applies to every logpath of the jail and covers a file truncated in place, a rotated-out file removed with unread bytes, and a log replaced by a different file. refuse pauses the source until repair-source acknowledges the loss; the daemon keeps running and other jails enforce. restart resumes and records a continuity-loss event. The diagnostics are InvalidTruncationPolicy, TruncationPolicyNotFileSource and TruncationPolicyNotAllowedInDefaults. A change is restart-only. Neither value recovers records that were never read.

Source and time contracts

KeyContract
timestampFile inputs require an explicit format: iso8601, syslog, epoch_seconds, common_log or undated. Choose for the actual log format.
timezone_offset_minutesFixed offset from UTC, -1439–1439, for the selected timestamp contract.
timezoneNamed timezone; cannot be combined with the fixed offset.
timezone_ambiguityreject, earlier or later; requires a named timezone.
journal_executablesOptional exact origin profile. Built-in SSH jails discover trusted standard paths when omitted; custom journal rules require it.
rule_filesUp to eight distinct absolute paths to bounded native JSON rules.
ignore_fileOptional ignore-policy file for custom rule jails; its contents can be reloaded.
[jails.sshd]
filter = "sshd"
source = "journald"
journal_executables = ["/usr/sbin/sshd", "/usr/lib/openssh/sshd-session"]

auto resolves the configured source at startup. file reads declared paths; journald requires journalctl and admitted origins; internal is the confirmed-ban feed for recidive. Internal recidive does not use log paths or custom rule files. A built-in SSH jail with no journal_executables setting discovers available root-owned executables from a bounded catalog of standard SSH paths. Discovery rejects unsafe path ownership or permissions. An explicit nonempty list remains an exact override; an empty list is invalid, and custom journal rules require an explicit profile. A source is healthy only after its runtime checks pass; configuration validation alone is not that check. A missing log file is reported as missing (FileNotFound) by jails and is not a failure. See log watching and the log file lifecycle.

For PortSentry v2.0.7, use filter = "portsentry", source = "file" and timestamp = "iso8601" with the history file. Follow the sensor and jail setup; this is not a generic syslog or legacy PortSentry input. PortSentry recreates its history file at every start, so choose on_truncation for that jail deliberately. File encoding replacement is described under log watching.

Deprecated and migration-managed settings

backend is a deprecated source alias accepted in defaults/jails: systemd maps to journald; auto, polling, pyinotify and gamin map to auto. Using both source and backend in one section is an error. It never chooses the firewall backend.

native_ingestion defaults to true; disabling native operation is not a supported deployment route. compatibility_manifest and compatibility_pending are migration-managed admission metadata. Do not clear blockers by hand to force an unsupported imported jail into service.

Validation and reload

sudo fail2zig --validate-config --config /etc/fail2zig/config.toml

Diagnostics identify file, line, column, section and key when available. Unreadable files and permission failures can be positionless. Validation summarizes configured sources and prints on_truncation=<value> for each file jail; an auto source is resolved when the daemon starts. Inspect the actual source and readiness through fail2zig jails and fail2zig status.

Some settings apply through fail2zig reload; others return restart_required. Adding a jail to the file and removing one are live changes. See the reload table. Neither successful syntax validation nor a reload request establishes installed firewall protection.

The tagged parser, shipped example and fail2zig.toml(5) define the release’s accepted configuration.

Edit on GitHub →