feature: the router makes scheduled backups to a local folder, copied to the cloud with rclone, or to a Proxmox Backup Server, and vpn-router-backup restores them.
Release / release (push) Successful in 13s

This commit is contained in:
slawek committed 2026-10-25 20:18:00 +01:00
1 parent 7d1f639508
commit fe45272f7c
16 files changed
+986 -19

No files matched your search

+48
View File
@@ -679,3 +679,51 @@ one connection, dropping the others. A change through the API is answered before
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
replaces the file.
## ADR-26: Backups are a staged, consistent copy, archived locally with tar and zstd or sent to a Proxmox Backup Server
Status: Accepted
Context: An image backup of the VM, such as Azure Backup, restores the whole disk. It cannot be
exported as a file, read on another system, or restored onto a new VM. The state that matters is
small, but it is spread over several places: `vpn-router.json` and the secrets, the PKI, the
WireGuard keys and peers, the firewall rules, the slapd directory and, optionally, the
PostgreSQL database. Only part of it can be made again: the CA, the password hashes and the TOTP
secrets cannot. The API and the command-line tools change the configuration, the PKI and the
directory while the router runs.
Decision: `vpn-router-backup run`, started by a timer on `backup.schedule`, stages a copy of that
state in one directory and then archives or sends it.
- **Consistency:** the files are copied and the directory is written with `slapcat` while the
configuration lock and the users lock are held, the locks every change of the API and the tools
takes, so a user, their certificate and the CRL are copied together. `slapcat` reads the MDB
database consistently while slapd runs (ADR-4), and `pg_dump` reads a snapshot. Files that
`vpn-router-setup` regenerates are left out.
- **Local target:** a tar archive compressed with zstd, both from the Ubuntu archive, so a local
backup needs nothing else and the archive can be read on any Linux system. A `pxar` archive
would need Proxmox's client and is not compressed.
- **Retention:** local archives are pruned by the rules of Proxmox Backup Server's prune, so the
settings mean the same for both targets and can be checked with Proxmox's prune simulator.
Settings finer than the schedule do not apply.
- **Cloud copy:** rclone, from the Ubuntu archive, copies the archives to any storage it
supports. The remote is defined in `/etc/vpn-router/rclone.conf` with `rclone config`, since
each backend has its own settings. `vpn-router.json` names only the remote, and the
configuration file is part of the backup. `sync` makes retention apply to the remote as well;
`copy` leaves it to the storage's lifecycle rules. Only this host's archives are included, so a
shared remote path is safe.
- **Proxmox Backup Server:** the static `proxmox-backup-client` sends the staged directory as one
`pxar` archive of a host snapshot. Deduplication makes frequent backups cheap. The server's
prune jobs keep the snapshots, so the token needs only `DatastoreBackup`, which cannot delete.
The client is a suggestion, not a dependency, because it comes from Proxmox's repository, which
an Ubuntu system does not know. Without it, only a PBS backup fails.
- **Restore:** only on the command line, as root. It replaces the directory and the database, so
it stops their services, keeps what it replaces and lets `vpn-router-setup` rebuild the rest.
The API offers no restore, so a stolen admin session cannot replace the router's keys.
Consequences: A backup holds every secret of the router, including the CA key, in plain form
unless a PBS key file encrypts it, so where it is kept must be as safe as the router. A restore
onto another VM brings back the old interface names and addresses, which may need changing. The
timer's schedules are systemd's shorthands, so all backups of one schedule start at the same
moment. A change of the users between the end of the copy and the next backup is in no backup
until then.