- Undo is staged like any edit: in review mode the web UI's Undo, and hc history undo with -C or HOUSECARL_CHANGESET, add a revert operation that checks, when replayed, that nothing it touches changed since; applied, History links the entries. Imports and takeovers still can't be staged. - The rules page sums up a chain's built-ins in one line; a window lists them with what they do and their hits, and switches them. - Warnings about the machine (dashboard, hc status): nftables.service enabled (it loads /etc/nftables.conf at boot and flushes every table when stopped), and the imported table still in force beside Housecarl's. The docs say to disable nftables.service but not stop it. - A raw rule with other fields set is refused (raw_conflict) instead of silently ignoring them; the rule dialog's nftables section hides what it replaces, and the importer's raw rules carry only their text. - A dynamic set with configured entries is refused (set_dynamic_entries): they were silently ignored. The set dialog shows entries or the entry timeout, as fits. - The rule dialog's chain choices are just the chains' names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
130 lines
7.4 KiB
Markdown
130 lines
7.4 KiB
Markdown
# Validation
|
|
|
|
Every change is checked as a whole configuration before it is applied.
|
|
An **error** stops the change; a **warning** is reported with it, and
|
|
stays on the dashboard and in `hc status` until fixed. Each problem has a
|
|
stable code, shown by the CLI as `[code]` and in the API as
|
|
`problems[].code`. `housecarld check` runs the checks on the saved
|
|
configuration.
|
|
|
|
After Housecarl's own checks, **nftables** checks the rendered ruleset
|
|
(`nft -c`). What it refuses (mostly raw rules) comes back as a
|
|
`firewall` error with nftables' message, and nothing is changed.
|
|
|
|
The warnings marked *host* compare the configuration with this machine's
|
|
interfaces.
|
|
|
|
## Names
|
|
|
|
Zones, sets, services and port forwards have names (a forward's is
|
|
optional): a letter, then letters, digits, `-` and `_`, up to 48. Names
|
|
are unique per kind, ignoring case.
|
|
|
|
| Code | | When |
|
|
|---|---|---|
|
|
| `zone_name`, `set_name`, `service_name`, `forward_name` | error | not a valid name, or one that looks like an address |
|
|
| `zone_name_taken`, `set_name_taken`, `service_name_taken`, `forward_name_taken` | error | another object of the kind has that name |
|
|
|
|
## Settings
|
|
|
|
| Code | | When |
|
|
|---|---|---|
|
|
| `policy` | error | a policy that isn't `accept` or `drop` |
|
|
| `settings_address` | error | an own prefix or antispoof entry that isn't an address or prefix, or is in the wrong list (an IPv6 prefix in the IPv4 list) |
|
|
| `flowtable_device` | error | a flowtable device with a wildcard: name each device |
|
|
| `flowtable_device_missing` | warning, *host* | a flowtable device doesn't exist; the flowtable is left out until it does |
|
|
| `probation_range` | error | the probation isn't 10 to 3600 seconds |
|
|
|
|
## Zones
|
|
|
|
| Code | | When |
|
|
|---|---|---|
|
|
| `zone_empty` | error | no interfaces |
|
|
| `zone_interface` | error | not an interface name (up to 15 characters; a trailing `*` matches many) |
|
|
| `zone_interface_taken` | error | the interface is in another zone already |
|
|
| `zone_interface_missing` | warning, *host* | the interface doesn't exist here (yet); its rules match once it does |
|
|
| `no_external_zone` | warning | no zone is external: antispoofing, MSS clamping, the DHCPv6 client rule and port forwards without public addresses need one |
|
|
|
|
## Sets and services
|
|
|
|
| Code | | When |
|
|
|---|---|---|
|
|
| `set_entry` | error | an entry that isn't an address, prefix (without host bits) or range |
|
|
| `set_empty` | warning | a set (not dynamic) with no entries: rules using it match nothing |
|
|
| `set_dynamic_entries` | error | configured entries on a dynamic set: its entries come at run time |
|
|
| `set_timeout` | error | a timeout on a set that isn't dynamic, or under a second |
|
|
| `service_empty` | error | a service with no entries |
|
|
| `service_entry` | error | an entry that isn't `PROTO/PORTS` (`tcp`, `udp`, `sctp`; a port or range) or `icmp/TYPE`, `icmpv6/TYPE` with a type nftables knows |
|
|
|
|
## Matches
|
|
|
|
These apply to rules, source NAT and redirects.
|
|
|
|
| Code | | When |
|
|
|---|---|---|
|
|
| `unknown_zone` | error | a zone that doesn't exist |
|
|
| `match_in_zone` | error | in zones on the output chain (it has no incoming interface) |
|
|
| `match_out_zone` | error | out zones on the input or early chain, or on a redirect |
|
|
| `match_address` | error | an address that can't be read |
|
|
| `unknown_set` | error | a name that is neither an address nor a set |
|
|
| `unknown_service` | error | a service that is neither built in nor yours |
|
|
| `family` | error | a family that isn't `ipv4` or `ipv6` |
|
|
| `match_never` | error | the addresses, services and family can't all match the same packet (IPv4 sources and an `icmpv6` service) |
|
|
|
|
## Rules
|
|
|
|
| Code | | When |
|
|
|---|---|---|
|
|
| `rule_chain` | error | a chain that isn't `input`, `forward`, `output` or `early` |
|
|
| `rule_action` | error | an action that isn't `accept`, `drop`, `reject` or `log` |
|
|
| `raw_rule` | error | a raw rule over several lines, or with `;` |
|
|
| `raw_conflict` | error | a raw rule with other fields set (zones, addresses, services, limits, log…): it is used as written, so they would be ignored |
|
|
| `early_conntrack` | error | connection states, limits or a connection limit in the early chain, which runs before connection tracking |
|
|
| `early_reject` | error | reject in the early chain: drop instead |
|
|
| `ct_state`, `ct_status` | error | an unknown connection state or status |
|
|
| `rule_day`, `rule_hours` | error | an unknown day, or hours that aren't `HH:MM`, or only one of them |
|
|
| `rule_limit` | error | a rate that isn't a number per second, minute, hour or day |
|
|
| `rule_conn_limit` | error | a connection limit under 1 |
|
|
| `rule_limit_unit` | error | a rate's unit that isn't packets, bytes, kbytes or mbytes |
|
|
| `rule_limit_bytes` | error | a byte rate nftables can't hold: over 4 GB a second, 293 MB a minute, 4.9 MB an hour or 208 KB a day (the message gives it per second) |
|
|
| `rule_limit_key` | error | a limit both per source and per destination |
|
|
| `rule_quota` | error | a quota under 1 byte |
|
|
| `rule_quota_key` | error | a quota both per source and per destination |
|
|
| `rule_quota_reset` | error | a reset that isn't daily, weekly or monthly; a day with another reset than monthly; a day outside 1-31 |
|
|
| `rule_group` | error | a grouping prefix outside 1-32 (IPv4) or 1-128 (IPv6) |
|
|
| `rule_traffic_accept` | error | a rule with a quota or a byte rate that accepts: it is checked before established connections are accepted, so it can only drop, reject or log |
|
|
| `flowtable_traffic` | warning | a forward rule with a quota or a byte rate while the flowtable is on: it only counts each connection's first packets |
|
|
| `reject_with` | error | `tcp-reset` on a rule that matches more than TCP, or with an action other than reject |
|
|
| `log_prefix` | error | a log prefix over 100 characters |
|
|
|
|
## Port forwards
|
|
|
|
| Code | | When |
|
|
|---|---|---|
|
|
| `forward_protocol` | error | no protocols, or one that isn't `tcp`, `udp` or `sctp` |
|
|
| `forward_ports` | error | the public port isn't a port or range |
|
|
| `forward_to_port` | error | the inside port isn't one port, or is given for a range of public ports |
|
|
| `forward_target` | error | neither `to` nor `to6`, or `to` that isn't one IPv4 address, or `to6` one IPv6 address |
|
|
| `forward_public` | error | no public addresses and no external zone, or an IPv6 public address |
|
|
| `forward_overlap` | error | another forward has the same protocol, port and public address |
|
|
| `forward_hairpin` | warning | hairpin without public addresses: inside clients' connections can't be recognised |
|
|
|
|
## Source NAT and redirects
|
|
|
|
| Code | | When |
|
|
|---|---|---|
|
|
| `snat_to` | error | `to` isn't one address, or of another family than the rule (give the rule the family) |
|
|
| `snat_everything` | warning | no out zones and no sources: everything leaving the firewall is translated |
|
|
| `redirect_service` | error | no service, or one without TCP, UDP or SCTP ports |
|
|
| `redirect_to_port` | error | the local port isn't one port |
|
|
|
|
## The machine
|
|
|
|
These come from housecarld looking at the machine, not at the
|
|
configuration: the dashboard and `hc status` show them.
|
|
|
|
| Code | | When |
|
|
|---|---|---|
|
|
| `nftables_service` | warning, *host* | Debian's `nftables.service` is enabled: at boot it loads `/etc/nftables.conf`, and stopping or restarting it runs `nft flush ruleset`, deleting Housecarl's table too. Disable it with `systemctl disable nftables` (without `--now`) |
|
|
| `takeover_pending` | warning, *host* | the table you imported from is still in force beside Housecarl's, so a packet must get past both; `hc takeover TABLE` deletes it once Housecarl's rules are right |
|