Add a local unix socket for root on the server

jarld also listens on /run/jarl/jarl.sock (socket in jarl.yaml). Requests
on it from root or jarld's own user need no token and act as an
administrator; the kernel's SO_PEERCRED says who is connecting, and
anyone else must send a token. The jarl CLI uses the socket when no server
is configured, or with -s local, so 'sudo jarl status' works on the server
with nothing set up and a lost token or password never locks root out.
Changes are audited under the system user's name via "socket".

The systemd unit creates /run/jarl (0750). Socket paths over 107 bytes
are refused with a clear message. The docs explain the socket and what
happens when a token is revoked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
abrown38andClaude Opus 5.5 committed 2026-10-08 15:10:57 +00:00
1 parent f528c2aa9c
commit cf49df0346
22 files changed
+570 -26

No files matched your search

+8
View File
@@ -28,6 +28,14 @@ change something (anything but GET/HEAD) must send it as `X-CSRF-Token`.
`GET /auth/whoami` returns the user, the token (if any), the effective
permissions and whether the caller is an administrator.
**The local socket**: on jarld's unix socket (`/run/jarl/jarl.sock`),
requests from root or jarld's own user need no `Authorization` header and
act as an administrator; other users must send a token:
```sh
sudo curl --unix-socket /run/jarl/jarl.sock http://localhost/api/v1/status
```
**HTTP Basic**, only on the lego `httpreq` routes (below): the username is
the token's user and the password the API token.
+24
View File
@@ -46,6 +46,30 @@ file is `~/.config/jarl/config.yaml` (mode 0600), or `--config` /
Get tokens from the web UI (Account → API tokens), from
`jarl token create`, or from `jarld init --token` on the server.
### On the server itself
On the DNS server, root needs no setup at all:
```sh
sudo jarl status
sudo jarl record add adbraun.lol www A 192.0.2.10
```
With no server configured, the CLI talks to jarld through its local unix
socket, `/run/jarl/jarl.sock`, where root and the `jarl` user are
administrators without a token ([permissions.md](permissions.md#the-local-socket)).
The history and audit log show such changes as made by `root` via
`socket`.
* `-s local` (or `JARL_SERVER=local`) uses the socket even when servers
are configured, and `local` can be part of a list (`-s local,ns2`).
`local` is reserved and can't be a configured server's name.
* `JARL_SOCKET` points at another socket path.
* A token given with `--token` or `JARL_TOKEN` is sent over the socket as
well, and then its permissions apply.
* Other users get a clear error ("only root and the jarl user can use
jarld's local socket"); they configure a server and token as above.
### Several servers and groups
With more than one Jarl (say a primary and a secondary, each with its own
+1
View File
@@ -19,6 +19,7 @@ The CLI has its own small configuration file, described in [cli.md](cli.md).
| Key | Default | Meaning |
|-----|---------|---------|
| `listen` | `["127.0.0.1:8053"]` | Addresses to listen on (`host:port`, several allowed). Keep these on localhost and management networks. |
| `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... |
+7 -2
View File
@@ -227,10 +227,15 @@ Open `https://<management-address>:8053/` and log in as `admin`.
## 9. Set up the CLI
The CLI can run on the server or on any machine that can reach jarld.
On the server itself, root needs nothing: `sudo jarl status` and every
other command use jarld's local socket without a token
([cli.md](cli.md#on-the-server-itself)).
On other machines, or as an ordinary user, give the CLI a server and a
token:
```sh
jarl config set server https://ns1.adbraun.lol:8053 # or http://127.0.0.1:8053 on the server
jarl config set server https://ns1.adbraun.lol:8053
jarl config set token # paste the token from 'jarld init' at the prompt
jarl status
jarl zone list
+49 -1
View File
@@ -7,6 +7,7 @@
| People in the web UI | Username + password → session cookie | argon2id password hashes. The session cookie is HttpOnly and SameSite=Strict and lasts 12h (2h idle). Every change also needs the session's CSRF token, which the UI sends in `X-CSRF-Token`. Changing a password logs out the user's other sessions. |
| CLI, scripts, certbot | API token: `Authorization: Bearer jarl_...` | Shown once at creation, stored hashed. Optional expiry. Revocable. Last use time and IP are recorded. |
| lego / Traefik (`httpreq`) | HTTP Basic: the token's user and the API token | Only on `/api/v1/acme/httpreq/...`, only when `acme.httpreq.enabled` is set, and by default only over HTTPS ([acme.md](acme.md)). The token's grants apply as usual. |
| Root on the server, with the CLI | jarld's local socket (`/run/jarl/jarl.sock`): no token | `sudo jarl ...` with no server configured. The kernel tells jarld which system user is connecting; root and the `jarl` user are administrators, anyone else must send a token. Audited as the system user (e.g. `root`) via `socket`. See [The local socket](#the-local-socket). |
| Root on the server | `jarld` subcommands (`init`, `user`, `import`, `adopt`, `check`, `resync`, `backup`) | Work directly on the database. They are for bootstrapping and recovery, and are audited as "local". |
API tokens are how you give a script, a tool or a person limited access:
@@ -161,9 +162,56 @@ In the web UI, the **Rotate** button next to each token asks how long the
old secret should keep working and how long the token should last, then
shows the new secret once.
## The local socket
jarld also listens on a unix socket, `/run/jarl/jarl.sock` (`socket` in
jarl.yaml; systemd creates `/run/jarl`). It serves the same API, but a
request on it without a token is trusted by **who sent it**: the kernel
reports the connecting process's user (`SO_PEERCRED`), and
| Connecting user | Without a token | With a token |
|--|--|--|
| root | administrator | the token's permissions |
| `jarl` (jarld's own user) | administrator | the token's permissions |
| anyone else | refused (401) | the token's permissions |
This gives root nothing it doesn't already have: root can read Jarl's
database, run `jarld user token` and edit named.conf directly. What it adds
is convenience and a way back in: on the server, `sudo jarl status` and
every other command work with nothing configured and no token stored in a
file, even after every token has been revoked and every password
forgotten.
* The socket file is mode 0600 in a 0750 directory owned by `jarl`, so
other users can't even connect; the user check doesn't depend on that.
* Changes made this way are in the history and audit log under the system
user's name (`root`) with `socket` as the method. That name isn't a Jarl
user, so it has no tokens of its own: `sudo jarl token create laptop
--user admin` makes one for a real user.
* The CLI uses the socket only when no server is configured at all, or
with `-s local`, so a configured server is never silently swapped for
the local one ([cli.md](cli.md#on-the-server-itself)).
* Turn it off with `socket: ""` in jarl.yaml.
## Recovering access
On the server, as root or the `jarl` user:
Revoking a token never locks you out: tokens and web sessions are
separate. Revoking the token your CLI uses stops only the programs that
use it; you stay logged in to the web UI with your password, with all your
rights, and can make a new token under Account → API tokens. Jarl also
refuses to delete, disable or demote the last administrator.
On the server, root can always use the CLI through the local socket,
without a token:
```sh
sudo jarl whoami
sudo jarl token create laptop --user admin # a new token for admin
sudo jarl user update admin --password # a new password
```
The `jarld` subcommands work even when jarld isn't running. As root or the
`jarl` user:
```sh
sudo -u jarl jarld user list