feature: the web service shows and changes the settings of the metrics service at /metrics/settings, and OpenVPN starts on a new router, as setup installs CRLs that revoke nothing.
Release / release (push) Failing after 14m34s

This commit is contained in:
slawek committed 2026-10-28 22:31:27 +01:00
1 parent 2dc4db440f
commit 5303779559
3 files changed
+60 -5

No files matched your search

+13 -1
View File
@@ -1007,7 +1007,7 @@ curl http://10.0.0.4:7081/status
### Tokens and signing in
`/config`, `/users`, `/service-accounts`, `/groups`, `/site-to-site`, `/backup`, `/remote-access`, `/ca`, `/logs`, `/sites`, `/dns`, `/shell` and `/account` need a JWT in an `Authorization: Bearer` header, signed with
`/config`, `/metrics/history`, `/metrics/settings`, `/users`, `/service-accounts`, `/groups`, `/site-to-site`, `/backup`, `/remote-access`, `/ca`, `/logs`, `/sites`, `/dns`, `/shell` 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
@@ -1332,6 +1332,18 @@ The answer of `GET /backup` holds:
A change is made, checked and applied as for `/site-to-site`.
### The /metrics/settings API
`/metrics/settings` shows and changes the settings of the metrics service, with the same token as
`/users`.
| Request | Body | Answer |
| --- | --- | --- |
| `GET /metrics/settings` | | `{"settings"}`: the `metrics` section without `otlp_password`, with `otlp_password_set` |
| `PUT /metrics/settings` | any of the settings of the `metrics` section; an empty or absent `otlp_password` keeps the secret, and an empty `otlp_username` removes it | `{"settings"}` |
A change is made, checked and applied as for `/site-to-site`.
### The /remote-access API
`/remote-access` shows and changes the remote access systems, IKEv2 (`p2s`),
+5 -2
View File
@@ -1,7 +1,10 @@
vpn-router (0.33.4-1) unstable; urgency=medium
vpn-router (0.34.0-1) unstable; urgency=medium
fix: OpenVPN starts on a new router, as setup installs CRLs that revoke nothing.
feature: the web service shows and changes the settings of the metrics service at /metrics/settings, and OpenVPN starts on a new router, as setup installs CRLs that revoke nothing.
* GET and PUT /metrics/settings show and change the metrics section;
otlp_password is never shown, an empty one keeps it, and an empty
otlp_username removes it.
* A CRL without revoked certificates is installed for strongSwan and
OpenVPN.
+42 -2
View File
@@ -47,6 +47,9 @@ Pages:
/metrics/history
the metrics the metrics service keeps, for a view: hour, day, month or year (see
metrics_history())
/metrics/settings
the settings of the metrics service: show and change, applied by vpn-router-setup (see
metrics_settings())
/logs the last lines of the journal of the router's units and of the files web.log_files lists
(see logs())
/sites the web sites Caddy serves: the settings of sites, list, add, change and remove (see sites()),
@@ -62,7 +65,7 @@ Pages:
loopback address, where Caddy passes the requests of the HTTPS site; POST /oidc/login counts
towards the limit of /auth calls
/status/full, /config, /metrics/history, /postgresql, /users, /service-accounts, /groups, /site-to-site, /backup, /remote-access, /ca, /logs, /sites, /dns, /shell and /account need an HS256 JWT in an Authorization: Bearer header signed
/status/full, /config, /metrics/history, /metrics/settings, /postgresql, /users, /service-accounts, /groups, /site-to-site, /backup, /remote-access, /ca, /logs, /sites, /dns, /shell and /account need an HS256 JWT in an Authorization: Bearer header signed
with vpnrouter_auth.WEB_KEY (see vpnrouter_auth.verify). A token of /auth names a user in sub;
/account serves that user, and the other paths need a member of vpnrouter_users.ADMIN_GROUP. A
token of vpn-router-token has the admin claim and names no user; it may call every path but
@@ -1351,6 +1354,39 @@ def metrics_history(view):
return 500, {'error': str(exc.code)}
def metrics_shown(settings):
"""The metrics section as /metrics/settings shows it: its settings but otlp_password, with otlp_password_set."""
return {**{option: value for option, value in settings.items() if option != 'otlp_password'},
'otlp_password_set': bool(settings['otlp_password'])}
def metrics_settings(method, body):
"""Answer a /metrics/settings request: the HTTP status and the JSON document.
GET /metrics/settings {"settings"}: the metrics section without otlp_password, with otlp_password_set
PUT /metrics/settings change any of the settings of the metrics section; an empty or absent otlp_password keeps
the current secret, and an empty otlp_username removes it
A change is applied by restarting vpn-router-setup without waiting for it.
"""
if body == 'invalid' or (body is not None and not isinstance(body, dict)):
return 400, {'error': 'the body must be a JSON object'}
if method in ('GET', 'HEAD'):
try:
return 200, {'settings': metrics_shown(vpnrouter.load_config()['metrics'])}
except SystemExit as exc:
return 500, {'error': str(exc.code)}
if method != 'PUT':
return 404, {'error': f'no route {method} /metrics/settings'}
body = dict(body or {})
if not body.get('otlp_password'):
body.pop('otlp_password', None)
if body.get('otlp_username') == '':
body['otlp_password'] = ''
changed = section_change('metrics', body)
return changed if isinstance(changed, tuple) else (200, {'settings': metrics_shown(changed)})
def changes_admins(connection, method, parts, body):
"""Whether a /users, /service-accounts or /groups request creates, renames to, changes or removes ADMIN_GROUP, or
removes a member of it."""
@@ -1819,7 +1855,8 @@ class Handler(BaseHTTPRequestHandler):
directory = next((name for name in ('users', 'service-accounts', 'groups', 'account', 'site-to-site', 'backup',
'ca', 'shell', 'remote-access', 'logs', 'sites', 'dns')
if path == f'/{name}' or path.startswith(f'/{name}/')), None)
protected = path in ('/status/full', '/config', '/metrics/history', '/postgresql') or directory is not None
protected = path in ('/status/full', '/config', '/metrics/history', '/metrics/settings', '/postgresql') or \
directory is not None
signing = path in ('/auth', '/auth/passkey', '/auth/passkey/complete', '/auth/refresh', '/auth/logout')
oidc = (path == '/.well-known/openid-configuration' or path.startswith('/oidc/')) and \
self.server.server_address[0] == vpnrouter_net.LOOPBACK
@@ -1861,6 +1898,9 @@ class Handler(BaseHTTPRequestHandler):
query = urllib.parse.parse_qs(urllib.parse.urlsplit(self.path).query)
code, document = metrics_history(query.get('range', ['hour'])[0])
body, kind = json.dumps(document).encode() + b'\n', 'application/json'
elif path == '/metrics/settings':
code, document = metrics_settings(self.command, payload)
body, kind = json.dumps(document, indent=2).encode() + b'\n', 'application/json'
elif directory == 'shell' and (path, self.command) == ('/shell', 'GET'):
return self.shell(claims.get('sub'))
elif directory == 'shell':