Changesets, as in Jarl: configuration changes are operations that can be staged in a changeset, checked as they are added (validation and nft -c), reviewed with the diff of the ruleset, and applied together as one history entry, then on probation like any change made over the network. The web UI collects edits for review by default (pending rows, a bar with Review and Apply now, a Changes page, a switch for live edits); hc stages with -C ID or HOUSECARL_CHANGESET (checked like JARL_CHANGESET) and has hc changeset create/list/show/diff/apply/discard/remove. Undo, import and takeover refuse a changeset. --dry-run shows the ruleset's diff. Bandwidth and quotas: rate limits in bytes, per source or destination; quotas per rule or per address that reset daily, weekly, monthly on a day, or by hand, with their usage carried across reloads, saved for reboots, shown by hc quotas and on the rules page, and reset with hc rule reset-quota. Rules that count traffic are checked before established connections are accepted (so they see every packet) and can't accept. Per-address limits group IPv6 by /64 by default (--group-v4/-v6). Per-source rate meters use update. The importer turns meters, byte rates and quotas into these fields. The examples in the web UI, hc's help and the docs use documentation addresses and generic names instead of the maintainer's network. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
121 lines
5.5 KiB
Markdown
121 lines
5.5 KiB
Markdown
# The web UI's certificate
|
|
|
|
Housecarl can serve its web UI and API over HTTPS in two ways:
|
|
|
|
* **A certificate you manage**: `tls.cert_file` and `tls.key_file` in
|
|
`housecarl.yaml`. housecarld rereads them when they change
|
|
([configuration.md](configuration.md#https)).
|
|
* **A certificate it gets itself** from Let's Encrypt (or another ACME CA),
|
|
with Jarl publishing the DNS-01 challenge. That's this page.
|
|
|
|
With DNS-01 the web UI doesn't need to be reachable from the internet.
|
|
`fw.adbraun.lol` can point at a private address and the certificate
|
|
still works on your network. Only the challenge record has to be visible
|
|
on the internet, and Jarl puts it in every view that has your zone.
|
|
|
|
## Setting it up
|
|
|
|
1. **Connect Housecarl to Jarl** with a user and a token that may only write
|
|
Housecarl's own challenge record: [jarl.md](jarl.md).
|
|
2. **Give your web UI a name in DNS**, such as `fw.adbraun.lol` with the
|
|
Housecarl server's address, in Jarl. It is the name you will type in the
|
|
browser.
|
|
3. **Configure the certificate** in `/etc/housecarl/housecarl.yaml`:
|
|
|
|
```yaml
|
|
listen: ["192.168.1.2:8070"] # an address your browser can reach
|
|
tls:
|
|
acme:
|
|
domain: fw.adbraun.lol
|
|
email: you@adbraun.lol # Let's Encrypt writes here if something is wrong
|
|
directory: letsencrypt-staging # first; see below
|
|
agree_tos: true # you accept Let's Encrypt's terms of service
|
|
jarl:
|
|
server: https://dns.adbraun.lol:8053
|
|
token_file: /etc/housecarl/jarl.token
|
|
```
|
|
|
|
4. **Restart housecarld** (`sudo systemctl restart housecarl`), and check:
|
|
|
|
```sh
|
|
sudo hc cert check
|
|
```
|
|
|
|
5. **Watch it get the certificate:** `sudo hc cert`, or Server →
|
|
Certificate in the web UI. Then open `https://fw.adbraun.lol:8070/`.
|
|
6. **Switch to the real CA.** The staging certificate works, but browsers
|
|
warn about it, because Let's Encrypt's staging service isn't trusted.
|
|
It's there to prove everything works without touching Let's Encrypt's
|
|
rate limits. Once it does, set `directory: letsencrypt`, restart, and
|
|
Housecarl gets a trusted certificate right away.
|
|
|
|
Until the first certificate arrives, the web UI is served over plain
|
|
HTTP. housecarld switches to HTTPS on the same port without a restart. From
|
|
then on, plain HTTP is redirected to HTTPS, except from the server itself
|
|
(`127.0.0.1`), so tools there keep working.
|
|
|
|
## Checking
|
|
|
|
Before every certificate order, and whenever you ask (`hc cert
|
|
check`, or Test in the web UI), Housecarl checks the whole path without
|
|
asking the CA. Let's Encrypt allows only 5 failed validations per name
|
|
per hour, so a broken setup shouldn't find out the hard way.
|
|
|
|
| Step | What it checks |
|
|
|---|---|
|
|
| Jarl | Jarl answers at `jarl.server`, and the token is valid (it says whose it is) |
|
|
| Zone | a primary zone the token can see holds `_acme-challenge.<domain>`, and in which views |
|
|
| Permission | the token may write the TXT record there (Jarl checks it as a dry run) |
|
|
| Write | a test record with a random value is written through Jarl, which loads it into named |
|
|
| Name servers | each of the zone's public name servers serves it, asked directly as the CA's resolvers will. A name server passes if any of its addresses does (`tls.acme.check_propagation`) |
|
|
| Public resolvers | the resolvers in `tls.acme.resolvers` (1.1.1.1, 9.9.9.9, 8.8.8.8) see it. If the name servers do but a resolver doesn't, it is only a warning: it may be holding an earlier answer |
|
|
| Clean up | the test record is removed |
|
|
|
|
Each step says what went wrong and what to do. For example:
|
|
|
|
```
|
|
ok Jarl the token is housecarl's, on Jarl v0.6.1
|
|
ok Zone adbraun.lol in views internal, external
|
|
FAIL Permission the token may not write the challenge record (...); give it the grant:
|
|
records:write zones=adbraun.lol names=_acme-challenge.fw.adbraun.lol types=TXT
|
|
```
|
|
|
|
The checks wait up to 90 seconds for the record to appear. A failing step
|
|
lists the DNS questions it asked, as `dig` commands, so you can ask them
|
|
yourself.
|
|
|
|
If the challenge name is a CNAME into another zone, the check follows it,
|
|
as Let's Encrypt does.
|
|
|
|
## Renewal
|
|
|
|
A certificate is renewed when a third of its lifetime is left: 30 days
|
|
before the end for Let's Encrypt's 90-day certificates, 2 days for 6-day
|
|
ones. housecarld looks twice a day. The check runs first every time, and
|
|
the new certificate is used at once, without a restart.
|
|
|
|
If renewing fails, the old certificate keeps being served, and housecarld
|
|
tries again after 1 hour, then 3, 6 and every 12. The dashboard shows a
|
|
problem (`certificate_failed`), and `certificate_expiring` once less than
|
|
14 days are left. `hc cert renew` (Renew now in the web UI) tries
|
|
again at once.
|
|
|
|
The ACME account and the certificate, with its key, are kept in Housecarl's
|
|
database, so backups include them.
|
|
|
|
## Settings
|
|
|
|
All under `tls.acme` in `housecarl.yaml` ([configuration.md](configuration.md)):
|
|
|
|
| Setting | Default | |
|
|
|---|---|---|
|
|
| `domain` | none (off) | the name on the certificate |
|
|
| `email` | none | the account's contact address |
|
|
| `directory` | `letsencrypt-staging` | `letsencrypt`, `letsencrypt-staging`, or an ACME directory URL (a private CA such as step-ca) |
|
|
| `agree_tos` | `false` | accept the CA's terms of service; no account is made without it |
|
|
| `resolvers` | `1.1.1.1:53`, `9.9.9.9:53`, `8.8.8.8:53` | public resolvers, to see the challenge as the CA does |
|
|
| `check_propagation` | `true` | also ask each of the zone's name servers directly |
|
|
| `ca_bundle` | none | extra CAs to trust for the ACME server itself |
|
|
|
|
`tls.cert_file` and `tls.acme` can't both be set.
|