- 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>
232 lines
8.6 KiB
Markdown
232 lines
8.6 KiB
Markdown
# Running Housecarl
|
|
|
|
## The services
|
|
|
|
```sh
|
|
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](cli.md#changesets).
|
|
|
|
## Changes on probation
|
|
|
|
Changes made over the network roll back unless confirmed
|
|
([design.md](design.md#not-locking-yourself-out)). 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"):
|
|
|
|
```sh
|
|
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:
|
|
|
|
```sh
|
|
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:
|
|
|
|
```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.
|
|
|
|
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:
|
|
|
|
```sh
|
|
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:
|
|
|
|
```sh
|
|
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](rules.md#rules)).
|
|
|
|
## 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.
|
|
|
|
```sh
|
|
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:
|
|
|
|
```sh
|
|
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](validation.md)).
|