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

+87 -14
View File
@@ -4,7 +4,7 @@
*Every Jarl has a housecarl at his side; this one keeps the door.*
Housecarl will be a firewall for Linux that you manage through a web UI, a
Housecarl is a firewall for Linux that you manage through a web UI, a
REST API and a command line client (`hc`), all three backed by the same
API. It is the third of a set with [Jarl](https://gitea.adbraun.lol/adam/jarl),
the BIND manager, and [Steward](https://gitea.adbraun.lol/adam/steward), the
@@ -17,24 +17,97 @@ door and decides who comes in. Housecarl does that for your network. It
keeps the machine's nftables ruleset, built from its own database, in one
table of its own.
## What it will do
## What it does
* **Zones** for your interfaces and VLANs (`lan`, `iot`, `guest`, `wan`),
with a default policy between each pair, and rules between them.
* **Address sets** that rules refer to, whose entries can expire by
themselves; another program can be allowed to change just one set. That
is how Steward's captive portal will let a guest in.
* **NAT**: masquerading and port forwards.
* **Changes you can trust**: validated as a whole, checked by nftables
before they are applied, applied in one transaction, and undoable. A
change that could lock you out is rolled back unless you confirm it.
* **Zones, sets and services, then rules.** Name your interfaces (`wan`,
`lan`, `wg`), your addresses (`trusted`, IPv4 and IPv6 in one set) and
your ports (`jellyfin`, or the built-in `ssh`, `dns`, `https`…), and
write ordered rules with them: in, out or through the firewall, by
zone, address, service, connection state, time of day, rate per
source, or connection count. Anything else can be written as an
nftables rule by hand.
* **IPv4 and IPv6 together.** One rule covers both; Housecarl writes the
nftables rules each family needs.
* **NAT in one object.** A port forward is the DNAT, the forward accept
and the hairpin NAT together, and can open the same port to an IPv6
host. Masquerading, SNAT and redirects too.
* **The rules every firewall needs**, built in and each one switchable:
loopback, established connections, invalid packets, the ICMPv6 that
IPv6 needs, antispoofing for your prefixes, MSS clamping for PPPoE, the
DHCPv6 client, and a flowtable fast path.
* **Changes you can trust.** Each change is validated as a whole, checked
by nftables itself, and applied in one transaction. A change made over
the network is on probation: unless you confirm it within two minutes,
it rolls itself back, so a rule that cuts you off undoes itself.
* **Your existing ruleset, imported.** `hc import` turns an nftables
ruleset into zones, sets, services, rules and port forwards, keeping the
rules' order, and keeps what it can't translate as raw rules
([docs/import.md](docs/import.md)).
* **Easy to watch.** Hits per rule, a live log of what was dropped and
why, and the history of every change, field by field, with undo.
* **Its own table only.** Docker, Incus and libvirt keep theirs. The
confirmed ruleset is loaded at boot, before the network comes up, even
if housecarld doesn't start.
* **Sets other programs can fill.** A dynamic set's entries expire by
themselves, and a token can be limited to one set: that is how
Steward's captive portal will let a guest in.
* **Users, tokens and HTTPS** as in Jarl and Steward, with the web UI's
certificate from Let's Encrypt through Jarl
([docs/certificates.md](docs/certificates.md)).
See [docs/design.md](docs/design.md) for the plan and the open questions.
Not yet: rules by MAC address or DNS name, and iptables import
([docs/design.md](docs/design.md)).
## Quick start
On Debian 13, from the package registry (see
[docs/install.md](docs/install.md) for other ways):
```sh
sudo apt install housecarl
sudo -u housecarl housecarld init # asks for the admin password
sudo systemctl enable --now housecarl # applies nothing until configured
# Import the ruleset you have...
sudo hc import --defines /etc/nftables.conf -n # look first
sudo hc import --defines /etc/nftables.conf
sudo hc takeover "inet main"
sudo systemctl disable nftables.service
# ...or start from scratch: let yourself in first
sudo hc set add admins 10.0.10.0/24
sudo hc rule add input --from admins --service ssh
sudo hc zone add wan --interface ppp0 --external
sudo hc zone add lan --interface enp2s0
sudo hc rule add forward --in lan --out wan
sudo hc snat add --out wan
sudo hc status
```
The web UI is on `http://127.0.0.1:8070/` (see `listen` in
`/etc/housecarl/housecarl.yaml`); write a rule that lets you reach it.
## Documentation
* [install.md](docs/install.md): packages, building from source, first steps
* [rules.md](docs/rules.md): zones, address sets, services, rules and the built-ins
* [nat.md](docs/nat.md): port forwards, hairpin NAT, IPv6 pinholes, source NAT, redirects
* [import.md](docs/import.md): moving from an existing nftables ruleset
* [operations.md](docs/operations.md): probation, the boot, hits and the log, backups, troubleshooting
* [certificates.md](docs/certificates.md): HTTPS with a certificate from Let's Encrypt, through Jarl
* [jarl.md](docs/jarl.md): connecting Housecarl to Jarl, and what its token may do
* [cli.md](docs/cli.md): the `hc` command line client
* [api.md](docs/api.md): the REST API
* [configuration.md](docs/configuration.md): `/etc/housecarl/housecarl.yaml`
* [permissions.md](docs/permissions.md): users, roles, grants and API tokens
* [validation.md](docs/validation.md): every check and what it means
* [design.md](docs/design.md): how Housecarl is built, and what 0.1 leaves out
* [development.md](docs/development.md): building, testing, releasing
## Status
Housecarl is at its start: it has its name, its logo and the skeleton of
the daemon (`housecarld`) and CLI (`hc`). There is nothing to run yet.
Housecarl is heading for its first release, 0.1.0: everything above
works and is tested against real traffic, but it isn't released yet.
## Licence