feature: bootstrap diagnostics on port 17081 during the first setup.
Release / release (push) Successful in 13s

This commit is contained in:
slawek committed 2026-10-24 10:07:22 +02:00
1 parent 2e2bacee7d
commit c0cd323a0c
13 files changed
+275 -181

No files matched your search

+33 -14
View File
@@ -65,7 +65,7 @@ The service listens on plain HTTP on the internal network.
Decision: `/config`, `/users` and `/groups` need a JWT in an `Authorization: Bearer` header,
HS256, verified with `hmac` and `hashlib`; no other algorithm is accepted. The key is
`/etc/vpn-router/web.key`, random, made by the web service on its first run and readable by root
only; it is never configured or output, and `web.jwt_secret` is ignored. The router itself issues
only; it is never configured or output. The router itself issues
the tokens: `POST /auth` checks a user's password and TOTP code with a bind (ADR-14) and answers
an access token naming the user in `sub`, valid for 5 minutes, and a refresh token valid for 8
hours (ADR-15); `vpn-router-token` makes tokens with the `admin` claim (ADR-13). A token without
@@ -348,26 +348,45 @@ no usable token. Changing a user's password, disabling, unlocking or removing th
signing out, end the user's sessions at once; an access token already issued lives out its
5 minutes.
## ADR-16: Each service creates what it needs on its first run and shows its starting phase
## ADR-16: Each service creates what it needs on its first run
Status: Accepted
Context: The package manager configures packages in an order of its own; on a new VM the Router
Manager was configured before the PostgreSQL server, so a database created at installation was
missing. PostgreSQL may also start after the services at boot. A router whose setup fails can
be unreachable, and the reason must be visible without a shell where possible.
missing. PostgreSQL may also start after the services at boot.
Decision: The web service makes its signing key and creates the refresh tokens' database itself,
and the Router Manager its sessions database; neither `vpn-router-setup` nor a maintainer script
creates them. Each service starts with a starting phase: an initialization thread does the work
and retries every 15 seconds until it succeeds, and meanwhile the service listens as usual but
serves only a page of its phase and the failure of the last attempt, answering 503. The web
service also shows the state, the step and the failure of the last run of `vpn-router-setup`,
which setup records in `/run/vpn-router/setup.json` at every step; `vpnrouter.fail` ends with a
`SystemExit` carrying its message, so setup records it. Once the initialization succeeds, the
service serves its real pages.
creates them. Each service does it before it listens, retrying every 15 seconds and logging each
failure, then listens.
Consequences: The order of installation and of the start of PostgreSQL does not matter; a
database that cannot be created shows why on the status page and is created once the cause is
gone. The web unit may write `/etc/vpn-router` for the key and the database password. While the
web service starts, `/status` is not the router's status, and clients see 503.
database that cannot be created is created once the cause is gone, and the reason is in the
service's log. The web unit may write `/etc/vpn-router` for the key and the database password.
## ADR-17: Bootstrap diagnostics run only until the first setup succeeds
Status: Accepted
Context: A new VM is reachable only once its setup has worked, and the reason of a failure must
be readable from the internal network before an SSH session is possible. A service that reports
the setup cannot be one of the services being set up: those import packages that may not load yet
(psycopg could not find libpq before `ldconfig` ran), and they would fail before reporting
anything. Once the router has been set up, SSH is the way to diagnose it. The more a diagnostic
page does, the more ways it has to fail itself.
Decision: Each package ships a bootstrap service of the Python standard library alone, a script
that answers every request with the plain-text output of a fixed list of commands: the setup
state, the services, the failed units, the addresses and the end of the logs. The router's
listens on 17081 and the Router Manager's on 17080, beside the ports of the services, 7081 and
7080, on every address. The units are never enabled and not started at installation;
`vpn-router-setup` starts them while `/var/lib/vpn-router/setup.done` does not exist, and opens
their ports on the internal interface. The first successful run writes that file, closes the ports
and stops them; a failed run leaves them running, so the reason stays readable. Setup records its
state, step and failure in `/run/vpn-router/setup.json` at every step; `vpnrouter.fail` ends with
a `SystemExit` carrying its message, so setup records it.
Consequences: The diagnostics need nothing the setup provides and cannot collide with the real
services. After the first success they never run again, also not after an upgrade or a reboot.
The page shows log lines to whoever reaches the internal network during the first setup.