jarld can now answer lego's httpreq DNS provider (POST /api/v1/acme/httpreq/present and /cleanup), so lego and Traefik publish DNS-01 challenges with only environment variables, without the jarl CLI. lego's username and API token arrive as HTTP Basic auth, accepted on these routes only. The endpoint is off by default (acme.httpreq.enabled) and HTTPS-only by default: jarld's own TLS, or a trusted proxy that says so with X-Forwarded-Proto or Forwarded (the last value counts). Plain HTTP can be allowed from loopback only or everywhere. GET /api/v1/acme/httpreq shows how jarld sees a request, to check a proxy setup. jarl acme and the endpoint share their code in internal/acme. Documented in docs/acme.md, configuration.md, api.md and permissions.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
13 KiB
Changelog
All notable changes to Jarl are documented here. The format follows Keep a Changelog, and versions follow Semantic Versioning.
[Unreleased]
The first version, to be released as 0.1.0.
Added
-
CLI: several servers in one configuration file. Name them (
jarl config server add ns1 URL), pick one with-s/--server NAMEorJARL_SERVER, set a default withjarl config use, and group them (jarl config group set dns ns1 ns2). A command aimed at a group (orall) is first checked on every server as a dry run and only runs if it succeeds everywhere, so a change one server rejects changes nothing anywhere. Named servers always use their own token. -
Reverse zones for any network: a /32 (or /128) gets a zone that answers for just that address, and a network between octet (or nibble) boundaries, such as a /22, is covered by several zones (four /24s) that are created together, from the CLI (
jarl zone create 10.0.8.0/22) and the web UI. Networks smaller than a /24 explain that they belong in the enclosing /24's zone. -
docs/operations.md: setting up a secondary server with its own jarld.
-
An endpoint for lego's
httpreqDNS provider (/api/v1/acme/httpreq/presentand/cleanup), so lego and Traefik can publish DNS-01 challenges with only environment variables, without the jarl CLI. lego's username and API token arrive as HTTP Basic auth, which is accepted on these routes only. Off by default (acme.httpreq.enabled), and HTTPS-only by default: jarld's own TLS, or a trusted proxy that says so withX-Forwarded-ProtoorForwarded. Plain HTTP can be allowed from loopback only (allow_loopback_http) or everywhere (require_https: false).GET /api/v1/acme/httpreqshows how jarld sees a request, to check a proxy setup.jarl acmeand the endpoint share their code (internal/acme). -
Trusted proxies may also send the standard
Forwarded: proto=httpsheader. When a proxy header lists several values, the last one (added by the nearest proxy) counts. -
lego support:
contrib/lego/jarl-lego.shis a program for lego'sexecDNS provider that publishes DNS-01 challenges throughjarl acme, including challenge names reached through a CNAME (docs/acme.md). -
docs/concepts.md: what each zone type does, what to enter as a new zone's name servers (and when glue addresses are needed), how reverse zones map to networks, and why dynamic updates give way to API tokens.
-
A logo: a Viking jarl drawn like a woodcut, with a spear-pointed crown that holds a DNS tree (a root node and its branches), a stern face and a full carved beard. White on a Nordic-blue disc (
assets/logo.svg), plus an all-white variant (assets/logo-white.svg). It is shown on the login page, links home in the top left of the web UI, and is the favicon. -
The login page shows an angular, Norse-style "JARL" wordmark drawn as SVG paths (
assets/brand.js) above a rule with a "DNS" node; it follows the light and dark themes. The header's "Jarl" uses Cinzel Bold (SIL OFL), self-hosted as a 13 KB Latin subset. -
Web UI: wherever an address match list is entered (server, view and zone options such as allow-query or allow-transfer, view match lists, ACL elements) the existing ACLs, TSIG keys and built-in names are offered as one-click suggestions, and names that don't exist are flagged as you type.
-
Query statistics (off by default;
statsin jarl.yaml, docs/stats.md). When enabled, Jarl adds a loopback statistics channel and a response log to named's configuration (merged into existingloggingandstatistics-channelsstatements) and switches response logging withrndc responselog. jarld aggregates named's counters and the log into a separate SQLite database: per-minute, hourly and daily counters (queries, result codes, record types, views, zones, cache hits, upstream latency, DNSSEC) and hourly and daily top lists (names, clients, NXDOMAIN and SERVFAIL names, refused clients), each with its own retention. Clients can be anonymized or excluded. It is shown on a new Statistics page in the web UI (KPI tiles, queries and failed answers over time, a weekday x hour heatmap, top lists and breakdowns, each chart with a table view), injarl stats status|summary|top|timeline|heatmap|breakdown, and in the API under/api/v1/stats/, behind the newstatspermission. Works with the bind9 packages of Debian 13 and Ubuntu 26.04. -
At startup jarld brings named's configuration up to date with jarl.yaml settings that affect it (such as statistics), through the usual checked pipeline. Files changed by hand are never overwritten.
-
jarld daemon: REST API (
/api/v1) and embedded web UI, with SQLite as the source of truth for the whole BIND configuration. Runs under systemd (Type=notify, sandboxed, unprivilegedjarluser in groupbind). SIGHUP and certificate file changes reload TLS. -
Configuration model: views (ordered, match-clients/destinations, recursive-only), ACLs (nested, negation, keys, reference tracking with rename and delete protection), TSIG keys (kept in a separate
keys.conf, never in diffs), zones of every type (primary, secondary, stub, static-stub, forward, mirror, hint, redirect, in-view), reverse zones from a CIDR, records of every type, options at server, view, zone and top-level scope, and defaults for new zones. -
BIND 9.20 option catalog: all 353 statements with grammar, allowed scopes, value kinds, deprecation status and ARM descriptions, used for validation and by the UI's option editor.
-
Changesets: immediate one-operation changes by default; staging into named changesets (
?changeset=,X-Jarl-Changeset,--changeset), batches (POST /apply,jarl apply -f), dry runs, previews with diffs, and revert of applied changesets. Optimistic locking with object versions. -
Apply pipeline: whole-configuration validation, rendering,
named-checkconf -zandnamed-checkzonein a staging directory, atomic install,rndc reconfigand per-zone reload, verification withrndc zonestatus, and automatic rollback when named doesn't load the change. Automatic serials (date, increment or unixtime policy, RFC 1982), bumped only when DNS data changes. Drift detection for files changed outside Jarl. -
Validation: CNAME/DNAME rules, NS/MX/SRV targets, glue and delegations, RRset TTL consistency, BIND 9.20 RRset/type limits, SPF/DMARC sanity, ACL/key references and cycles, option syntax and scope, and security lints (open resolver, open zone transfers, dynamic updates on managed zones).
-
Records: TXT entered as plain text (automatic quoting and 255-byte splitting), relative and absolute names with detection of doubled origins, PTR records kept in step with A/AAAA (
ptr), RRset replace, delete by match, zone file import (merge or replace). -
Import and adoption:
jarld importreads an existing named.conf tree and zone files (comments,$INCLUDE,$GENERATE, legacymaster/slavespellings);jarld adoptswitches named to Jarl's configuration with a backup and automatic fallback. -
Authentication: web logins with argon2id passwords, sessions (HttpOnly, SameSite=Strict) and CSRF tokens, rate limiting per user and IP; hashed API tokens (
jarl_...) with optional expiry. -
Permissions: granular grants (resource × read/write, filtered by zone, view, record name pattern and type), roles (
readonly,dns-editor,operator,acme), tokens restricted to a subset of their user's rights, administrators. -
Audit log of logins, changes, users, tokens and secret reveals, with user, token, IP and outcome, searchable and written to the journal.
-
Web UI: dashboard, zones and records with inline editing and pending markers, views, ACLs, TSIG keys, the option editor, review and apply, history with revert, audit log, users, account and tokens, search. Review mode by default, with an "apply immediately" switch.
-
jarl CLI: zones, records, views, ACLs, TSIG keys, options, settings, changesets, batches, history, users, tokens, audit, search, status, check and resync. Table, JSON and YAML output, and documented exit codes.
-
ACME DNS-01:
jarl acme set|unset, certbot hooks (jarl acme certbot-auth|certbot-cleanup), an acme.sh DNS API hook (contrib/acme.sh/dns_jarl.sh), automatic zone and view discovery, idempotent retries, and--follow-cname/--resolverfor a dedicated ACME zone. -
Operations:
jarld backup(online),jarld resync,jarld check,jarld render, local user recovery commands, audit retention. -
Packaging: Makefile (build, test, e2e, install, dist), systemd unit, sysusers.d and tmpfiles.d files, example configuration (
jarld config --example). -
Tests: unit tests for every package, integration tests against the real named 9.20 (checkers, reload and rollback, import and adopt), and an end-to-end script (
scripts/e2e.sh) that drives every CLI command against a running named. -
Documentation: concepts, install and adoption, configuration, CLI, API, permissions, ACME, validation, operations and development guides, and a split-DNS guide for adding views to a server that has none.
Changed
-
Web UI: the review/live switch is a small "Review edits" / "Live edits" button in the header, and the bar below it only appears while there are pending changes.
-
TSIG keys: the name help no longer calls
xfer-keya domain name (any letters, digits, hyphens and dots will do; both servers must use the same name), and the keys page points to restricted API tokens for giving scripts or people limited access to zones and records. -
Web UI, new zone dialog: choose Domain or Reverse zone. For a reverse zone you give the network (10.10.0.0/24, 2001:db8:1::/48) and the zone name is generated. Either way the dialog shows the exact zone name it will create and explains problems before submitting: an address or URL instead of a name, a host-like name such as www.example.com, a zone that already exists in the view, a child or parent of an existing zone, and networks that don't follow the octet or nibble boundaries.
-
Web UI, record form: after adding a record every field is cleared except the type, and the cursor returns to the name. A "Will add:" line shows the record exactly as it will be stored, with warnings for unqualified names (e.g. a CNAME to readthedocs.io without the final dot). In reverse zones the form asks for the address, defaults to PTR, offers the host names of A/AAAA records with that address, warns about a second PTR for an address, and the record list shows each record's address.
-
Reverse zones: a record name can be given as an address (10.10.0.20) in the API and CLI too, and a PTR target without a final dot that contains a dot (nas.adbraun.lol) is read as the full host name (with a warning); a bare label is refused.
-
jarl resync/jarld resyncrefuse to overwrite files that were changed outside Jarl unless--forceis given, as documented. Before, they always overwrote them.
Fixed
-
The dashboard's "Pending changes (1 open)" counted empty changesets (and other users'), so the Changes page then showed none. It now counts your own changesets that contain changes, and the status API's
open_changesetsonly counts non-empty ones. In review mode, a draft created for an edit that the server rejects is discarded instead of being left open and empty. -
Under the shipped systemd unit (
RestrictSUIDSGID=yes), the first zone in a new view failed with "chmod /var/lib/bind/jarl/VIEW: operation not permitted": setting the setgid bit on the new directory is refused there. Jarl now falls back to the permission bits, so the directory is group-writable for named, and only changes the mode when it needs to. -
Web UI: the "Jarl 0.1.0" brand link no longer underlines the gap between the name and the version on hover.
-
Web UI: creating a zone from the dashboard's "New zone" link no longer opens the dialog a second time.
-
Warnings noticed while parsing record data (a name read as absolute) were lost before reaching the result; they are shown again.
-
The negative-caching warning now uses the effective negative TTL, which is the smaller of the SOA minimum and the zone's default TTL (RFC 2308). It explains the real effect (new names can stay invisible to resolvers that already asked) and gives the command that fixes it. Before, it fired on the SOA minimum alone and wrongly blamed BIND's resolver cap.
-
Problems printed by
jarld checkshow zone names without the trailing dot. -
jarlnow shows the warnings a change introduces when it is applied or staged, not only on dry runs. -
jarl checkandjarl resyncno longer print named-checkconf's routine "loaded serial" lines, only its errors and warnings. -
jarl zone listshowed only primary zones:zone create's default--type primaryleaked into the list filter through a shared variable. Hint, secondary, forward and other zones are listed again. -
named-checkconf and named-checkzone output is classified by the tools' exit code. Their warnings (e.g.
both "recursion no;" and "allow-recursion" active) were shown as errors. They are now shown as warnings in the CLI,jarld checkand the web UI. -
The
zone_outside_viewerror says how to fix it (--adopt-zones). -
The end-to-end test starts named with
-flike the Debian unit; with-g, named sends all logging to stderr.