Files
housecarl/docs/operations.md
T
abrown38andClaude Opus 5.5 a45c893830 Connections, "every packet" rules, and a packet tracer
- Connections: the kernel's tracked connections, read and closed over
  ctnetlink (internal/conntrack, raw netlink like the drop log), with
  their NAT and TCP state; closing one makes the rules check its next
  packet again (GET /connections, POST /connections/close; hc
  connections; Traffic → Connections).
- Rules can check every packet (every_packet, --every-packet): before
  established connections are accepted, so a drop or reject also stops
  connections already open. They can't accept.
- A packet tracer (internal/trace): a table of its own, inet
  housecarl-trace, marks the packets a filter picks, `nft monitor trace`
  reports every rule and policy they meet in every table, and the tracer
  names what accepted or dropped them, Housecarl's rules by name (POST,
  GET, DELETE /trace; hc trace; Traffic → Trace). One trace at a time,
  at most 200 packets and 20 a second; the table goes when it ends.

e2e: 157 checks, among them an upload a normal block doesn't stop and an
every-packet one does, and a trace naming the blocking rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 20:22:34 +01:00

8.6 KiB

Running Housecarl

The services

systemctl status housecarl          # housecarld: the API, the web UI, the log
journalctl -u housecarl -f
systemctl reload housecarl          # reread the TLS certificate
systemctl status housecarl-ruleset  # the ruleset loaded at boot

The firewall itself is the kernel's: it stays in force while housecarld is stopped, restarted or upgraded. Without housecarld there is no API, no web UI, no log and no probation timer.

housecarld logs each change it applies, rollbacks, and what it couldn't apply. When it starts, it applies the configuration again (rolling back first what was on probation when it stopped).

Reviewing before applying

In the web UI, edits are collected as pending changes (the bar under the header), and Changes shows them with the diff of the ruleset; Apply puts them in force together. The switch in the header (Review edits / Live edits) applies each edit at once instead. With hc, see cli.md.

Changes on probation

Changes made over the network roll back unless confirmed (design.md). The web UI and hc confirm their own changes once they reach Housecarl again over a new connection, so normally nothing waits:

$ hc rule add input --from admins --service ssh
created rule input #31
applied and confirmed (history 12): this client reached Housecarl again over a new connection

A change that cuts the client off can't be confirmed that way:

applied on probation (history 13), but couldn't confirm it: … context deadline exceeded
It is rolled back within 1m50s unless confirmed ('hc confirm')

By hand (with --no-confirm, or with Settings → "a person confirms each change"):

hc status              # Probation: 2 changes roll back in 97s unless confirmed
hc confirm
hc rollback            # or undo them now

The web UI shows a banner with a countdown and both buttons. A rollback is in the history, as an undo by housecarl via rollback, so it can be undone like any change.

If you are cut off by a change: wait. When the time is up, the previous ruleset comes back by itself. If you are cut off by a confirmed change, use the console, or SSH from a source the rules still let in:

sudo hc history list
sudo hc history undo 42

The ruleset at boot

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:

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.

Keep Debian's nftables.service disabled (systemctl disable nftables, without --now): at boot it loads /etc/nftables.conf, and stopping or restarting it runs nft flush ruleset, which deletes every table, Housecarl's included. housecarl-ruleset.service runs after it at boot, which covers the ordinary case, but not a restart of nftables.service later. Housecarl never reads or writes /etc/nftables.conf; its ruleset is saved in /var/lib/housecarl/ruleset.nft. The dashboard and hc status warn while nftables.service is enabled.

Other programs' tables are left alone: fail2ban's (inet f2b-table), Docker's, Incus's. Housecarl replaces only its own table, so they keep their rules through every Housecarl change and restart, and nothing needs restarting in a particular order. A packet must get past every table on its hook, so a drop in any of them wins.

Hits

Every rule, NAT rule, built-in and policy has a counter. hc rule list, hc forward list and the web UI show packets; hc counters shows them all, with bytes. They count from housecarld's start, and carry across changes to the ruleset.

Quotas

hc quotas (and the rules page) show what each quota used and when it resets; hc rule reset-quota ID starts one again. housecarld resets quotas at midnight when their period ends (late, if it was stopped then), and saves their usage every five minutes and when it stops; after a reboot it puts it back. A reboot without a clean stop loses at most the last five minutes' count.

