diff --git a/README.md b/README.md index 12f3d81..3f4b4bd 100644 --- a/README.md +++ b/README.md @@ -526,10 +526,12 @@ curl http://10.0.0.4:17081/ | `/users` | The users of OpenVPN and road-warrior access; needs a token | | `/groups` | The groups of users; needs a token | -The status holds the state of `vpn-router-setup` and `slapd`, of `strongswan` when site-to-site or +The status holds the state of `vpn-router-setup`, `slapd` and each PostgreSQL cluster unit +(`postgresql@-`, or `postgresql` as `no cluster` without one), of `strongswan` when site-to-site or road-warrior access is configured, of `wg-quick@wg0` when WireGuard is, and of `openvpn-server@vpn-router` when OpenVPN is; the number of connected -OpenVPN clients, from the server's status file, which OpenVPN rewrites every minute; the package version; whether +OpenVPN clients, from the server's status file, which OpenVPN rewrites every minute; the package version and, +as `manager_version`, the installed version of `vpn-router-manager` (`null`, shown as not installed, without it); whether Docker is installed, and when it is, the state of `docker.service` and of `vpn-router-docker` (the Docker rules), which do not change `status`; whether the site-to-site tunnel is established; the number of connected road-warrior (IPsec P2S) clients; and diff --git a/debian-package/debian/changelog b/debian-package/debian/changelog index 7f0d0f1..a400081 100644 --- a/debian-package/debian/changelog +++ b/debian-package/debian/changelog @@ -1,3 +1,14 @@ +vpn-router (0.11.0-1) unstable; urgency=medium + + feature: the status page shows the Router Manager's version. + + * / and /status show the installed version of vpn-router-manager, as + manager_version in JSON, or that it is not installed. + * The services show the state of each PostgreSQL cluster, as + postgresql@-, or postgresql: no cluster without one. + + -- Slawomir Koszewski Sun, 25 Oct 2026 09:00:00 +0200 + vpn-router (0.10.0-1) unstable; urgency=medium feature: Manager-only accounts, and the Router Manager's first administrator. diff --git a/debian-package/src/usr/lib/vpn-router/web b/debian-package/src/usr/lib/vpn-router/web index 6011a82..e3a8258 100644 --- a/debian-package/src/usr/lib/vpn-router/web +++ b/debian-package/src/usr/lib/vpn-router/web @@ -26,7 +26,8 @@ served (404) while the key does not exist. /auth is limited to AUTH_LIMIT calls address in AUTH_WINDOW seconds; the address of a call from one of web.trusted_proxies, by default the address the service listens on, is the last entry of its X-Forwarded-For header. -/ and /status show only the status: whether the services run, the package version, whether +/ and /status show only the status: whether the services run, the package version and the +installed version of vpn-router-manager (manager_version, null when it is not installed), whether Docker is installed, and the state of site-to-site, road-warrior (IPsec P2S), OpenVPN and WireGuard. No configuration data, key or address is shown. @@ -143,9 +144,10 @@ def unit_state(unit): return result.stdout.strip() if result and result.stdout.strip() else 'unknown' -def package_version(): - result = run('dpkg-query', '-W', '-f=${Version}', 'vpn-router') - return result.stdout.strip() if result and result.returncode == 0 else None +def package_version(package='vpn-router'): + """The installed version of the package, or None when it is not installed.""" + result = run('dpkg-query', '-W', '-f=${Version}', package) + return (result.stdout.strip() or None) if result and result.returncode == 0 else None def docker_status(): @@ -239,9 +241,20 @@ def openvpn_clients(): return None +def postgresql_clusters(): + """The state of each PostgreSQL cluster unit (postgresql@-), or {'postgresql': 'no cluster'} without one. + + postgresql.service, which only runs /bin/true, is not reported. + """ + lines = lines_of('systemctl', 'list-units', 'postgresql@*.service', '--all', '--plain', '--no-legend') or [] + names = [line.split()[0].removesuffix('.service') for line in lines if line.strip()] + return {name: unit_state(f'{name}.service') for name in names} or {'postgresql': 'no cluster'} + + def collect(state): """The status document. state says which features the router has.""" - units = {'vpn-router-setup': unit_state('vpn-router-setup.service'), 'slapd': unit_state('slapd.service')} + units = {'vpn-router-setup': unit_state('vpn-router-setup.service'), 'slapd': unit_state('slapd.service'), + **postgresql_clusters()} if state.get('site_to_site') or state.get('p2s'): units['strongswan'] = unit_state('strongswan.service') if state.get('wireguard'): @@ -252,6 +265,7 @@ def collect(state): status = { 'status': 'ok' if all(value == 'active' for value in units.values()) else 'degraded', 'version': package_version(), + 'manager_version': package_version('vpn-router-manager'), 'time': time.strftime('%Y-%m-%dT%H:%M:%S%z'), 'units': units, 'docker': docker_status(), @@ -327,6 +341,7 @@ def details(state): def text_of(status): """The status for people, as plain text.""" lines = ['VPN Router', f'Status: {status["status"]}', f'Version: {status["version"] or "unknown"}', ''] + lines.append(f'Router Manager: {status["manager_version"] or "not installed"}') lines += [f'{name}: {value}' for name, value in status['units'].items()] docker = status['docker'] lines.append('Docker: not installed' if not docker['installed']