fix: service accounts are managed at /service-accounts, not /users, and the internal one of the metrics service is only listed.
Release / release (push) Successful in 12s

This commit is contained in:
slawek committed 2026-10-25 18:18:03 +01:00
1 parent d1970b77b7
commit f01be41191
4 files changed
+143 -34

No files matched your search

+31 -8
View File
@@ -428,10 +428,11 @@ it is added:
certificate, so no VPN access. It can sign in to `/auth` and, as a member of `admins`, manage
the router. `add` without the first and the last name adds one; its names cannot be set later;
- a service account, a Manager-only account whose `vpnRouterScope` is `service`, for a service
that checks passwords against the directory; it cannot sign in to `/auth`. `add --service` and
`POST /users` with `"service": true` add one. The internal one is the account
`metrics.service_account` names while metrics are enabled: `vpn-router-setup` adds it and keeps
its password (see Metrics), so `/users` refuses to set its password.
that checks passwords against the directory; it cannot sign in to `/auth`. `add --service` adds
one, and the web service manages them at `/service-accounts`, not at `/users`. The internal one
is the account `metrics.service_account` names while metrics are enabled: `vpn-router-setup` adds
it and keeps its password (see Metrics). The API only lists it: `/service-accounts` refuses every
other request on it, and `/groups` refuses to put it in or take it out of a group, with 403.
Passwords and TOTP secrets are read from the first line of standard input. The answer is text: a
table of the users, the client profile, a TOTP secret or a short message, and a failure is a
@@ -610,6 +611,7 @@ curl http://10.0.0.4:17081/
| `/auth` | Signing in with a user's password and TOTP code, refreshing and signing out |
| `/config` | The configuration without its secret options, and what the router resolved and runs, as JSON; needs a token |
| `/users` | The users of OpenVPN and road-warrior access; needs a token |
| `/service-accounts` | The service accounts; needs a token |
| `/groups` | The groups of users; needs a token |
| `/site-to-site` | The site-to-site connections: list, add, change and remove; needs a token |
| `/account` | The account of the signed-in user: its details, its password and its TOTP enrolment; needs a user's token |
@@ -634,7 +636,7 @@ curl http://10.0.0.4:7081/status
### Tokens and signing in
`/config`, `/users`, `/groups`, `/site-to-site` and `/account` need a JWT in an `Authorization: Bearer` header, signed with
`/config`, `/users`, `/service-accounts`, `/groups`, `/site-to-site` and `/account` need a JWT in an `Authorization: Bearer` header, signed with
HS256 by the key the web service makes on its first run, `/etc/vpn-router/web.key` (root only).
The key never leaves the router; while it does not exist these paths and `/auth` are not served
(404). A missing, expired or wrongly signed token, or one of another algorithm, gets 401 with
@@ -717,9 +719,9 @@ and answer bodies are JSON; a request body is at most 4096 bytes.
| Request | Body | Answer |
| --- | --- | --- |
| `GET /users` | | `{"users": [{"name", "manager_only", "service", "internal", "enabled", "locked", "certificate", "otp", "first_name", "last_name", "email"}, ...]}` |
| `POST /users` | `{"name", "password", "first_name", "last_name", "email"}` for a VPN user; `{"name", "password", "email"?}` for a Manager-only account; `{"name", "password", "service": true, "email"?}` for a service account | 201 and the user |
| `POST /users` | `{"name", "password", "first_name", "last_name", "email"}` for a VPN user; `{"name", "password", "email"?}` for a Manager-only account | 201 and the user |
| `PUT /users/<name>` | any of `{"first_name", "last_name", "email"}`; for a Manager-only account only `{"email"}`, `""` removing it | the user |
| `PUT /users/<name>/password` | `{"password": ...}`; refused with 400 for the internal service account | the user |
| `PUT /users/<name>/password` | `{"password": ...}` | the user |
| `POST /users/<name>/disable` | | the user |
| `POST /users/<name>/enable` | | the user |
| `POST /users/<name>/unlock` | | the user |
@@ -730,7 +732,8 @@ and answer bodies are JSON; a request body is at most 4096 bytes.
| `DELETE /users/<name>/otp` | | the user |
A failure is `{"error": ...}` with 400 for an invalid request, 404 for a user that does not
exist, 409 for a user that exists and 500 otherwise. `/users` works whether or not OpenVPN or
exist or is a service account, 409 for a user that exists and 500 otherwise. `/users` does not
list service accounts; `/service-accounts` serves them. `/users` works whether or not OpenVPN or
road-warrior access is enabled; the profile needs OpenVPN. Passwords, private keys and TOTP
secrets cross the internal network in plain HTTP, like the token.
@@ -740,6 +743,26 @@ curl -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
http://10.0.0.4:7081/users
```
### The /service-accounts API
`/service-accounts` manages the service accounts with the same token and the same module as
`/users`.
| Request | Body | Answer |
| --- | --- | --- |
| `GET /service-accounts` | | `{"service_accounts": [{"name", "internal", "enabled", "locked", "email", ...}, ...]}`, each as `/users` shows a user |
| `POST /service-accounts` | `{"name", "password", "email"?}` | 201 and the account |
| `PUT /service-accounts/<name>` | `{"email"}`; `""` removes it | the account |
| `PUT /service-accounts/<name>/password` | `{"password": ...}` | the account |
| `POST /service-accounts/<name>/disable` | | the account |
| `POST /service-accounts/<name>/enable` | | the account |
| `POST /service-accounts/<name>/unlock` | | the account |
| `DELETE /service-accounts/<name>` | | `{"name", "removed": true}` |
The internal account, `internal` true, is only listed: every other request on it gets 403, as
`vpn-router-setup` adds and keeps it. A failure is otherwise as for `/users`, with 404 also for a
name that is not a service account.
### The /account API
`/account` serves the user the access token names, whether or not the user is a member of