From 8efe21bfa61411fcba365c6f02012a0805fdd81b Mon Sep 17 00:00:00 2001 From: Slawomir Koszewski Date: Sat, 14 Nov 2026 12:52:44 +0100 Subject: [PATCH] feature: the package installs the Router Manager, which is no longer a package of its own. --- .dockerignore | 11 + .gitignore | 4 + ADR.md | 155 +- debian-package/Dockerfile => Dockerfile | 13 +- README.md | 450 ++- build.sh | 20 + debian-package/build.sh | 17 - debian-package/debian/changelog | 32 + debian-package/debian/config | 10 + debian-package/debian/control | 8 +- debian-package/debian/dirs | 1 + debian-package/debian/install | 6 + debian-package/debian/postinst | 23 +- debian-package/debian/postrm | 11 +- debian-package/debian/rules | 4 + debian-package/debian/templates | 18 + .../vpn-router.vpn-router-manager.service | 21 + .../src/usr/lib/vpn-router/bootstrap | 6 +- debian-package/src/usr/lib/vpn-router/manager | 903 +++++ debian-package/src/usr/lib/vpn-router/setup | 30 +- debian-package/src/usr/lib/vpn-router/upgrade | 108 + .../src/usr/lib/vpn-router/vpnrouter.py | 9 +- .../usr/lib/vpn-router/vpnrouter_backup.py | 3 +- .../usr/lib/vpn-router/vpnrouter_manager.py | 212 ++ .../lib/vpn-router/vpnrouter_platforms/gcp.py | 2 +- .../src/usr/lib/vpn-router/vpnrouter_sites.py | 2 +- .../lib/vpn-router/vpnrouter_web/__init__.py | 3 +- .../lib/vpn-router/vpnrouter_web/status.py | 12 +- .../src/usr/lib/vpn-router/wait-for-libpq | 4 +- .../share/doc/vpn-router/manager.json.example | 6 + .../share/vpn-router/templates/Caddyfile.j2 | 2 +- dev.sh | 65 + docs/account.md | 30 + docs/backup.md | 25 + docs/certificates.md | 30 + docs/dns.md | 56 + docs/docker.md | 11 + docs/groups.md | 18 + docs/index.md | 36 + docs/logs.md | 26 + docs/metrics.md | 22 + docs/remote-access.md | 41 + docs/router.md | 51 + docs/service-accounts.md | 68 + docs/shell.md | 51 + docs/site-files.md | 19 + docs/site-to-site.md | 30 + docs/sites.md | 53 + docs/system.md | 17 + docs/users.md | 34 + debian-package/publish.sh => publish.sh | 15 +- web/.oxlintrc.json | 8 + web/index.html | 27 + web/package-lock.json | 3240 +++++++++++++++++ web/package.json | 40 + web/public/favicon.svg | 10 + web/src/AccountPanel.tsx | 236 ++ web/src/App.tsx | 320 ++ web/src/BackupDialog.tsx | 202 + web/src/BackupPanel.tsx | 204 ++ web/src/BackupSummary.tsx | 53 + web/src/Card.tsx | 17 + web/src/CertificateDialog.tsx | 162 + web/src/CertificatesPanel.tsx | 754 ++++ web/src/ConfirmDialog.tsx | 34 + web/src/DnsDomainDialog.tsx | 71 + web/src/DnsPanel.tsx | 263 ++ web/src/DnsRecordDialog.tsx | 86 + web/src/DnsSettingsDialog.tsx | 102 + web/src/DockerPanel.tsx | 58 + web/src/EditorPanel.tsx | 274 ++ web/src/EkuDialog.tsx | 71 + web/src/GroupDialog.tsx | 89 + web/src/GroupsPanel.tsx | 192 + web/src/HelpPanel.tsx | 178 + web/src/HttpsSettingsDialog.tsx | 54 + web/src/ImportDialog.tsx | 72 + web/src/IssuingCaDialog.tsx | 100 + web/src/LogView.tsx | 19 + web/src/LogsPanel.tsx | 116 + web/src/MetricsPanel.tsx | 264 ++ web/src/MetricsSettingsDialog.tsx | 154 + web/src/NetworkPanel.tsx | 68 + web/src/OtpDialog.tsx | 112 + web/src/PageMenu.tsx | 75 + web/src/PanelBoard.tsx | 265 ++ web/src/PasskeyDialog.tsx | 67 + web/src/PeerDialog.tsx | 78 + web/src/PersonDialog.tsx | 54 + web/src/PersonFields.tsx | 30 + web/src/PkiSettingsDialog.tsx | 177 + web/src/ProfileDialog.tsx | 115 + web/src/RemoteAccessDialog.tsx | 127 + web/src/RemoteAccessPanel.tsx | 178 + web/src/RouterPanel.tsx | 659 ++++ web/src/SearchField.tsx | 22 + web/src/ServiceAccountDialog.tsx | 421 +++ web/src/ServiceAccountsPanel.tsx | 278 ++ web/src/SettingsButton.tsx | 60 + web/src/ShellPanel.tsx | 284 ++ web/src/SignIn.tsx | 78 + web/src/SiteDialog.tsx | 274 ++ web/src/SiteFiles.tsx | 343 ++ web/src/SiteToSiteDialog.tsx | 170 + web/src/SiteToSitePanel.tsx | 185 + web/src/SiteToSiteStatusPanel.tsx | 63 + web/src/SitesPanel.tsx | 260 ++ web/src/SitesSettingsDialog.tsx | 79 + web/src/SystemPanel.tsx | 22 + web/src/TemplateDialog.tsx | 136 + web/src/ThemeMenu.tsx | 26 + web/src/Toolbar.tsx | 12 + web/src/UpdatesNotice.tsx | 274 ++ web/src/UserDialog.tsx | 105 + web/src/UsersPanel.tsx | 355 ++ web/src/WireGuardConfigDialog.tsx | 53 + web/src/WireGuardPeers.tsx | 167 + web/src/api.ts | 1158 ++++++ web/src/main.tsx | 44 + web/src/monaco.ts | 14 + web/src/person.ts | 28 + web/src/saveFile.ts | 8 + web/src/settings.ts | 71 + web/src/useRead.ts | 30 + web/src/wireguard.ts | 104 + web/tsconfig.app.json | 22 + web/tsconfig.json | 4 + web/tsconfig.node.json | 20 + web/vite.config.ts | 36 + 129 files changed, 17381 insertions(+), 104 deletions(-) create mode 100644 .dockerignore rename debian-package/Dockerfile => Dockerfile (70%) create mode 100755 build.sh delete mode 100755 debian-package/build.sh create mode 100644 debian-package/debian/vpn-router.vpn-router-manager.service create mode 100755 debian-package/src/usr/lib/vpn-router/manager create mode 100755 debian-package/src/usr/lib/vpn-router/upgrade create mode 100644 debian-package/src/usr/lib/vpn-router/vpnrouter_manager.py create mode 100644 debian-package/src/usr/share/doc/vpn-router/manager.json.example create mode 100755 dev.sh create mode 100644 docs/account.md create mode 100644 docs/backup.md create mode 100644 docs/certificates.md create mode 100644 docs/dns.md create mode 100644 docs/docker.md create mode 100644 docs/groups.md create mode 100644 docs/index.md create mode 100644 docs/logs.md create mode 100644 docs/metrics.md create mode 100644 docs/remote-access.md create mode 100644 docs/router.md create mode 100644 docs/service-accounts.md create mode 100644 docs/shell.md create mode 100644 docs/site-files.md create mode 100644 docs/site-to-site.md create mode 100644 docs/sites.md create mode 100644 docs/system.md create mode 100644 docs/users.md rename debian-package/publish.sh => publish.sh (77%) create mode 100644 web/.oxlintrc.json create mode 100644 web/index.html create mode 100644 web/package-lock.json create mode 100644 web/package.json create mode 100644 web/public/favicon.svg create mode 100644 web/src/AccountPanel.tsx create mode 100644 web/src/App.tsx create mode 100644 web/src/BackupDialog.tsx create mode 100644 web/src/BackupPanel.tsx create mode 100644 web/src/BackupSummary.tsx create mode 100644 web/src/Card.tsx create mode 100644 web/src/CertificateDialog.tsx create mode 100644 web/src/CertificatesPanel.tsx create mode 100644 web/src/ConfirmDialog.tsx create mode 100644 web/src/DnsDomainDialog.tsx create mode 100644 web/src/DnsPanel.tsx create mode 100644 web/src/DnsRecordDialog.tsx create mode 100644 web/src/DnsSettingsDialog.tsx create mode 100644 web/src/DockerPanel.tsx create mode 100644 web/src/EditorPanel.tsx create mode 100644 web/src/EkuDialog.tsx create mode 100644 web/src/GroupDialog.tsx create mode 100644 web/src/GroupsPanel.tsx create mode 100644 web/src/HelpPanel.tsx create mode 100644 web/src/HttpsSettingsDialog.tsx create mode 100644 web/src/ImportDialog.tsx create mode 100644 web/src/IssuingCaDialog.tsx create mode 100644 web/src/LogView.tsx create mode 100644 web/src/LogsPanel.tsx create mode 100644 web/src/MetricsPanel.tsx create mode 100644 web/src/MetricsSettingsDialog.tsx create mode 100644 web/src/NetworkPanel.tsx create mode 100644 web/src/OtpDialog.tsx create mode 100644 web/src/PageMenu.tsx create mode 100644 web/src/PanelBoard.tsx create mode 100644 web/src/PasskeyDialog.tsx create mode 100644 web/src/PeerDialog.tsx create mode 100644 web/src/PersonDialog.tsx create mode 100644 web/src/PersonFields.tsx create mode 100644 web/src/PkiSettingsDialog.tsx create mode 100644 web/src/ProfileDialog.tsx create mode 100644 web/src/RemoteAccessDialog.tsx create mode 100644 web/src/RemoteAccessPanel.tsx create mode 100644 web/src/RouterPanel.tsx create mode 100644 web/src/SearchField.tsx create mode 100644 web/src/ServiceAccountDialog.tsx create mode 100644 web/src/ServiceAccountsPanel.tsx create mode 100644 web/src/SettingsButton.tsx create mode 100644 web/src/ShellPanel.tsx create mode 100644 web/src/SignIn.tsx create mode 100644 web/src/SiteDialog.tsx create mode 100644 web/src/SiteFiles.tsx create mode 100644 web/src/SiteToSiteDialog.tsx create mode 100644 web/src/SiteToSitePanel.tsx create mode 100644 web/src/SiteToSiteStatusPanel.tsx create mode 100644 web/src/SitesPanel.tsx create mode 100644 web/src/SitesSettingsDialog.tsx create mode 100644 web/src/SystemPanel.tsx create mode 100644 web/src/TemplateDialog.tsx create mode 100644 web/src/ThemeMenu.tsx create mode 100644 web/src/Toolbar.tsx create mode 100644 web/src/UpdatesNotice.tsx create mode 100644 web/src/UserDialog.tsx create mode 100644 web/src/UsersPanel.tsx create mode 100644 web/src/WireGuardConfigDialog.tsx create mode 100644 web/src/WireGuardPeers.tsx create mode 100644 web/src/api.ts create mode 100644 web/src/main.tsx create mode 100644 web/src/monaco.ts create mode 100644 web/src/person.ts create mode 100644 web/src/saveFile.ts create mode 100644 web/src/settings.ts create mode 100644 web/src/useRead.ts create mode 100644 web/src/wireguard.ts create mode 100644 web/tsconfig.app.json create mode 100644 web/tsconfig.json create mode 100644 web/tsconfig.node.json create mode 100644 web/vite.config.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c017a72 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +.git +.claude +.vscode +out +tmp +examples +web/node_modules +web/dist +.venv +dev.json +**/__pycache__ diff --git a/.gitignore b/.gitignore index 1e99881..23ac7a4 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,7 @@ tmp *.tfstate *.tfstate.* *.auto.tfvars +node_modules +web/dist +.venv +dev.json diff --git a/ADR.md b/ADR.md index 279c886..7d6dcaa 100644 --- a/ADR.md +++ b/ADR.md @@ -49,7 +49,7 @@ the overall status and the Client IP, `standard` also the package versions and t active and other services, and `full` also each service and the state of the site-to-site, IKEv2, OpenVPN and WireGuard features. The Client IP is the address the request came from, through the trusted proxies, so a person can see the address the router sees. Nothing comes from the configuration. -`/status` names its content, so clients such as manage-ui and the Router Manager show what it +`/status` names its content, so clients such as Cloud Manager and the Router Manager show what it has. `vpn-router-setup` opens the port on both interfaces in UFW, so the choice of `web.listen` is made by the service alone, and writes the features and the facts it resolves to `/run/vpn-router/web.json`. @@ -227,7 +227,7 @@ no CORS headers, so a browser cannot call it directly, and tokens kept in the pa by its scripts. Decision: `vpn-router-manager` is a Debian package of its own, built in the repository -`linux-cloud-router-manager`. A React and MUI application in the style of manage-ui is built +`linux-cloud-router-manager`. A React and MUI application in the style of Cloud Manager is built with Vite when the package is built and shipped as static files. A Python server, using the Ubuntu packages `python3-requests` and `python3-psycopg`, serves them, signs users in through the router's `/auth`, keeps their tokens in a server-side session in PostgreSQL behind an @@ -650,7 +650,7 @@ Status: Accepted Context: The router had one IPsec site-to-site tunnel, the `remote` section, run as the swanctl connection `remote-site`, with the local identity mode in `wan` and the local subnets in `local`. A router often links more than one site, each with its own peer, subnets and key, and an admin -should add them without a shell. The install answers and the Cloud Platform describe one +should add them without a shell. The install answers and Cloud Manager describe one connection, and the platform rewrites `vpn-router.json` when it rebuilds the VM. Decision: `site_to_site` maps a connection name to its settings: `addrs`, `id`, `cidrs`, @@ -658,7 +658,7 @@ Decision: `site_to_site` maps a connection name to its settings: `addrs`, `id`, without a fallback for the old format. The name, a lowercase letter followed by at most 30 lowercase letters, digits and hyphens, names the swanctl connection, its child and its secret `ike-psk-`, so it is a valid swanctl section name and stays the same while the connection -lives. The install answers and the Cloud Platform write the connection `remote-site`, the first +lives. The install answers and Cloud Manager write the connection `remote-site`, the first one, so its name is the one the tunnel always had. - **Local settings per connection:** each connection has its own `local_cidrs` and @@ -692,7 +692,7 @@ Consequences: A configuration with `remote` is refused, so the package and a con the new format go together. The status shows each connection. A rate across a rekey misses the traffic of the old child SA since the previous sample. A renamed connection starts a new series. A connection added through the API lives in -`vpn-router.json` only; the Cloud Platform's rebuild of the VM writes the file again with its +`vpn-router.json` only; Cloud Manager's rebuild of the VM writes the file again with its one connection, dropping the others. A change through the API is answered before setup has applied it, so a configuration that setup cannot apply shows in the setup state, not in the answer. A connection with a key file keeps it until a key is given through the API, which then @@ -1375,6 +1375,7 @@ last line of defence, every server replaces the default reports: - `vpnrouter_serve.serve`, which the web service and the metrics service run, sets the servers' `handle_error`, `threading.excepthook` and `sys.excepthook`. +- `vpnrouter_manager.serve`, which the Router Manager runs, does the same. - The bootstrap server sets its server's `handle_error` and `sys.excepthook`. A `ConnectionError` of a client that went away is not logged; any other exception is logged in @@ -1396,3 +1397,147 @@ columns. `release.py` joins its lines with single spaces into the commit message Consequences: The commit message is one line however the summary is wrapped, so a summary rewrapped later still matches the commit of its release. + +## ADR-50: The help is Markdown rendered in the browser, in a panel that pushes the page + +Status: Accepted + +Context: The Router Manager's pages were explained only in its README. Help should be written as +plain files next to the code, shipped with the package, readable only after signing in, and able +to show pictures. serve-markdown renders Markdown in the browser with marked, DOMPurify and +github-markdown-css. Google Cloud's console shows its Learn panel on the right and moves the page +left, so the help never covers what it explains. + +Decision: The help is the Markdown files of `docs/` (plain Markdown, no MDX), with SVG and PNG +pictures, installed to `/usr/share/vpn-router/manager/docs` and served by the manager at +`/docs/` behind the session, with no-sniff and a CSP that keeps an SVG from running +scripts. The browser renders them with marked (GFM) and DOMPurify, styled with +github-markdown-css in the Manager's light or dark scheme, without syntax highlighting or +diagrams. The help icon in the header opens a persistent drawer on the right whose width the page +gives up; it opens at the topic of the page and tab shown, and links between topics stay in the +panel. + +Consequences: Help is edited as Markdown and released with the package. The Manager depends on +marked, DOMPurify and github-markdown-css. A page without a topic of its own shows the index. The +help is not reachable without a session. + +## ADR-51: The files of web sites pass the manager streamed, outside the JSON proxy + +Status: Accepted + +Context: The Web Sites page uploads and downloads the files of the router's web sites, up to the +router's `sites.upload_max_mb`, by default 512 MiB. The manager's proxy reads a request body of at +most 64 KiB into memory, sends it as JSON and holds the whole answer; the API helper of the page +parses every answer as JSON. Encoding files in JSON would grow them by a third and still need the +whole file in memory. + +Decision: `GET`, `HEAD` and `PUT` of `/api/sites//files/...` take their own route in the +manager: the request body is passed to the router as it is read, with its `Content-Type` and +`Content-Length`, and the router's answer is passed back as it arrives, with its `Content-Type`, +`Content-Length` and `Content-Disposition`. The page uploads with `XMLHttpRequest`, whose upload +events give the progress `fetch` does not, sending the file as the raw body, a zip archive with +`?unzip=true`, and downloads with `fetch` into a blob. Folders, renaming and removal stay JSON +calls of the usual proxy. + +Consequences: Large files cross the manager without filling its memory. An upload needs a +`Content-Length`, which browsers send for a file. Dropped folders are not uploaded as trees; a zip +archive carries a tree. + +## ADR-52: On a workstation, the router's URL is the Terraform output router_web_url + +Status: Accepted + +Context: With `terraform_dir`, the manager built `http://:7081`. +The port of the router's web service is `web.port` and the interfaces it listens on are `web.listen`, +which the platform sets from its variables; the manager that `dev.sh` runs on a workstation cannot read them. + +Decision: `router_url` is the output `router_web_url` of the platform, which derives the URL from +the same variables that configure the router. + +Consequences: The manager follows the router's port and interfaces without settings of its own; a +state written before the platform had the output gives no `router_url` until the next deploy. + +## ADR-53: The system's packages are upgraded with apt-get upgrade, apart from the router's package + +Status: Accepted + +Context: The Overview tab upgrades only `vpn-router`. The router's +operating system needs its other packages upgraded too, from the same page. An upgrade restarts +services, the manager among them, so it cannot run inside the manager's unit. + +Decision: The System tab lists what `apt-get -s upgrade` would upgrade and runs `apt-get upgrade +-y` through the same helper as the router's package, with `--system`, in its own transient unit +and with its own state and log files, so each tab shows its own last upgrade. `upgrade`, not +`dist-upgrade` or `full-upgrade`, so nothing is installed or removed; a package that needs a new +dependency is kept back. The manager refuses an upgrade while the other runs; the check for +updates is the same `apt-get update` for both. + +Consequences: Kept-back packages, a new kernel series among them, stay until upgraded on the +router. An upgrade of the system also upgrades the router's package when it is newer, which +the Overview tab's state does not record. + +## ADR-54: The Shell page's editor is Monaco, opened by vre and loaded on demand + +Status: Accepted + +Context: Administrators edit files on the router from the Shell page. The page needs no file +browser: the shell names the files, as `$EDITOR` would. Monaco is VS Code's editor component and +is published as the ESM package `monaco-editor`. Its full entry point brings the language services +of CSS, HTML, JSON and TypeScript, each with a worker, and its bundle is several megabytes. + +Decision: The router's `vre` names a file in a text message over the shell's WebSocket, which the +manager relays unchanged. The page reads and saves the file through the JSON proxy, whose body +limit is raised for `/api/shell/editor/` alone to hold a file of up to 1 MiB escaped in JSON. +Monaco 0.57 is bundled from npm through its tree-shakeable entry points: `monaco-editor/editor` +with all editor features and all language definitions, and only the editor worker. It is a chunk +of its own, imported when the first file opens, and Vite's preload helper has a chunk of its own, +so the entry does not load Monaco. + +Consequences: The editor colours the syntax of every language Monaco defines, but has no +validation, completion or formatting by language services. The page loads Monaco, about 1.1 MB +compressed, the first time a file opens. + +## ADR-55: index.html sets the color scheme before the first paint + +Status: Accepted + +Context: Each page of the manager is a document of its own, so a page switch loads `index.html` +again. MUI sets the color scheme class on `` and the page background from JavaScript, once +React renders, which waits for the bundle and for `GET /info`. Until then the browser paints its +default white page. + +Decision: An inline script in the head of `index.html` reads the mode MUI stores in localStorage +(`mui-mode`, `system` when not stored), as MUI's `InitColorSchemeScript` does, and adds the class +`light` or `dark` to ``. An inline style gives `html.dark` the dark color scheme and MUI's +default dark background, `#121212`. The page has no meta color-scheme, which would follow the +system against a stored light or dark choice. + +Consequences: A page in the dark theme is dark from its first paint. The script depends on the +storage key and class names of MUI's defaults, which `colorSchemeSelector: 'class'` uses. + +## ADR-56: The Router Manager is part of the vpn-router package + +Status: Accepted + +Context: The Router Manager runs only on the router: it imports `vpnrouter_net`, `vpn-router-setup` +opens its ports and creates its first administrator, Caddy proxies it and the status reports it. +As a package of its own it would pin the router's version and the router its, and a change of the +router's API would need two repositories and two changelogs. + +Decision: One package, `vpn-router`, installs the manager with the router. The manager's programs +are in `/usr/lib/vpn-router`, its application and help in `/usr/share/vpn-router/manager`, its +settings and secrets in `/etc/vpn-router` (`manager.json`, `manager-database.secret`, +`manager-admin.json`) and the state of its upgrades in `/var/lib/vpn-router`. The unit +`vpn-router-manager.service`, its transient units and its database keep their names. The debconf +questions of its first administrator are `vpn-router/admin_username` and `admin_password`. The +router's bootstrap page shows the manager, and the manager has no bootstrap service of its own. +The package carries no migration from the separate package: it has no relation to +`vpn-router-manager`, and dpkg refuses to install it beside that package, since both have +`vpn-router-manager.service`. `dev.sh` still runs the manager on a workstation, with +`terraform_dir` and `bind`. + +Consequences: A router with the separate package is moved to this one by hand: remove +`vpn-router-manager` without purging it, move its files to the new paths, then install +`vpn-router`. A backup made before the merge restores the manager's files to +`/etc/vpn-router-manager`, where the manager no longer reads them. The repository holds the +manager's `web/` and `docs/`, and its build runs from the root. diff --git a/debian-package/Dockerfile b/Dockerfile similarity index 70% rename from debian-package/Dockerfile rename to Dockerfile index 8cc7383..ca46b45 100644 --- a/debian-package/Dockerfile +++ b/Dockerfile @@ -1,4 +1,13 @@ +ARG NODE_VERSION=24 ARG UBUNTU_VERSION=24.04 + +FROM node:${NODE_VERSION}-bookworm-slim AS web +WORKDIR /web +COPY web/package.json web/package-lock.json ./ +RUN npm ci +COPY web/ ./ +RUN npm run build + FROM ubuntu:${UBUNTU_VERSION} ARG HOST_UID ARG HOST_GID @@ -12,7 +21,9 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* WORKDIR /build -COPY . src/ +COPY debian-package/ src/ +COPY docs/ src/docs/ +COPY --from=web /web/dist src/web/dist/ RUN cd src && dpkg-buildpackage -us -uc -b RUN lintian --no-tag-display-limit /build/*.changes || true diff --git a/README.md b/README.md index c0bd15b..8603fac 100644 --- a/README.md +++ b/README.md @@ -769,9 +769,9 @@ address, and `--email ''` removes it. Changing a user's password, disabling, unl refresh tokens of `/auth` and its codes, tokens and sessions of the OpenID Provider. The exit status is 0 on success, 2 for an invalid request, 3 when the user does not exist, 4 when the user exists and 1 for any other failure. -When the Router Manager is installed with an administrator (its debconf questions -`vpn-router-manager/admin_username` and `admin_password`), its package leaves the answers in -`/etc/vpn-router-manager/admin.json` (root only). `vpn-router-setup` then creates that user as a +When the package is installed with a Router Manager administrator (the debconf questions +`vpn-router/admin_username` and `admin_password`), it leaves the answers in +`/etc/vpn-router/manager-admin.json` (root only). `vpn-router-setup` then creates that user as a Manager-only account, adds it to the group `admins`, which it creates when missing, and removes the file. @@ -943,7 +943,7 @@ configuration: no key, identity, subnet, or address but the client's. They show | `status_content` | `/` and `/status` show | |---|---| | `basic` (default) | The overall status (`ok` or `degraded`), the time and the Client IP | -| `standard` | Also the `vpn-router` version, the `vpn-router-manager` version or that it is not installed, and the number of active services and of services in any other state, without their names | +| `standard` | Also the `vpn-router` version and the number of active services and of services in any other state, without their names | | `full` | Also each service and its state, Docker, how many site-to-site connections are set up and how many of them are established (`site_to_site`: `connections` and `established`, `null` when swanctl cannot be asked), IKEv2, OpenVPN and WireGuard | The Client IP is the address the request came from: the last entry of `X-Forwarded-For` for a @@ -966,16 +966,16 @@ The service is `/usr/lib/vpn-router/web`, which starts it, and the package ### Bootstrap diagnostics Until a run of `vpn-router-setup` has succeeded, every run starts `vpn-router-bootstrap`, which -serves diagnostics as plain text on port 17081 of every address, and the firewall opens 17081 and -17080 (the Router Manager's bootstrap) on the internal interface. The first successful run stops -the bootstrap services and closes their ports, and writes `/var/lib/vpn-router/setup.done`, after -which they never run again; a failed run leaves them running, so the reason stays readable until +serves diagnostics as plain text on port 17081 of every address, and the firewall opens 17081 +on the internal interface. The first successful run stops +the bootstrap service and closes its port, and writes `/var/lib/vpn-router/setup.done`, after +which it never runs again; a failed run leaves it running, so the reason stays readable until a later run succeeds. The unit is never enabled. The page, read again every 10 seconds, shows the state, the step and the failure of the last setup run (`/run/vpn-router/setup.json`, which setup writes at every step), the state of the router's services, the failed units, the addresses and routes, `cloud-init status`, and the end of -the logs of `vpn-router-setup` and `vpn-router-web`: +the logs of `vpn-router-setup`, `vpn-router-web` and `vpn-router-manager`: ```bash curl http://10.0.0.4:17081/ @@ -999,11 +999,10 @@ curl http://10.0.0.4:17081/ The status holds the state of `vpn-router-setup`, `slapd` and each PostgreSQL cluster unit (`postgresql@-`, or `postgresql` as `no cluster` without one), of -`vpn-router-manager` when that package is installed, of `strongswan` when site-to-site or +`vpn-router-manager`, of `strongswan` when site-to-site or IKEv2 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 5 seconds; the package version and, -as `manager_version`, the installed version of `vpn-router-manager` (`null`, shown as not installed, without it); whether +OpenVPN clients, from the server's status file, which OpenVPN rewrites every 5 seconds; the package version; 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`; as `site_to_site`, the number of site-to-site connections that are set up (`connections`) and of those established (`established`, `null` when @@ -1150,7 +1149,7 @@ vre /etc/vpn-router/vpn-router.json ~/notes.txt ### The /config page -`/config` shows what `/` and `/status` leave out, for a client such as manage-ui: +`/config` shows what `/` and `/status` leave out, for a client such as Cloud Manager: | Key | Content | | --- | --- | @@ -1477,7 +1476,7 @@ and the facts for `/config` from `/run/vpn-router/web.json` (readable by root on ## HTTPS Caddy serves the status web service on `https.web_port` (7444 by -default), while the Router Manager is installed the manager on `https.manager_port` (7443), and +default), the Router Manager on `https.manager_port` (7443), and while metrics are enabled the metrics service on `metrics.https_port` (9465), all for `wan.local_fqdn`, while `https.enabled` is `true` and `wan.local_fqdn` is set. It obtains and renews the certificate from Let's Encrypt with the HTTP challenge, so `wan.local_fqdn` must resolve to the router's public address and port 80 must be reachable from the internet; the @@ -1967,10 +1966,9 @@ A backup holds the state that `vpn-router-setup` cannot make again: | Item | Content | |---|---| -| `/etc/vpn-router` | `vpn-router.json`, the secrets and `rclone.conf` | +| `/etc/vpn-router` | `vpn-router.json`, the secrets, the Router Manager's settings and `rclone.conf` | | `/etc/wireguard` | The WireGuard keys and the peers | | `/etc/ufw`, `/etc/default/ufw` | The firewall rules | -| `/etc/vpn-router-manager` | The Router Manager's settings, when it is installed | | `files/` | Each `psk_file` outside `/etc/vpn-router` | | `ldap/vpn-router.ldif` | The user directory, written by `slapcat`: users, groups, service accounts, password hashes and TOTP secrets, and the PKI: the CAs and their keys, the root key while it is on the router, and the certificates issued, with the keys of the router's and the users' certificates | | `postgresql/vpn-router.dump` | With `backup.metrics`, the database of the metrics and the refresh tokens, written by `pg_dump -Fc` | @@ -2051,13 +2049,398 @@ What it replaces is kept in `/var/lib/vpn-router/backup/before-restore/