Files
jarl/README.md
T
adamandClaude Opus 5.5 c645dae14f
release / release (push) Successful in 1m50s
Release 0.3.0-rc.2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 23:27:56 +00:00

189 lines
9.7 KiB
Markdown

<p align="center"><img src="artwork/banner.svg" width="260" alt="Jarl: a bearded Viking jarl in a crown holding a DNS tree, above the JARL wordmark"></p>
# Jarl
*A Jarl rules a Hold; this one rules your zones.*
Jarl manages a BIND 9 (named) server: zones, records of every type, views,
ACLs, TSIG keys and any named.conf option. It has a web UI, a REST API and
a command line client. All three go through the same validated, audited,
all-or-nothing change pipeline, and nothing reaches named before BIND's own
checkers have accepted it.
```
$ jarl record add adbraun.lol nas A 192.168.1.20 --ptr
add nas.adbraun.lol. IN A 192.168.1.20
add 20.1.168.192.in-addr.arpa. IN PTR nas.adbraun.lol.
zone 1.168.192.in-addr.arpa serial 2026100700 -> 2026100701
zone adbraun.lol serial 2026100701 -> 2026100702
applied (changeset 12)
$ jarl record add adbraun.lol www A 192.168.1.99
error: www.adbraun.lol has a CNAME, so it can't have any other records (found A); a CNAME must be the only record at its name (RFC 1034) [zone adbraun.lol]
```
## Features
* **Everything in one place**: primary, secondary, forward, stub, mirror,
hint, redirect and in-view zones, reverse zones from a CIDR, split DNS with
views, ACLs, TSIG keys, and every BIND 9.20 option at server, view, zone
and top level, with the ARM's description and grammar built in.
* **Safe changes.** Every change is validated as a whole (CNAME rules,
glue, delegations, RRset TTLs, references, option syntax, open-resolver
and open-transfer lints), checked with `named-checkconf -z`, installed
atomically, reloaded, and verified with `rndc zonestatus`. If named
doesn't load it, the previous files are put back.
* **Changesets**: apply edits one at a time, or bundle them, review the
diff, and apply them together. Every applied changeset can be reverted.
Serials are bumped automatically.
* **Users and API tokens with granular permissions**, e.g. a token that may
only write `_acme-challenge` TXT records in one zone. Tokens can be
rotated (a new secret, same grants), with an optional grace period, and
Jarl warns two weeks before one expires (dashboard, log, and the CLI
using it).
Passwords use argon2id, sessions have CSRF protection, logins are rate
limited.
* **Two-factor login** with any authenticator app (TOTP), with recovery
codes; optional per user, or required for administrators or everyone
(`totp.require`). With it on, creating API tokens and changing users
asks for a fresh code, so a stolen password can't be turned into a token.
* **Audit log** of every login, change and token, with user, token, IP and
outcome.
* **Daily backups** of the database and the DNSSEC keys by a systemd timer,
checked as they are written, with retention that never deletes the last
few; `jarld restore` puts one back safely, and raises zone serials so
named and the secondaries take the restored data.
* **Name server checks**: whether the secondaries serve the current serial,
and, for zones on the internet, whether the parent zone delegates them
to the right name servers with the right glue and DS records.
* **DNSSEC signing** per zone, by BIND itself, with every step that involves
the parent zone guarded: Jarl says when the DS record can be published,
refuses to make a zone unsigned while its DS may still be out there, turns
signing off in safe steps, and can wipe the slate clean.
* **Porkbun support** (optional): the domain's name servers, glue records
(any number, IPv4 and IPv6) and DS records at the registrar, set from Jarl
after checks that the change won't take the domain offline.
* **No token needed on the server**: root's `sudo jarl ...` talks to jarld
over a local unix socket, so a lost token or password never locks you
out.
* **Query statistics** (optional, off by default): queries over time,
busiest hours of the week, top names and clients, NXDOMAIN and refused
queries, record types, cache hit rate. Shown in the web UI and the CLI,
and exportable as JSON. Stored as aggregates in a separate database with
retention you set.
* **Let's Encrypt DNS-01** built in: `jarl acme` plus certbot, acme.sh and lego
hooks, an HTTPS-only endpoint for lego's and Traefik's `httpreq` provider,
and CNAME delegation to a dedicated ACME zone.
* **Adopts an existing server**: imports named.conf and zone files
(comments included), then switches named over with an automatic
fallback.
* One static Go binary for the daemon (web UI embedded, SQLite built in) and
one for the CLI, run by a sandboxed systemd service as an unprivileged
user. Releases come as Debian packages in an apt repository (server and
CLI separately) and as standalone CLI downloads for Linux, macOS and
Windows; both binaries have bash completion.
## Quick start
On a Debian/Ubuntu BIND 9.20 server ([install.md](docs/install.md) has the
details):
```sh
sudo apt install jarl # from the Gitea package registry (install.md shows adding it)
# or build it: make deb && sudo apt install ./dist/jarl*_amd64.deb, or make && sudo make install
sudo -u jarl jarld init # database + first administrator
sudo -u jarl jarld import # read the current named.conf and zones
sudo jarld adopt # switch named over (keeps a backup, rolls back on failure)
sudo systemctl enable --now jarl
sudo jarl status # root on the server needs no token (local socket)
```
Then open the web UI at `http://127.0.0.1:8053/`, or set `listen` and `tls`
in `/etc/jarl/jarl.yaml` to reach it from your management network.
## Documentation
| | |
|-|-|
| [docs/concepts.md](docs/concepts.md) | How Jarl works: source of truth, changesets, the apply pipeline, authentication. Read this first |
| [docs/install.md](docs/install.md) | Installing, importing and adopting an existing server, TLS, upgrading |
| [docs/configuration.md](docs/configuration.md) | `/etc/jarl/jarl.yaml` reference |
| [docs/cli.md](docs/cli.md) | The `jarl` command line client |
| [docs/api.md](docs/api.md) | REST API reference |
| [docs/permissions.md](docs/permissions.md) | Users, tokens, grants and roles, two-factor login |
| [docs/views.md](docs/views.md) | Split DNS: adding views to a server that has none |
| [docs/dnssec.md](docs/dnssec.md) | DNSSEC signing, going public with your own name servers, Porkbun |
| [docs/acme.md](docs/acme.md) | Let's Encrypt DNS-01 with certbot, acme.sh, lego and Traefik |
| [docs/stats.md](docs/stats.md) | Query statistics: enabling, what is collected, retention |
| [docs/validation.md](docs/validation.md) | Every check Jarl makes |
| [docs/operations.md](docs/operations.md) | Backups, drift, failures, recovery, monitoring |
| [docs/development.md](docs/development.md) | Code layout, tests against real BIND, conventions |
| [CHANGELOG.md](CHANGELOG.md) | Changes per release |
## The web UI
The UI is built for getting things done rather than for looks. It has
these pages:
* **Dashboard**: named status, adoption, drift, recent changes, open
problems.
* **Statistics** (when enabled): queries over time, failed answers, a
weekday x hour heatmap, top names and clients, and breakdowns. Every chart
has a table view.
* **Zones**: list, create (choose a domain or a network, and the dialog shows
the exact zone name it will create and warns about likely mistakes;
fields adapt to the zone type), per-zone records
with inline editing, type-specific hints and a preview of the exact record
that will be added (reverse zones take addresses and suggest host names), settings (SOA, TTL, serial
policy, view), options, the generated zone file, pasting a zone file
to merge or replace records, name server checks, DNSSEC (keys, the DS
record to copy, and each step with what it needs first) and, with a
Porkbun key, the registrar.
* **Views, ACLs, TSIG keys, Server options**: wherever an address match list
is entered, the existing ACLs and keys are offered and unknown names are
flagged. The options editor knows every
BIND 9.20 statement for the scope and shows its description, grammar and
a value field that fits it.
* **Changes**: pending edits with the diff, named-checkconf output and
problems, applied as one changeset. History lists applied changesets with
their diffs and a revert button.
* **Audit, Users, Account** (password, two-factor login, API tokens) and **Search**.
By default, edits are collected as pending changes. Pending rows are shown
inline (green = new, yellow = changed, struck out = deleted) until you
review and apply them. Switch on "Apply edits immediately" to skip the
review step.
## Requirements
* BIND 9.20, with `named-checkconf`, `named-checkzone` and `rndc`. The
option catalog follows 9.20. 9.18 will probably work, because
named-checkconf still checks every change, but it is untested.
* Linux with systemd for the packaged service. Defaults match
Debian/Ubuntu, and other layouts are configurable.
* Go 1.27+ to build.
## Status
Version 0.2.0 is the current release, and 0.3.0 is in testing
(0.3.0-rc.2). 0.2.0 added DNSSEC signing, Porkbun support, two-factor
login and view colours; 0.3.0 adds disabled records and scripting output
for the CLI. Jarl is feature-complete for one server's configuration.
Managing several servers from one jarld and dynamic updates are not
handled yet ([concepts.md](docs/concepts.md#what-jarl-deliberately-doesnt-do-yet)).
## License
Jarl is released under the [MIT License](LICENSE).
## Credits
The logo is part of Jarl: a bearded Viking jarl in blue and white, whose
crown holds a DNS tree (a root node with its branches). The master is
`artwork/logo.png`, which `scripts/logo.py` traces into the vector
`assets/logo.svg` and `assets/logo.js`. The "JARL" wordmark is drawn as SVG
paths in `assets/brand.js`, so the web UI needs no font files. The banner at
the top of this file, `artwork/banner.svg`, is built from both by
`scripts/banner.py`.