tls.acme names the certificate and jarl: the Jarl server and a token that may only write the challenge record. Steward gets the certificate with DNS-01 (lego), writing the record through Jarl's API into every view that has the zone, renews it with a third of its lifetime left and swaps it in without a restart. A check runs before every order: Jarl, the zone, the token's permission, a test record, the zone's name servers, public resolvers, clean-up; each step says what to fix. Staging by default. With a certificate, each listener serves HTTPS and plain HTTP on one port (redirecting plain HTTP, except from the server itself). steward cert / cert check / cert renew, Server -> Certificate in the web UI, GET /certificate, POST /certificate/check|renew. docs/certificates.md and docs/jarl.md (the Jarl user and the token's grant). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
86 lines
3.3 KiB
Markdown
86 lines
3.3 KiB
Markdown
# Connecting Steward to Jarl
|
|
|
|
Steward uses [Jarl](https://gitea.adbraun.lol/adam/jarl), the BIND
|
|
manager, for its DNS records. In this version that means one thing: the
|
|
DNS-01 challenge for the web UI's certificate ([certificates.md](certificates.md)).
|
|
Registering clients' names is planned to use the same connection.
|
|
|
|
Steward talks to Jarl's REST API with an API token. Give it a **user of its
|
|
own**, with **only the permission it needs**.
|
|
|
|
## 1. A user for Steward, in Jarl
|
|
|
|
On the Jarl server, make a user without a password. It can't log in to
|
|
Jarl's web UI, only use its tokens. Grant it the challenge record of
|
|
Steward's own name, and nothing else:
|
|
|
|
```sh
|
|
jarl user create steward --no-password --comment "Steward on dhcp01" \
|
|
--grant "records:write zones=adbraun.lol names=_acme-challenge.dhcp.adbraun.lol types=TXT"
|
|
```
|
|
|
|
Use your zone for `adbraun.lol`, and the name Steward's web UI is reached
|
|
by for `dhcp.adbraun.lol` (`tls.acme.domain`). The grant lets the token add
|
|
and remove TXT records at exactly `_acme-challenge.dhcp.adbraun.lol`:
|
|
|
|
* nothing else in the zone (`names=`);
|
|
* no other record type (`types=TXT`);
|
|
* no other zone (`zones=`).
|
|
|
|
It can see that the zone exists, which it needs to find it, but it can't
|
|
read the zone's other records.
|
|
|
|
Why not something wider:
|
|
|
|
* **Jarl's `acme` role** (`records:write names=_acme-challenge.* types=TXT`)
|
|
would let the token publish challenges for *any* name in *any* zone, and
|
|
so obtain certificates for all of them. Steward needs only its own name.
|
|
* **Your own account's token** would carry all your permissions, and
|
|
Steward's certificate would stop renewing the day your account changes.
|
|
A user per program keeps the two apart, and the audit log shows
|
|
`steward/...` for what Steward did.
|
|
|
|
If the challenge name is a CNAME into a zone kept for challenges, such as
|
|
`_acme-challenge.dhcp.adbraun.lol CNAME dhcp.acme.adbraun.lol.`, grant the
|
|
target instead: `zones=acme.adbraun.lol names=dhcp.acme.adbraun.lol`.
|
|
|
|
## 2. The token
|
|
|
|
Make the token, and copy it to the Steward server into a file only the
|
|
`steward` user can read:
|
|
|
|
```sh
|
|
jarl token create dhcp01 --user steward -q > steward.token # on the Jarl server, or with the jarl CLI anywhere
|
|
scp steward.token dhcp01:
|
|
ssh dhcp01 'sudo install -m 0600 -o steward -g steward steward.token /etc/steward/jarl.token && rm steward.token'
|
|
rm steward.token
|
|
```
|
|
|
|
A token file is better than putting the token in `steward.yaml`: the
|
|
configuration file is readable by the `steward` group, and gets copied
|
|
around more readily.
|
|
|
|
You can give the token an expiry (`--expires 365d`). Rotate it in Jarl with
|
|
`jarl token rotate ID --grace 1d`, then put the new secret in the file and
|
|
restart stewardd within the grace period. Since the certificate is renewed
|
|
every couple of months, nothing breaks while you do.
|
|
|
|
## 3. Steward's settings
|
|
|
|
In `/etc/steward/steward.yaml`:
|
|
|
|
```yaml
|
|
jarl:
|
|
server: https://dns.adbraun.lol:8053
|
|
token_file: /etc/steward/jarl.token
|
|
# ca_cert: /etc/steward/jarl-ca.pem # if Jarl's certificate isn't from a public CA
|
|
```
|
|
|
|
Then restart stewardd (`systemctl restart steward`). Steward needs to
|
|
reach Jarl's API over the network: Jarl's `listen` must include an
|
|
address Steward can reach, ideally over HTTPS.
|
|
|
|
`steward cert check` ([certificates.md](certificates.md#checking)) tests
|
|
the connection, the token's permission, and that the record reaches the
|
|
internet.
|