# Users, tokens and permissions ## Ways to authenticate | Who | How | Notes | |-----|-----|-------| | 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 | `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: a token restricted with grants (below) can, for example, only change TXT records under `_acme-challenge` in one zone. TSIG keys, on the TSIG keys page, are something else: shared secrets that BIND servers use to authenticate each other (zone transfers, NOTIFY); they don't give access to Jarl. Failed logins are rate limited: 8 per username and 20 per client IP within 15 minutes. Failed token attempts count against the IP too. Every attempt is in the audit log. A user **without a password** (`--no-password`) is a service account. It can't log in to the web UI and is used only through its tokens, which suits certbot, CI jobs and backups. ## Grants A user's permissions are a list of grants. Administrators (`admin: true`) may do everything, and only they can manage users, see everyone's tokens and run `resync`. A grant is written as: ``` RESOURCE:ACCESS [zones=PATTERNS] [views=PATTERNS] [names=PATTERNS] [types=TYPES] ``` | Resource | Covers | |----------|--------| | `zones` | zone definitions (create, delete, SOA, TTL, view) and zone options | | `records` | records in primary zones | | `views` | views and view options | | `acls` | ACLs | | `tsig` | TSIG keys (`write` is needed to see a secret) | | `options` | server options, top-level statements, the defaults for new zones, `jarl check` | | `audit` | everyone's audit events and changesets (without it, users see only their own) | | `stats` | query statistics, including client addresses and the names they look up (read only) | | `*` | all of the above | `ACCESS` is `read` or `write` (write includes read). Filters narrow a grant. Each takes comma separated patterns where `*` and `?` are wildcards, and a missing filter matches everything: * `zones=adbraun.lol,*.adbraun.lol`: zone names, without the final dot * `views=internal`: view names (`_default` means zones outside any view) * `names=_acme-challenge.*`: record owner names, as FQDNs without the final dot * `types=TXT,CNAME`: record types Examples: ``` *:read read everything records:write zones=lab.adbraun.lol edit records in one zone zones:write zones=*.lab.adbraun.lol create/delete zones below lab records:write zones=adbraun.lol views=internal only the internal copy of a split zone records:write names=_acme-challenge.* types=TXT ACME challenges anywhere records:write zones=acme.adbraun.lol types=TXT only TXT records in a dedicated ACME zone ``` A user who may see some records of a zone may also see that the zone exists (its name and type), but not its other records. ### Roles Roles are ready-made grant lists. Choosing a role copies its grants, which you can then edit. | Role | Grants | |------|--------| | `readonly` | `*:read` (includes statistics) | | `dns-editor` | `zones:write`, `records:write`, `views:read`, `acls:read`, `options:read` | | `operator` | `*:write` (everything except user management) | | `acme` | `records:write names=_acme-challenge.* types=TXT` | ```sh jarl user create alice --role dns-editor jarl user update alice --grant "records:write zones=adbraun.lol" --grant "zones:read" # replaces all grants jarl user update alice --role readonly ``` ## Tokens Every token belongs to a user, and every token request acts as that user. The audit log shows the user and the token name. * A token **without grants** has all of its user's permissions, and they follow the user: if the user loses a permission, so does the token. * A token **with grants** is restricted to them. Effective permission is *user grants ∩ token grants*: a token can never do more than its user. Admin rights don't pass through a restricted token. * A restricted token can create further tokens, but only ones no wider than itself. * Disabling or deleting a user stops all their tokens at once. ```sh jarl token create laptop # full rights of the user, no expiry jarl token create ci --grant "records:write zones=lab.adbraun.lol" --expires 90d jarl token create certbot --user certbot --role acme # admin: a token for another user jarl token list --all jarl token revoke 12 ``` In the web UI: Account → API tokens (your own) and Users → *user* → Tokens (admin). ## Recovering access On the server, as root or the `jarl` user: ```sh sudo -u jarl jarld user list sudo -u jarl jarld user passwd admin # set a new password and re-enable the account sudo -u jarl jarld user add --admin rescue # a new administrator sudo -u jarl jarld user token admin cli # a new API token for a user ```