Files
jarl/docs/configuration.md
T
adamandClaude Opus 5.5 c37de31f4c Prometheus metrics at /metrics, configured in jarl.yaml
New metrics: section (enabled, listen, allow, token). /metrics is served on
the main listeners, the local socket (root and jarld's user) and optional
metrics-only addresses; a scrape needs an allowed address or the bearer
token. Exports health (named up, problems, drift, backups, blocklists,
DNSSEC phases, token expiry) and, with stats on, named's and Jarl's query
counters since jarld started plus named's process and cache gauges.
docs/metrics.md has example queries and alerts checked with promtool.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:33:41 +01:00

222 lines
12 KiB
Markdown

# jarld configuration (`/etc/jarl/jarl.yaml`)
jarld reads `/etc/jarl/jarl.yaml` (or the file given with `-c/--config`,
which every jarld subcommand accepts). Every setting is optional. The
defaults suit Debian/Ubuntu with BIND 9.20. Unknown keys are an error, so
typos don't go unnoticed.
```sh
jarld config --example # a commented example file with every setting
jarld config # the effective configuration (file + defaults)
```
The CLI has its own small configuration file, described in [cli.md](cli.md).
## Settings
### Web UI and API
| Key | Default | Meaning |
|-----|---------|---------|
| `listen` | `["127.0.0.1:8053"]` | Addresses to listen on (`host:port`, several allowed). Keep these on localhost and management networks. Ports below 1024 (`"192.168.10.2:443"`) work because `jarl.service` gives jarld the `CAP_NET_BIND_SERVICE` capability, and only that ([HTTPS on port 443](#example-https-on-port-443)). |
| `socket` | `/run/jarl/jarl.sock` | A local unix socket for the API. Requests on it from root or jarld's own user need no token and have administrator rights; anyone else must send a token ([permissions.md](permissions.md#the-local-socket)). The `jarl` CLI uses it when no server is configured. At most 107 bytes long; `""` turns it off. If it can't be created, jarld logs a warning and serves the network addresses only. |
| `tls.cert_file`, `tls.key_file` | (empty: plain HTTP) | PEM certificate chain and key. Both or neither. The files are rechecked every 10 minutes and on SIGHUP (`systemctl reload jarl`), so renewed certificates are used without a restart. |
| `trusted_proxies` | `[]` | Addresses or prefixes of reverse proxies whose `X-Forwarded-For` and `X-Forwarded-Proto` (or `Forwarded: proto=`) headers are believed. Needed for correct client IPs in the audit log and for rate limiting behind a proxy, and for knowing that a proxied request came over HTTPS (the session cookie's Secure flag, `acme.httpreq`). From other addresses these headers are ignored. |
| `session.lifetime` | `12h` | A web session ends this long after login... |
| `session.idle_timeout` | `2h` | ...or after this long without requests. |
| `session.cookie_secure` | `auto` | `auto` sets the cookie's Secure flag when the request came over HTTPS (directly or via a trusted proxy); `always` / `never` force it. |
| `totp.require` | `none` | Who must use [two-factor login](permissions.md#two-factor-login): `none` (each user decides), `admins`, or `all` (every user with a password). Someone who must and hasn't set it up can only set it up after logging in. API tokens and the local socket are not affected. |
| `totp.issuer` | `""` | The name authenticator apps show for the account. Empty means `Jarl <short hostname>`, so several servers are told apart. At most 64 characters, no `:`. Changing it doesn't affect apps already set up. |
Durations accept Go syntax (`90s`, `15m`, `12h`) and days (`30d`).
### Storage
| Key | Default | Meaning |
|-----|---------|---------|
| `database` | `/var/lib/jarl/jarl.db` | SQLite database: configuration, users, tokens, history, audit log. |
| `staging_dir` | `/var/lib/jarl/staging` | Scratch space where candidate configurations are rendered and checked with named-checkconf before they are installed. |
| `audit.retention_days` | `0` | Delete audit events older than this many days (0 keeps them forever). Changeset history is kept regardless. |
### Backups
`jarld backup` without a file name, which the `jarl-backup.timer` systemd
timer runs daily, writes `jarl-<UTC time>.db` into `backup.dir` (with the
DNSSEC keys of signed zones inside it) and then deletes old backups there. It reads these settings from this file each
time it runs; when it runs is the timer's (`systemctl edit
jarl-backup.timer`). See [operations.md](operations.md#backups), also for
restoring.
| Key | Default | Meaning |
|-----|---------|---------|
| `backup.dir` | `/var/backups/jarl` | Where backups go. The backup service's sandbox may only write here; to use another directory, allow it with `ReadWritePaths=` in `systemctl edit jarl-backup.service`. |
| `backup.retention` | `30d` | Delete backups older than this, by the time in their name. At least `1d`; `0` keeps every backup. |
| `backup.keep_at_least` | `7` | Always keep this many of the newest backups, however old, so a long gap in backups never deletes them all. At least 1. |
| `backup.stats` | `false` | Also back up the query statistics database (`stats-<UTC time>.db`). |
| `backup.warn_after` | `2d` | The dashboard and `jarl status` warn when the newest backup is older than this. Keep it above the timer's interval; `0` turns the warning off. |
Only files named `jarl-<time>.db` and `stats-<time>.db` are ever deleted.
### Logging
| Key | Default | Meaning |
|-----|---------|---------|
| `log.level` | `info` | `debug`, `info`, `warn` or `error`. |
| `log.format` | `text` | `text` or `json`. Under systemd both go to the journal (`journalctl -u jarl`). |
Audit events are logged as `msg=audit` lines as well as stored in the
database.
### Query statistics
The `stats:` section turns on query statistics (off by default). See
[stats.md](stats.md) for every setting and what it changes in named's
configuration. Changing it takes effect when jarld restarts.
### Prometheus metrics
| Key | Default | Meaning |
|-----|---------|---------|
| `metrics.enabled` | `false` | Serve Prometheus metrics at `/metrics` on `listen` and the local socket ([metrics.md](metrics.md)). |
| `metrics.listen` | `[]` | Extra `ADDRESS:PORT`s that serve only `/metrics`, over plain HTTP, e.g. `["0.0.0.0:9450"]`. |
| `metrics.allow` | `["127.0.0.1", "::1"]` | Addresses or prefixes that may scrape without a token. |
| `metrics.token` | `""` | A bearer token (at least 16 characters) that lets any address scrape. Keep jarl.yaml readable by root and jarld only when set. |
Query counters need `stats.enabled` as well. Changes take effect when jarld
restarts.
### ACME (lego httpreq endpoint)
An endpoint for lego's `httpreq` DNS provider, so lego and Traefik can
publish DNS-01 challenges without the jarl CLI. See
[acme.md](acme.md#6-lego-and-traefik-over-http-httpreq-without-the-jarl-cli)
for how it works and how to set up HTTPS for it. Changes take effect when
jarld restarts.
| Key | Default | Meaning |
|-----|---------|---------|
| `acme.httpreq.enabled` | `false` | Answer `POST /api/v1/acme/httpreq/present` and `/cleanup`. Off, they answer `404 disabled`. |
| `acme.httpreq.require_https` | `true` | Refuse requests that didn't come over HTTPS: jarld's own `tls`, or a proxy in `trusted_proxies` that sends `X-Forwarded-Proto: https`. The rest of Jarl is unaffected by this setting. `false` accepts plain HTTP from anywhere (logged as a warning at startup). |
| `acme.httpreq.allow_loopback_http` | `false` | Also accept plain HTTP from `127.0.0.1` and `::1` (lego on the DNS server itself), unless that address is in `trusted_proxies`. |
### BIND
| Key | Default | Meaning |
|-----|---------|---------|
| `bind.main_config` | `/etc/bind/named.conf` | The file named loads. `jarld import` reads it, and `jarld adopt` replaces it with an include of Jarl's configuration. |
| `bind.config_dir` | `/etc/bind/jarl` | Where Jarl writes `named.conf` and `keys.conf`. Must be readable by named. |
| `bind.zone_dir` | `/var/lib/bind/jarl` | Primary zone files (`<view>/<zone>.zone`). named may write journals, signed copies of zones and, under `dnssec-keys/`, DNSSEC keys here. |
| `bind.cache_dir` | `/var/cache/bind/jarl` | Where named stores secondary, stub and mirror zones. |
| `bind.file_group` | `bind` | Group given to generated files and directories so named can read them. Empty leaves the group alone. |
| `bind.named_user` | `bind` | The user named runs as. `jarld restore` gives it the DNSSEC keys it puts back. |
| `bind.named_checkconf` | `/usr/bin/named-checkconf` | Used to check every candidate configuration (`-z` also loads the zones). |
| `bind.named_checkzone` | `/usr/bin/named-checkzone` | Used to check zone files. |
| `bind.rndc` | `/usr/sbin/rndc` | Used to reconfigure and reload named and to read zone serials. |
| `bind.rndc_args` | `[]` | Extra rndc arguments, e.g. `["-k", "/etc/bind/rndc.key", "-s", "127.0.0.1", "-p", "953"]`. Debian's rndc finds `/etc/bind/rndc.key` by itself. |
| `bind.reload_timeout` | `15s` | How long to wait for named to load changed zones (checked with `rndc zonestatus`) before the change is rolled back. |
| `bind.no_reload` | `false` | Write files but never run rndc. Use it when named runs on another host or for testing. Changes are then not verified against a running named. |
The three directories must exist and be writable by the `jarl` user. The
tmpfiles.d entry creates them, and the systemd unit only allows writes
there (`ReadWritePaths`). If you change them, update both
`/usr/lib/tmpfiles.d/jarl.conf` and the unit (`systemctl edit jarl`):
```ini
[Service]
ReadWritePaths=/srv/bind/jarl /srv/bind/zones
```
### Porkbun (registrar)
With an API key, Jarl can show and set a domain's name servers, glue
records and DNSSEC DS records at Porkbun, after checking that the change
won't break the domain ([dnssec.md](dnssec.md#porkbun)). Without a key
(the default) nothing about Porkbun shows up.
| Key | Default | Meaning |
|-----|---------|---------|
| `porkbun.api_key` | `""` | The API key (`pk1_...`). Set both or neither. |
| `porkbun.secret_api_key` | `""` | Its secret (`sk1_...`). |
| `porkbun.domains` | `[]` | Only these domains (default: every primary zone the key can reach). |
The key can move your domain anywhere, so it is only ever read from this
file: it isn't stored in the database or backups, and there is no API to
set it. Keep the file readable only by root and jarld (`chown root:jarl
jarl.yaml; chmod 640 jarl.yaml`, as the Debian package installs it); jarld
warns at start when everyone can read it. At porkbun.com, switch on "API
Access" for the domain, and limit the key to the domain if you can.
### Blocking
How jarld downloads blocklists ([blocking.md](blocking.md)). The lists and
rules themselves are configuration in the database.
| Key | Default | Meaning |
|-----|---------|---------|
| `blocking.refresh` | `24h` | How often each blocklist is checked for changes (at least `1h`); it is downloaded only when it changed ([blocking.md](blocking.md)). |
| `blocking.max_download_mb` | `64` | Larger downloads are refused (the previous version stays in use). |
| `blocking.max_entries` | `1000000` | Lists with more domains are refused. |
## Example: management network with TLS
```yaml
listen:
- "127.0.0.1:8053"
- "192.168.10.2:8053"
tls:
cert_file: /etc/jarl/tls/fullchain.pem
key_file: /etc/jarl/tls/privkey.pem
log:
level: info
audit:
retention_days: 730
```
## Example: HTTPS on port 443
```yaml
listen:
- "127.0.0.1:8053" # the CLI on the server keeps using this (or the socket)
- "192.168.10.2:443"
tls:
cert_file: /etc/jarl/tls/fullchain.pem
key_file: /etc/jarl/tls/privkey.pem
```
jarld runs as the unprivileged `jarl` user, and binding a port below 1024
needs the `CAP_NET_BIND_SERVICE` capability, which `jarl.service` grants
(`AmbientCapabilities=CAP_NET_BIND_SERVICE`; nothing else). The unit in
Jarl 0.2.0 and earlier doesn't, and jarld then fails with "bind:
permission denied". Upgrading the package installs the new unit; until
then, a drop-in does the same:
```sh
sudo systemctl edit jarl
# [Service]
# CapabilityBoundingSet=CAP_NET_BIND_SERVICE
# AmbientCapabilities=CAP_NET_BIND_SERVICE
sudo systemctl restart jarl
```
## Example: behind nginx
```yaml
listen: ["127.0.0.1:8053"]
trusted_proxies: ["127.0.0.1"]
```
```nginx
location / {
proxy_pass http://127.0.0.1:8053;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s; # applying a large changeset can take a while
}
```
`X-Forwarded-Proto` tells jarld that the client used HTTPS. It sets the
session cookie's Secure flag, and the lego `httpreq` endpoint needs it
(Caddy and Traefik send it without being told). Check with
`curl https://YOUR-PROXY/api/v1/acme/httpreq`, whose `transport` field says
how jarld saw the request.