The log

Rules with --log (Log in the web UI) and, with hc settings set --log-drops on, what the input and forward policies drop (at most 10 packets a second) are logged:

hc log -f
hc log --search 198.51.100.7
10:02:13  hc input #95 drop:     wan  -   198.51.100.7:53868 -> 203.0.113.10:22   tcp syn
10:02:15  hc forward policy drop: wan  eth1  198.51.100.9:443 -> 192.168.1.20:51432  tcp

The packets go to netlink log group 4171, where housecarld reads them; the last 2000 are kept in memory. They aren't written to the kernel log or the journal. A rule's log isn't rate-limited: on a busy rule, give it a limit, or log only new connections (--state new).

Connections

The built-in that accepts established connections lets their packets through without checking the rules again: a rule added later doesn't stop a connection already open. Traffic → Connections (hc connections) lists what the kernel tracks, with NAT (to 192.168.1.10:443: a port forward; as 203.0.113.10: masqueraded) and the time left:

hc connections --search 192.168.1.50
hc connections --search ":443 tcp"
hc connections close --search 192.168.1.50

Closing a connection deletes its tracking entry: its next packet is checked by the rules as they are now. One they block is stopped; one they allow carries on (TCP connections are picked up again mid-stream).

To make a block stop open connections by itself, tick "every packet" on the rule (--every-packet): it is checked before established connections are accepted, like rules with a quota (rules.md).

Tracing

Traffic → Trace (hc trace) follows packets through the whole ruleset: Housecarl's rules and every other table (Docker's, fail2ban's, an old inet main), each rule and policy they meet, and what accepted or dropped them:

$ hc trace --address 192.168.1.50 --port 443 --seconds 60
14:02:13.120  192.168.1.50:51432 -> 198.51.100.9:443  tcp syn in eth1 out wan
    inet housecarl forward: built-in: mss-clamp -> continue
    inet housecarl forward: rule forward #12 (kids at night) -> drop
    => DROPPED by rule forward #12 (kids at night)

It traces packets from or to the address, on the port (either way), for 5 to 600 seconds, at most 200 packets and 20 a second. It works with nftables' own tracing: a table of its own (inet housecarl-trace, hooked before everything else) marks the packets, and nft monitor trace reports them; the table goes when the trace ends. One trace runs at a time.

When something doesn't get through

  1. Trace it (above): it names the rule or policy that dropped it, in whichever table.
  2. hc log -f with --log-drops on: is it dropped by a policy (no rule accepted it), or by a rule (its number is in the line)?
  3. hc rule list forward (or input): hits on the rule you expect?
  4. hc ruleset: the rule as nftables sees it, with the zones' interfaces and the sets' families.
  5. hc status: other tables. A Docker or Incus table, or an old one, can drop what Housecarl accepts.
  6. Port forwards: hc forward list hits, and the inside host's own firewall and default route (answers must go back through Housecarl).

Backups

housecarl-backup.timer runs housecarld backup daily. It writes a consistent copy of the database to backup.dir (/var/backups/housecarl/housecarl-<time>.db), and deletes those older than backup.retention (30 days), keeping at least backup.keep_at_least (7). A backup holds everything: configuration, history, users, tokens (hashed) and the audit log.

sudo -u housecarl housecarld backup                 # now
sudo -u housecarl housecarld backup /tmp/h.db       # to a file of your choice
sudo systemctl disable --now housecarl-backup.timer # no daily backups

To restore, stop housecarld first; restore refuses to replace the database under it:

sudo systemctl stop housecarl
sudo -u housecarl housecarld restore /var/backups/housecarl/housecarl-20261014T020000Z.db
sudo systemctl start housecarl

housecarld applies the restored configuration when it starts. The old database is kept next to it as housecarl.db.before-restore-<time>.

Upgrades

apt upgrade restarts a running housecarld. A new version upgrades the database when it starts, and an older one refuses a database a newer one has upgraded: restore a backup to go back.

Checks

housecarld check validates the configuration in the database against this machine's interfaces, the same checks as hc status shows (validation.md).