release / release (push) Successful in 4m43s
0.6.1's fourth release candidate: the Stats page's range buttons follow the retention settings, jarl changeset show prints local time, and the CLI no longer prints "and N more" when the server sends no PTR list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
243 lines
14 KiB
Markdown
243 lines
14 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 (suggested,
|
|
filled and kept in step from your A records), 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.
|
|
* **Internet exposure checks**: Jarl works out which views are reachable
|
|
from the internet (a GUA-addressed internal network still counts as
|
|
yours) and checks them: response rate limiting (set up in log-only mode
|
|
first, with what it would limit shown before you enforce it), open
|
|
resolvers, ANY amplification, cache snooping, version and host name
|
|
leaks, open management interfaces. Fixes are staged for review, changes
|
|
that open a public view up must be confirmed, and deliberate exceptions
|
|
can be acknowledged with a reason.
|
|
* **Prometheus metrics** (optional): health to alert on (named down,
|
|
configuration problems, drift, failing blocklists, stale backups, DNSSEC
|
|
steps waiting at the parent, expiring tokens) and query counters per
|
|
result code, view, zone and blocklist, with example alerts. Set up in
|
|
jarl.yaml, with an address allow list and/or a bearer token.
|
|
* **Blocking** like Pi-hole, done by named itself: blocklists (Adblock,
|
|
hosts or plain domains) downloaded and kept up to date, your own block
|
|
and allow rules that beat every list, per view, with "why is this
|
|
blocked?" checks and blocked counts in the statistics.
|
|
* **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.
|
|
* **Certificates for the server itself**: Jarl obtains and renews TLS
|
|
certificates from Let's Encrypt (or any ACME CA), answering the DNS-01
|
|
challenge in its own zones or through Porkbun's API while Porkbun still
|
|
serves the domain, decided at every renewal. Use them for the web UI
|
|
(HTTPS without a restart) and for named's DNS over TLS and HTTPS (named
|
|
rereads them on renewal), or bring your own.
|
|
* **DNS over TLS and HTTPS** for your own devices in one step: the
|
|
certificate, named's listeners on the addresses you choose (after
|
|
checking the ports are free) and the host name in your private views.
|
|
* **Linked zones for split DNS**: the same zone in several views, with the
|
|
shared records kept in step by Jarl and a few records of each view's own
|
|
(an internal address for a public name, say).
|
|
* **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/encrypted-dns.md](docs/encrypted-dns.md) | DNS over TLS and HTTPS for your clients: setup, ports, client settings |
|
|
| [docs/certificates.md](docs/certificates.md) | TLS certificates Jarl obtains and renews for its web UI and named's DNS over TLS/HTTPS |
|
|
| [docs/stats.md](docs/stats.md) | Query statistics: enabling, what is collected, retention |
|
|
| [docs/reverse.md](docs/reverse.md) | Reverse zones: suggested from your records, created in the right view and filled, PTR records kept in step |
|
|
| [docs/exposure.md](docs/exposure.md) | Internet exposure: which views are public, hardening checks, response rate limiting |
|
|
| [docs/metrics.md](docs/metrics.md) | Prometheus metrics: access, what is exported, example alerts |
|
|
| [docs/blocking.md](docs/blocking.md) | Blocking ads, trackers and malware: blocklists, your own rules, views, memory |
|
|
| [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.6.0 is the current release, and 0.6.1 is in testing
|
|
(0.6.1-rc.4): it gets certificates onto your other hosts (`jarl cert
|
|
get`: Jarl obtains and renews them, a host downloads them and runs a
|
|
hook, a daily timer keeps them current), exports certificates, fixes
|
|
PTR records and overrides in linked zones, and makes one-view tokens
|
|
(such as ACME tokens for the external view) work with linked zones.
|
|
Reverse zones list addresses in numeric order and no longer report
|
|
another view's hosts as missing PTR records.
|
|
0.6.0 makes reverse zones easy (suggested from your records,
|
|
created in the right view and filled, PTR records kept in step), rebuilds
|
|
comparing a zone's views, lets you explore the statistics down to each
|
|
client and name (a query log), shows the databases' size, and gives
|
|
encrypted DNS several host names and views per listen address. 0.5 added Prometheus metrics,
|
|
address ranges in ACLs and other address match lists, safer changeset
|
|
handling, internet exposure checks with response rate limiting, TLS
|
|
certificates Jarl obtains and renews itself, linked zones for split DNS,
|
|
DNS over TLS and HTTPS, and statistics per view. 0.4.0 added blocking (blocklists and
|
|
your own rules, like Pi-hole, done by named, with different blocking for
|
|
different devices), disabling zones, server statistics and view keys, on
|
|
top of 0.3.0's disabled records, script-friendly CLI output and client
|
|
names in the statistics, and 0.2.0's DNSSEC signing, Porkbun support,
|
|
two-factor login and view colours. 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`.
|