Build Housecarl 0.1.0: the nftables firewall manager

Zones, address sets (IPv4 and IPv6 mixed, dynamic sets with timeouts) and
services, ordered rules in the early, input, forward and output chains,
port forwards with hairpin NAT and IPv6 pinholes, source NAT, redirects,
and switchable built-ins (loopback, conntrack, ICMPv6, antispoofing, MSS
clamping, DHCPv6 client, flowtable). Every change is validated, checked
with nft -c and applied in one transaction to Housecarl's own table,
inet housecarl; changes made over the network roll back unless
confirmed, and the confirmed ruleset is loaded at boot by
housecarl-ruleset.service. An empty configuration applies nothing.

Also: hit counters and the drop log (nflog), 'hc import' of an existing
nftables ruleset and 'hc takeover', the hc CLI, the REST API and the web
UI, users, scoped tokens (sets:write:NAME), the audit log, the web UI's
certificate through Jarl, Debian packages, docs, unit tests with real
nft 1.1.3, and an e2e test with real traffic (71 checks).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
abrown38andClaude Opus 5.5 committed 2026-10-19 07:57:14 +01:00
1 parent 54beb0bba3
commit 34292eeebb
118 files changed
+22165 -114

No files matched your search

+29 -8
View File
@@ -1,8 +1,9 @@
# Working on Housecarl
Notes for coding agents (and people) changing this repository. Read this
first, then [docs/design.md](docs/design.md), the plan and its open
questions.
first, then [docs/design.md](docs/design.md) (how it fits together and
what 0.1 leaves out) and [docs/development.md](docs/development.md)
(layout, tests, releasing).
Housecarl is a firewall manager for Linux: `housecarld` keeps the
machine's nftables ruleset and serves a REST API and a web UI; `hc` is the
@@ -12,9 +13,6 @@ their ways: the same auth, socket and web UI helpers, the same release
process, the same look. When in doubt, do it the way Jarl and Steward do.
Go module `housecarl`, `CGO_ENABLED=0`, Linux only for housecarld.
Housecarl is at its start: only the version commands, the build and the
brand exist. Most rules below are for what is coming.
## Ground rules
* **The database is the source of truth.** The ruleset is rendered from
@@ -28,11 +26,17 @@ brand exist. Most rules below are for what is coming.
* **Housecarl's own table only.** Write `inet housecarl` and nothing
else; never flush the whole ruleset (Docker, libvirt and others keep
theirs).
* **Don't lock the user out.** Changes to the rules for the machine itself
are applied on probation and rolled back unless confirmed
(docs/design.md).
* **Don't lock the user out.** Changes made over the network are applied
on probation and rolled back unless confirmed (docs/design.md). An
empty configuration applies nothing. But never force a rule that keeps
SSH or the web UI open: the maintainer doesn't want one.
* **Stay within nftables 1.1.3** (Debian 13). The dev machine's newer nft
accepts things 1.1.3 refuses; the tests with `HOUSECARL_NFT` and the
e2e test use 1.1.3.
* **Every user-visible change updates `CHANGELOG.md`** under
`## [Unreleased]`, and the matching `docs/` page in the same change.
New validation codes get a case in `internal/validate/validate_test.go`
and a row in `docs/validation.md`.
* **Errors** say what is wrong and how to fix it, naming the object.
* **Schema changes** append a migration; never edit a shipped one.
* `make test` runs `go vet` and a `gofmt -l` check. `goimports` isn't
@@ -56,8 +60,25 @@ brand exist. Most rules below are for what is coming.
```sh
make test # vet + gofmt check + unit tests
make e2e # real traffic through a router container (needs Docker)
```
* **Docker** on this machine needs `DOCKER_HOST=unix:///var/run/docker.sock`.
`docker build` can't reach the network here: images are made with
`docker run` + `docker commit` (scripts/e2e.sh makes `housecarl-nft`).
The e2e test prints `RESULT: N passed, 0 failed`; run it after any
change to `render`, `nft`, `engine`, `server`, `importer` or the CLI.
* **Real nft in unit tests:**
`HOUSECARL_NFT="docker run --rm -i --cap-add NET_ADMIN housecarl-nft nft"`
and `HOUSECARL_NFT_IMAGE=housecarl-nft`; without them those tests skip.
Unprivileged namespaces (`unshare -rn`) are blocked here by AppArmor.
* **The systemd sandbox is only tested by a package test**: `make deb`,
installed in a `debian:trixie` container running systemd
(`--privileged --cgroupns=host /sbin/init`).
* **The web UI** has no JS tests. Bundle `assets/app.js` with esbuild to
catch syntax and import errors, and look at it in headless Chrome
against the e2e lab (`KEEP=1`, web UI on `10.88.10.1:8070`).
## Web UI rules
As in Jarl and Steward: