Boot fallback, flowtable devices that come and go, healthz, renames

The boot unit fails closed: if ruleset.nft doesn't load, it loads
ruleset-fallback.nft (render.Fallback), saved beside it from the same
configuration: nothing new in from external zones, established, the
ICMPv6 IPv6 needs and DHCPv6 replies let in, the inside left alone and
masqueraded, and the unit marked failed with a critical message.
housecarld replaces it when it applies; status, the dashboard and
/healthz say while it is in force. no_external_zone now also says the
fallback closes everything without an external zone.

The boot ruleset has no flowtable: nftables on Debian 13's kernel
refuses one naming a device that doesn't exist yet (ppp0 before PPPoE),
and with it the whole file. internal/linkwatch listens to rtnetlink link
messages; housecarld applies again when a flowtable device appears or
comes back as a new interface (by ifindex), under the engine's lock.
docs/flowtable.md: what it does, when to use it and when not, how.

GET /healthz reports firewall (applied, unconfigured, fallback,
missing), changes on probation and the machine's warnings, with 503
unless in force, read at most every five seconds.

Renaming a zone, set or service changes what names it in the same
change (one history entry, one undo); dynamic sets keep their names.
The importer keeps a chain's head in order: once a would-be built-in
stays a rule, the ones after it do too, so the maintainer's ICMPv6
accepts stay behind the invalid drop as written. hc settings set
--flowtable none empties the list.

make test (nft 1.1.3) and make e2e (242 checks, with VLAN
sub-interfaces, the fallback and the flowtable) pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
abrown38andClaude Opus 5.5 committed 2026-10-19 23:16:46 +01:00
1 parent 835a9ba2cc
commit 9d766b9823
38 files changed
+1335 -84

No files matched your search

+75 -2
View File
@@ -71,14 +71,63 @@ sudo hc history undo 42
A confirmed ruleset (or one changed through the socket) is saved to
`/var/lib/housecarl/ruleset.nft`, which `housecarl-ruleset.service`
loads before the network comes up. To see what would load:
loads before the network comes up, so before PPPoE, WireGuard or DHCP
bring up their interfaces. To see what would load:
```sh
sudo cat /var/lib/housecarl/ruleset.nft
sudo nft -c -f /var/lib/housecarl/ruleset.nft # check it
```
Dynamic sets start empty after a reboot.
Dynamic sets start empty after a reboot. The saved ruleset has no
flowtable, since its devices may not exist yet: housecarld adds it when
it starts, and whenever a flowtable device appears
([flowtable.md](flowtable.md#devices-that-come-and-go)).
### If the ruleset fails to load at boot
Should `ruleset.nft` fail to load (a damaged file, an nftables or kernel
upgrade that refuses something in it), the machine would otherwise boot
with no firewall at all until housecarld starts. Instead,
`housecarl-ruleset.service` loads a **fallback** that housecarld saves
beside it, `/var/lib/housecarl/ruleset-fallback.nft`, from the same
confirmed configuration:
* **Nothing new gets in from the external zones' interfaces**, to the
firewall or through it: no port forwards, no SSH or web UI from the
internet.
* What the firewall and the inside need to keep working is let in:
established connections, the ICMPv6 IPv6 needs (neighbour discovery,
router advertisements, packet too big), and DHCPv6 replies from
link-local addresses.
* **Everything else is let through**: the LAN, WireGuard and the
firewall itself are reachable from the inside, so you can log in and
fix it.
* IPv4 leaving an external zone is masqueraded (when any source NAT is
configured), so the inside still reaches the internet. MSS clamping
stays, if it is on.
PPPoE itself isn't affected: its discovery and session are Ethernet
frames, and the PPP link's negotiation happens before any IP, neither
of which an `inet` table sees.
The fallback only knows which interfaces face the internet from the
zones marked **external**. With none, it lets nothing but loopback
through, and the warning `no_external_zone` says so.
When the fallback loads, the unit is marked failed, and the journal says
why (as a critical message):
```sh
systemctl status housecarl-ruleset
journalctl -b -u housecarl-ruleset
```
housecarld puts the real ruleset back when it starts, and saves a fresh
`ruleset.nft`. If it can't apply it either, the fallback stays in force:
`hc status` and the dashboard say "NOT in force: the boot fallback is in
force", and `/healthz` answers 503 ([Monitoring](#monitoring)). Fix
what `hc status` or the journal reports, then restart housecarld.
Keep Debian's `nftables.service` disabled (`systemctl disable
nftables`, without `--now`): at boot it loads `/etc/nftables.conf`, and
@@ -224,6 +273,30 @@ database is kept next to it as `housecarl.db.before-restore-<time>`.
database when it starts, and an older one refuses a database a newer one
has upgraded: restore a backup to go back.
## Monitoring
`GET /healthz` needs no login, and answers within a few seconds of any
change (it reads the ruleset at most every five seconds, however often
it is asked):
```sh
$ curl -s https://firewall.example.net:8070/healthz
{"ok":true,"version":"0.1.0","firewall":"applied","probation":0,"warnings":[]}
```
| Field | |
|---|---|
| `ok` | `true` with HTTP 200 when the firewall is as it should be; `false` with HTTP **503** otherwise |
| `firewall` | `applied` (Housecarl's ruleset is in force), `unconfigured` (nothing configured yet, so nothing to apply), `fallback` (the [boot fallback](#if-the-ruleset-fails-to-load-at-boot) is in force), `missing` (no Housecarl table: flushed, or never applied) |
| `probation` | changes waiting to be confirmed, rolled back unless they are |
| `warnings` | codes of problems with the machine, as `hc status` shows them (`nftables_service`) |
For Checkmk, an HTTP check on the URL that expects status 200 (and,
if you like, the string `"firewall":"applied"`) covers housecarld being
down, the fallback and a missing ruleset. Reach it from where the input
rules let the monitor in (a rule for the Checkmk server's address and
port 8070).
## Checks
`housecarld check` validates the configuration in the database against