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

+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