From 083ad9a5963c0a8708b351921ab8313d3b0381d6 Mon Sep 17 00:00:00 2001 From: Slawomir Koszewski Date: Mon, 10 Aug 2026 09:02:45 +0200 Subject: [PATCH] Reengineed the code. Generalized the package. Cloud configurators are modules. --- .gitignore | 2 + README.md | 221 ++++++- cloud-router-cloud-init.yaml.tpl | 28 - debian-package/Dockerfile | 3 + debian-package/build.sh | 4 +- debian-package/debian/changelog | 2 +- debian-package/debian/config | 37 +- debian-package/debian/control | 32 +- debian-package/debian/copyright | 2 +- debian-package/debian/dirs | 14 +- debian-package/debian/install | 13 +- debian-package/debian/postinst | 73 +-- debian-package/debian/postrm | 49 ++ debian-package/debian/prerm | 10 +- debian-package/debian/rules | 3 + debian-package/debian/templates | 146 +++-- .../vpn-router.vpn-router-setup.service | 13 + debian-package/publish.sh | 14 +- ...9-cloud-router.conf => 99-vpn-router.conf} | 0 .../src/opt/cloud-router/bin/simple-ca | 208 ------ .../src/usr/lib/cloud-router/configure | 257 -------- .../src/usr/lib/vpn-router/configure | 22 + .../src/usr/lib/vpn-router/seed-config | 72 +++ debian-package/src/usr/lib/vpn-router/setup | 337 ++++++++++ .../src/usr/lib/vpn-router/simple-ca | 611 ++++++++++++++++++ .../src/usr/lib/vpn-router/vpnrouter.py | 503 ++++++++++++++ .../vpnrouter_platforms/__init__.py | 53 ++ .../vpn-router/vpnrouter_platforms/azure.py | 11 + .../lib/vpn-router/vpnrouter_platforms/gcp.py | 8 + .../vpn-router/vpnrouter_platforms/generic.py | 6 + .../templates/90-cloud-router.yaml.j2 | 7 - .../templates/cloud-router.default.j2 | 14 - .../templates/p2s-forwarder.conf.j2 | 2 - .../doc/vpn-router/vpn-router.conf.example | 69 ++ .../templates/p2s-forwarder.conf.j2 | 4 + .../templates/remote-site.conf.j2 | 2 +- .../templates/road-warrior.conf.j2 | 4 +- .../templates/wg0.conf.j2 | 0 examples/cloud-init.yaml.tpl | 88 +++ examples/install.sh | 73 +++ 40 files changed, 2367 insertions(+), 650 deletions(-) delete mode 100644 cloud-router-cloud-init.yaml.tpl create mode 100755 debian-package/debian/postrm create mode 100644 debian-package/debian/vpn-router.vpn-router-setup.service mode change 100644 => 100755 debian-package/publish.sh rename debian-package/src/etc/sysctl.d/{99-cloud-router.conf => 99-vpn-router.conf} (100%) delete mode 100755 debian-package/src/opt/cloud-router/bin/simple-ca delete mode 100755 debian-package/src/usr/lib/cloud-router/configure create mode 100755 debian-package/src/usr/lib/vpn-router/configure create mode 100755 debian-package/src/usr/lib/vpn-router/seed-config create mode 100755 debian-package/src/usr/lib/vpn-router/setup create mode 100755 debian-package/src/usr/lib/vpn-router/simple-ca create mode 100644 debian-package/src/usr/lib/vpn-router/vpnrouter.py create mode 100644 debian-package/src/usr/lib/vpn-router/vpnrouter_platforms/__init__.py create mode 100644 debian-package/src/usr/lib/vpn-router/vpnrouter_platforms/azure.py create mode 100644 debian-package/src/usr/lib/vpn-router/vpnrouter_platforms/gcp.py create mode 100644 debian-package/src/usr/lib/vpn-router/vpnrouter_platforms/generic.py delete mode 100644 debian-package/src/usr/share/cloud-router/templates/90-cloud-router.yaml.j2 delete mode 100644 debian-package/src/usr/share/cloud-router/templates/cloud-router.default.j2 delete mode 100644 debian-package/src/usr/share/cloud-router/templates/p2s-forwarder.conf.j2 create mode 100644 debian-package/src/usr/share/doc/vpn-router/vpn-router.conf.example create mode 100644 debian-package/src/usr/share/vpn-router/templates/p2s-forwarder.conf.j2 rename debian-package/src/usr/share/{cloud-router => vpn-router}/templates/remote-site.conf.j2 (95%) rename debian-package/src/usr/share/{cloud-router => vpn-router}/templates/road-warrior.conf.j2 (91%) rename debian-package/src/usr/share/{cloud-router => vpn-router}/templates/wg0.conf.j2 (100%) create mode 100644 examples/cloud-init.yaml.tpl create mode 100755 examples/install.sh diff --git a/.gitignore b/.gitignore index 730c2b9..66dcddb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ out .*.md AGENTS.md CLAUDE.md +__pycache__ +.vscode diff --git a/README.md b/README.md index f9f8d3a..494d321 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,221 @@ -# Linux Cloud Router for Ubuntu/Debian Linux +# vpn-router -This project provides an Ubuntu/Debian Linux-based cloud router solution that can be deployed on various cloud platforms. It offers routing and S2S and P2S VPN capabilities, making it an ideal choice for organizations looking to establish secure and efficient network connectivity in the cloud. +A Debian package that configures a Linux host or virtual machine as a VPN router: +- site-to-site IKEv2 IPSec with pre-shared key authentication (strongSwan swanctl) +- road-warrior (P2S) access using IKEv2 EAP-TLS, with a bundled certificate authority +- optional WireGuard endpoint +- routing and NAT for a protected subnet, TCP MSS clamping, and UFW firewall rules + +Nothing in the package is specific to a cloud provider. It is built on two inputs, the +name of the external NIC and the name of the internal one; the addresses on them are read +from the system rather than configured again. Nothing is auto-detected, because a router +that guesses which interface to NAT out of is a router that can guess wrong silently. +Platform-specific additions live in separate modules and are selected explicitly. + +## Installing + +The package can be installed by hand or as a step in automated provisioning. Both are +supported equally, and both end up with the same two things: a configuration file and a +service that applies it. + +``` +/etc/vpn-router/vpn-router.conf the configuration +systemctl restart vpn-router-setup apply it +``` + +### By hand + +```sh +apt-get install vpn-router +$EDITOR /etc/vpn-router/vpn-router.conf +systemctl restart vpn-router-setup +``` + +Accepting every default at install time is safe. Installing the package enables IP +forwarding and relaxes `rp_filter` through its own drop-in in `/etc/sysctl.d/`, which a +router needs, but it configures no tunnels, adds no firewall rules and does not enable the +firewall. Fill the file in when you are ready. + +### Automated + +Preseed the answers and the router comes up configured, with no follow-up command: + +```sh +debconf-set-selections <<'EOF' +vpn-router vpn-router/external_interface string eth0 +vpn-router vpn-router/internal_interface string eth1 +vpn-router vpn-router/local_fqdn string router.example.com +vpn-router vpn-router/local_cidrs string 10.0.0.0/24 +vpn-router vpn-router/int_gateway_ip string 10.1.1.1 +vpn-router vpn-router/remote_addrs string peer.example.net +vpn-router vpn-router/remote_id string peer.example.net +vpn-router vpn-router/remote_cidrs string 192.168.0.0/24 +vpn-router vpn-router/psk password s3cr3t +EOF +DEBIAN_FRONTEND=noninteractive apt-get install -y vpn-router +``` + +Alternatively write `/etc/vpn-router/vpn-router.conf` directly before or after installing. +A configuration-management tool should do that and then restart `vpn-router-setup`. + +See [examples/](examples/) for a cloud-init template and a shell installer. + +## Configuration + +`/etc/vpn-router/vpn-router.conf` is INI, mode 0600, and is created once on first install. +The package never rewrites it, so it is safe to edit, keep in version control or template. +A fully commented reference is installed at +`/usr/share/doc/vpn-router/vpn-router.conf.example`. + +Lists are comma-separated, booleans are `true`/`false`, an empty value means unset, and a +`_b64` suffix means the value is base64-encoded. + +| Section | Setting | Meaning | +|---|---|---| +| `general` | `platform` | Platform module: `generic`, `azure` or `gcp` | +| `interfaces` | `external` | Name of the NIC facing the untrusted network | +| `interfaces` | `internal` | Name of the NIC facing the protected network | +| `wan` | `local_fqdn` | This router's FQDN | +| `wan` | `local_id_mode` | IKE identity source: `fqdn`, `public_ip` or `internal_ip` | +| `local` | `cidrs` | Local subnets advertised into the tunnel | +| `local` | `int_gateway_ip` | Next hop on the internal side | +| `remote` | `addrs` | Remote gateway address(es) or FQDN | +| `remote` | `id` | Remote IKE identity, without a leading `@` | +| `remote` | `cidrs` | Remote subnets reachable through the tunnel | +| `remote` | `psk_b64` | Pre-shared key, base64-encoded | +| `remote` | `psk_file` | Path to a file holding the raw key; wins over `psk_b64` | +| `p2s` | `enabled` | Road-warrior access | +| `p2s` | `address_pool` | Pool assigned to road-warrior clients | +| `p2s` | `ca_name` | Common name for a locally created CA | +| `wireguard` | `enabled` | WireGuard endpoint | +| `wireguard` | `address` | Address and prefix for `wg0` | +| `wireguard` | `listen_port` | UDP port, default 51820 | + +WireGuard peers are added by hand in `/etc/wireguard/wg0.conf`. The package owns the +`[Interface]` section of that file and rewrites it when the settings above change, but it +carries every `[Peer]` section across untouched. + +Encode the pre-shared key with: + +```sh +printf %s 'the key' | base64 -w0 +``` + +Every feature is independent. Leaving a group empty simply means that feature is not +configured, and its generated files are removed. + +The two interface names are the base the rest is built on. The addresses strongSwan binds +on come from the external NIC, and the address used for `local_id_mode = internal_ip` +comes from the internal one, so neither is configured twice and neither can drift out of +step with the host. If a named interface does not exist the service stops and says so. + +## Applying changes + +```sh +systemctl restart vpn-router-setup +``` + +The service also runs at boot. It regenerates the configuration files, reads the current +addresses of the two interfaces, applies routes, generates WireGuard keys and injects +firewall rules. It then starts `strongswan` and `wg-quick@wg0` if the corresponding +feature is configured, reloading or restarting them only when their inputs changed, and +restarts `systemd-resolved` only when the road-warrior DNS drop-in changed. Turning a +feature off in the configuration stops its service and removes its files. Every step is +idempotent, so restarting with no changes does nothing. + +To regenerate the configuration files without touching the running system: + +```sh +/usr/lib/vpn-router/configure +``` + +## Certificates + +`/etc/vpn-router/pki` is the PKI directory, laid out the way `simple-ca` expects: + +``` +ca_cert.pem ca_key.pem ca_bundle.pem