Compare commits

..
2 Commits
5 changed files with 52 additions and 4 deletions

No files matched your search

+1
View File
@@ -1160,6 +1160,7 @@ vre /etc/vpn-router/vpn-router.json ~/notes.txt
| `docker` | `docker.exposed_ports` and the rules of the `VPN-ROUTER-DOCKER` chain |
| `postgresql` | `clusters`: each PostgreSQL cluster of `pg_lsclusters` with its version, name, port, status and data directory, and for one that is online its `settings`: `server_version`, `listen_addresses`, `port`, `max_connections`, `shared_buffers`, `effective_cache_size`, `work_mem`, `password_encryption`, `ssl` and `timezone`; `null` when `pg_lsclusters` fails |
| `caddy` | The installed Caddy: `version`, `source`, `upstream` once Caddy's repository is configured and `ubuntu` otherwise, and `service`, the state of `caddy.service`; `null` when Caddy is not installed |
| `oidc` | The OpenID Provider: `enabled`, and `issuer`, its issuer URL, `null` while it is disabled |
### The /postgresql page
+19
View File
@@ -1,3 +1,22 @@
vpn-router (0.37.2-1) unstable; urgency=medium
fix: the web service, the metrics service and the bootstrap server log a failure in one line instead of a traceback, and nothing when a client goes away.
* A request whose client closed the connection is no longer logged.
* Any other failure of a request, a thread or the service is logged in
one line with the exception's type and message.
-- Slawomir Koszewski <slawek@koszewscy.waw.pl> Fri, 30 Oct 2026 01:59:19 +0100
vpn-router (0.37.1-1) unstable; urgency=medium
fix: /config answers the OpenID Provider's state and issuer.
* /config has oidc: enabled, and issuer, the issuer URL, null while the
provider is disabled.
-- Slawomir Koszewski <slawek@koszewscy.waw.pl> Fri, 30 Oct 2026 01:53:55 +0100
vpn-router (0.37.0-1) unstable; urgency=medium
feature: the OpenID Provider has the client credentials and device authorization grants, public clients, and service accounts as resources with roles and scopes in JWT access tokens.
@@ -8,6 +8,7 @@ the logs.
"""
import subprocess
import sys
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
PORT = 17081
@@ -51,5 +52,15 @@ class Handler(BaseHTTPRequestHandler):
self.wfile.write(body)
def failed(request, client_address):
"""Log a failed request in one line, and nothing when the client went away."""
failure = sys.exc_info()[1]
if not isinstance(failure, ConnectionError):
print(f'a request from {client_address[0]} failed: {type(failure).__name__}: {failure}', file=sys.stderr, flush=True)
if __name__ == '__main__':
ThreadingHTTPServer(('', PORT), Handler).serve_forever()
sys.excepthook = lambda kind, value, _: print(f'failed: {kind.__name__}: {value}', file=sys.stderr, flush=True)
server = ThreadingHTTPServer(('', PORT), Handler)
server.handle_error = failed
server.serve_forever()
@@ -4,6 +4,7 @@ Only the Python standard library is used, so the bootstrap server, which must ru
is broken, can use it.
"""
import sys
import threading
import time
from http.server import ThreadingHTTPServer
@@ -14,9 +15,21 @@ def serve(wanted, handler, log, interval):
wanted answers ([(address, port), ...], '') or ([], the reason not to listen). A server whose
place is no longer wanted is stopped, and a new one listens at each new place; a place that
cannot be bound is tried again the next round. Every new message is logged with log. Does
not return.
cannot be bound is tried again the next round. Every new message is logged with log. A request
whose client went away is not logged; any other exception a request, a thread or the process
does not handle is logged with log in one line, without a traceback. Does not return.
"""
def failed(request, client_address):
failure = sys.exc_info()[1]
if not isinstance(failure, ConnectionError):
log(f'a request from {client_address[0]} failed: {type(failure).__name__}: {failure}')
def thread_failed(hook):
if not issubclass(hook.exc_type, (SystemExit, ConnectionError)):
log(f'{hook.thread.name if hook.thread else "a thread"} failed: {hook.exc_type.__name__}: {hook.exc_value}')
threading.excepthook = thread_failed
sys.excepthook = lambda kind, value, _: log(f'failed: {kind.__name__}: {value}')
servers, reported = {}, None
while True:
targets, reason = wanted()
@@ -34,6 +47,7 @@ def serve(wanted, handler, log, interval):
except OSError as exc:
messages.append(f'cannot listen on {target[0]}:{target[1]}: {exc}')
continue
server.handle_error = failed
thread = threading.Thread(target=server.serve_forever, name=f'server {target[0]}', daemon=True)
thread.start()
servers[target] = (server, thread)
@@ -211,9 +211,11 @@ def postgresql_databases():
def details(state):
"""What /config shows: the configuration without its secrets, and what the router resolved and runs; caddy is the
installed Caddy, {version, source, service}, service the state of caddy.service, null when it is not installed."""
installed Caddy, {version, source, service}, service the state of caddy.service, null when it is not installed; oidc
is {enabled, issuer} of the OpenID Provider, issuer null while it is disabled."""
configuration = state.get('configuration', {})
caddy = package_version('caddy')
oidc = configuration.get('oidc', {}).get('enabled') is True
return {
'configuration': configuration,
'network': dict(state.get('network', {}), routes=routes()),
@@ -230,6 +232,7 @@ def details(state):
# Caddy is upstream's once its repository is configured (vpn-router-caddy).
'caddy': {'version': caddy, 'source': 'upstream' if vpnrouter.CADDY_APT_SOURCE.is_file() else 'ubuntu',
'service': unit_state('caddy.service')} if caddy else None,
'oidc': {'enabled': oidc, 'issuer': vpnrouter.oidc_issuer(configuration) if oidc else None},
}