A set can take its addresses from Jarl: every A and AAAA record of its
names (NAME@VIEW), CNAMEs followed within Jarl's zones in the same view.
housecarld reads them every jarl.refresh and after each change, keeps
only the addresses within the zones and networks the set allows, and
keeps what it last read (saved in the database and the boot ruleset)
while Jarl can't be reached. A change of the addresses applies the
ruleset again without a history entry and is audited (set.jarl).
hc set add --jarl --within, hc set jarl; the kind "from Jarl" and
Addresses… on the Sets page; GET /sets/{name}/jarl and /jarl/refresh;
validation codes set_jarl_*, status warnings jarl_*; docs/jarl.md.
e2e phase with scripts/fakejarl.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
86 lines
5.6 KiB
Markdown
86 lines
5.6 KiB
Markdown
# Configuration file
|
|
|
|
`/etc/housecarl/housecarl.yaml` says how housecarld runs. The firewall's
|
|
configuration (zones, sets, rules, NAT, settings) isn't here; it is in
|
|
the database, changed with the CLI, the API or the web UI.
|
|
|
|
`housecarld config --example` prints a commented file with the defaults.
|
|
Every setting is optional; unknown settings are an error. housecarld
|
|
reads the file when it starts.
|
|
|
|
| Setting | Default | |
|
|
|---|---|---|
|
|
| `listen` | `["127.0.0.1:8070"]` | addresses of the web UI and API (`host:port`). Keep them on localhost or a management network, and write an input rule that lets you reach them |
|
|
| `tls.cert_file`, `tls.key_file` | none | serve HTTPS with this certificate; housecarld rereads it when the file changes, or on `systemctl reload housecarl` |
|
|
| `tls.acme.*` | off | get the certificate from Let's Encrypt, with Jarl publishing the challenge ([certificates.md](certificates.md#settings)) |
|
|
| `jarl.server`, `jarl.token_file` (or `jarl.token`), `jarl.ca_cert`, `jarl.insecure` | none | the Jarl server for the certificate's challenge and the sets from Jarl, and its token ([jarl.md](jarl.md)) |
|
|
| `jarl.refresh` | `1m` | how often the sets from Jarl read their names (10s or more; [jarl.md](jarl.md#when-the-addresses-change)) |
|
|
| `ruleset` | `/var/lib/housecarl/ruleset.nft` | the confirmed ruleset, which `housecarl-ruleset.service` loads at boot; its fallback is saved beside it as `ruleset-fallback.nft` ([operations.md](operations.md#if-the-ruleset-fails-to-load-at-boot)). Change both paths in the unit too |
|
|
| `trusted_proxies` | `[]` | addresses or prefixes of reverse proxies whose `X-Forwarded-For` and `X-Forwarded-Proto` are believed |
|
|
| `socket` | `/run/housecarl/housecarl.sock` | the local API socket; `""` turns it off |
|
|
| `database` | `/var/lib/housecarl/housecarl.db` | the SQLite database: configuration, history, users, audit log |
|
|
| `log.level` | `info` | `debug` (every HTTP request), `info`, `warn` or `error` |
|
|
| `log.format` | `text` | `text` or `json` |
|
|
| `session.lifetime` | `12h` | how long a web UI login lasts |
|
|
| `session.idle_timeout` | `2h` | logged out after this long without use |
|
|
| `session.cookie_secure` | `auto` | `auto` (secure over HTTPS), `always` or `never` |
|
|
| `backup.dir` | `/var/backups/housecarl` | where `housecarld backup` writes |
|
|
| `backup.retention` | `30d` | backups older than this are deleted… |
|
|
| `backup.keep_at_least` | `7` | …keeping at least this many |
|
|
| `stats.enabled` | `true` | keep traffic, drops, hits, blocked sources, connections and quotas over time ([stats.md](stats.md)) |
|
|
| `stats.database` | `/var/lib/housecarl/stats.db` | the statistics' own database; deleting it is safe |
|
|
| `stats.retention.minute`, `.hour`, `.day` | `7d`, `90d`, `730d` | how long per-minute, hourly and daily figures are kept |
|
|
| `stats.top_n` | `100` | entries kept per top list, per hour and per day |
|
|
| `shadow.enabled` | `false` | shadow mode: Housecarl's ruleset is loaded beside the firewall you have, but blocks, rejects and translates nothing; its counters, log and tracer show what it would do. No change is on probation, and takeovers and closing connections are refused ([shadow.md](shadow.md)) |
|
|
| `shadow.position` | `after` | where Housecarl's filter chains run in shadow mode: `after` the other firewall's (`filter + 10`: it sees only what that one lets through, so what it would drop is what switching would break) or `before` them (`filter - 10`: it sees every packet) |
|
|
| `names.enabled` | `true` | show inside addresses' names beside them, found by reverse lookups (below) |
|
|
| `names.resolver` | `""` | the DNS server to ask, such as `10.10.0.1:53`; empty: the system's (`/etc/hosts`, then `/etc/resolv.conf`) |
|
|
| `names.cache` | `10m` | how long a name, or the lack of one, is kept |
|
|
|
|
Durations are written `15s`, `12h` or `30d`.
|
|
|
|
Shadow mode is here, and not a firewall setting, so that no user or API
|
|
token can switch enforcement off; restart housecarld to switch.
|
|
|
|
### Names
|
|
|
|
The web UI and `hc` show a name beside each **inside** address in
|
|
connections, the log, quotas and traffic per device: `laptop
|
|
192.168.1.20`. Housecarl finds it with a reverse (PTR) lookup, so the
|
|
names are your reverse zones' (and `/etc/hosts`'). An address counts as
|
|
inside when it is in a network connected to an inside zone's interface,
|
|
or private (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `fc00::/7`)
|
|
and not in an external zone's network. Outside addresses get no name:
|
|
their reverse zones belong to whoever holds the addresses, who would
|
|
choose the names, and looking up every scanner is slow.
|
|
|
|
Names are looked up when a page or `hc` asks, cached (a failed lookup is
|
|
tried again after a minute), and never stored: statistics stay by
|
|
address. With a split DNS (views), the server asked must answer the
|
|
firewall as an inside client; set `names.resolver` if the system's
|
|
resolver doesn't. IPv6 traffic per device is counted per /64, which has
|
|
no name. `hc --no-names` leaves them out.
|
|
|
|
The probation's length is a firewall setting, not here: `hc settings set
|
|
--probation 5m`. So is counting traffic per device for the statistics:
|
|
`hc settings set --stats-per-device on`.
|
|
|
|
## HTTPS
|
|
|
|
Over plain HTTP, passwords and tokens cross the network in the clear;
|
|
housecarld warns when it listens on a non-loopback address without TLS.
|
|
Let it get its own certificate through Jarl ([certificates.md](certificates.md)),
|
|
or give it one:
|
|
|
|
```yaml
|
|
listen: ["192.168.1.1:8070"]
|
|
tls:
|
|
cert_file: /etc/housecarl/tls/fullchain.pem
|
|
key_file: /etc/housecarl/tls/privkey.pem
|
|
```
|
|
|
|
Or keep it on `127.0.0.1` behind a reverse proxy that terminates TLS, and
|
|
list the proxy in `trusted_proxies`. With a certificate, the same port
|
|
answers HTTPS and redirects plain HTTP to it, except from the firewall
|
|
itself.
|