Add cloud-router configuration templates and scripts
- Introduced debian templates for cloud-router configuration parameters. - Added simple-ca.sh script for managing a minimal Certificate Authority (CA) for IKEv2 PKI. - Created sysctl configuration to enable IP forwarding and adjust rp_filter settings. - Implemented configure script to render configuration files using Jinja2 templates. - Added simple-ca script for generating CA and certificates. - Created Jinja2 templates for various configuration files including netplan, strongSwan, and WireGuard. - Implemented UFW rules setup for IPsec and WireGuard. - Added support for road-warrior and site-to-site VPN configurations.
This commit is contained in:
@@ -0,0 +1,369 @@
|
||||
# Plan: Debian Package Source for linux-cloud-router
|
||||
|
||||
## Context
|
||||
|
||||
The `azure-router` Terraform module provisions a Linux router VM using Cloud Init. Cloud Init
|
||||
installs packages, writes config files (strongSwan swanctl, WireGuard, netplan, sysctl, UFW
|
||||
rules), and runs a one-shot setup service. All site-specific values (IPs, FQDNs, CIDRs, PSK)
|
||||
are injected at provision time via Terraform's `templatefile()`.
|
||||
|
||||
The Debian package replicates the same outcome on Ubuntu 22.04, 24.04, and 26.04 hosts
|
||||
(exact supported version list TBD — compatibility and feature availability must be verified
|
||||
across all three; see Further Considerations). Site-specific values
|
||||
are collected via **debconf** interactive prompts at `dpkg install` time. A `postinst` script
|
||||
reads the debconf answers and generates all derived config files.
|
||||
|
||||
---
|
||||
|
||||
## Decisions
|
||||
|
||||
- Site config: **debconf** interactive prompts at install time
|
||||
- P2S certificates: managed with `simple-ca.sh` directly
|
||||
- Networking: Ubuntu only — netplan (not `/etc/network/interfaces.d/`)
|
||||
- Build approach: full Debian source package (`dpkg-buildpackage` + debhelper ≥13)
|
||||
- PKI library: `simple-ca.sh` (OpenSSL-based); currently RSA-4096, ECDSA/Ed25519 support
|
||||
is a planned future enhancement to the script itself — not a concern for this package
|
||||
- `strongswan-pki` is **not** a dependency; `openssl` covers all PKI needs (including PKCS#12
|
||||
which `pki` tool cannot produce)
|
||||
|
||||
---
|
||||
|
||||
## Source Tree Layout
|
||||
|
||||
```
|
||||
linux-cloud-router/
|
||||
├── debian/
|
||||
│ ├── control
|
||||
│ ├── changelog
|
||||
│ ├── rules
|
||||
│ ├── copyright
|
||||
│ ├── install
|
||||
│ ├── dirs
|
||||
│ ├── templates ← debconf question definitions
|
||||
│ ├── config ← pre-install debconf prompts
|
||||
│ ├── postinst ← generates all config files, enables services
|
||||
│ └── prerm ← stops/disables services
|
||||
└── src/
|
||||
├── etc/
|
||||
│ ├── sysctl.d/
|
||||
│ │ └── 99-cloud-router.conf
|
||||
│ ├── xdg/nvim/
|
||||
│ │ └── init.vim
|
||||
│ └── systemd/system/
|
||||
│ └── cloud-router-setup.service
|
||||
└── usr/local/
|
||||
├── lib/
|
||||
│ └── simple-ca.sh
|
||||
└── sbin/
|
||||
└── cloud-router-setup adapted from azure-router-setup
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 0 — Package Availability Research
|
||||
|
||||
Use Apple's `container` CLI to launch one-shot Ubuntu containers for each target release:
|
||||
|
||||
```sh
|
||||
PKGS="strongswan-swanctl charon-systemd libstrongswan-extra-plugins \
|
||||
libcharon-extra-plugins wireguard-tools ufw openssl debconf \
|
||||
debhelper netplan.io"
|
||||
|
||||
for RELEASE in 22.04 24.04 26.04; do
|
||||
echo "=== ubuntu:$RELEASE ==="
|
||||
container run --rm ubuntu:$RELEASE \
|
||||
sh -c "apt-get update -qq && apt-cache policy $PKGS"
|
||||
done
|
||||
```
|
||||
|
||||
`container` is also the primary build environment: mount the source tree and run
|
||||
`dpkg-buildpackage` inside the target Ubuntu container, keeping the macOS host clean.
|
||||
|
||||
Record the candidate version for each package per release. Use the results to:
|
||||
- Confirm all packages exist in the default repos (no PPAs needed)
|
||||
- Verify `debhelper` version supports compat 14 (requires ≥13.10); fall back to compat 13
|
||||
if 22.04 support is required and its debhelper is too old
|
||||
- Confirm strongSwan package names are consistent across releases
|
||||
- Finalise the supported Ubuntu version list
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 — Package Scaffold
|
||||
|
||||
1. `debian/control` — package name `cloud-router`, short description, `Build-Depends` and `Depends` (see below)
|
||||
2. `debian/changelog` — initial `1.0.0-1` entry
|
||||
3. `debian/rules` — minimal `dh $@` (debhelper)
|
||||
4. `debian/copyright` — MIT (matches simple-ca.sh)
|
||||
|
||||
**`debian/control` Build-Depends:**
|
||||
```
|
||||
debhelper-compat (= 14)
|
||||
```
|
||||
|
||||
**`debian/control` Depends:**
|
||||
```
|
||||
strongswan-swanctl, charon-systemd, libstrongswan-extra-plugins, libcharon-extra-plugins,
|
||||
wireguard-tools, ufw, debconf, openssl
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — Static Source Files
|
||||
|
||||
6. `src/etc/sysctl.d/99-cloud-router.conf`
|
||||
```
|
||||
net.ipv4.ip_forward = 1
|
||||
net.ipv4.conf.all.rp_filter = 0
|
||||
net.ipv4.conf.default.rp_filter = 0
|
||||
```
|
||||
(identical content to cloud-init)
|
||||
|
||||
7. `src/etc/xdg/nvim/init.vim` — exact neovim settings from `router-cloud-init.tpl`
|
||||
|
||||
8. `src/etc/systemd/system/cloud-router-setup.service`
|
||||
Renamed from `azure-router-setup.service`. Same content:
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Linux Cloud Router Setup
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/sbin/cloud-router-setup
|
||||
RemainAfterExit=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — `cloud-router-setup` Script
|
||||
|
||||
9. Adapt `azure-router-setup` bash script from `router-cloud-init.tpl`:
|
||||
- Add `source /etc/default/cloud-router` at the top
|
||||
- Replace all Terraform-interpolated values (`${local_subnet}`, `${remote_cidrs}`,
|
||||
`${wg_listen_port}`, `${p2s_address_pool}`, `${local_addrs}`, `${fqdn}`) with the
|
||||
corresponding variable names sourced from `/etc/default/cloud-router`
|
||||
- Rename script to `cloud-router-setup`; install to `src/usr/local/sbin/cloud-router-setup`
|
||||
- The script remains idempotent (checks for existing UFW rule markers before inserting)
|
||||
- WireGuard key generation and WireGuard UFW rules are guarded by `if [ "$WG_ENABLED" = "true" ]`
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 — PKI Library
|
||||
|
||||
10. `src/usr/local/lib/simple-ca.sh` — Bash function module providing
|
||||
`make_ca`, `make_cert`, `make_pfx`. Source it in any shell session with
|
||||
`source /usr/local/lib/simple-ca.sh`; set `SIMPLE_CA_DIR=/etc/cloud-router/pki`
|
||||
before use.
|
||||
|
||||
11. `cloud-router-sync-pki` — standalone script that copies PKI output to swanctl's
|
||||
expected locations:
|
||||
- `/etc/cloud-router/pki/ca_bundle.pem` → `/etc/swanctl/x509ca/ca.pem`
|
||||
- `/etc/cloud-router/pki/{server}_cert.pem` → `/etc/swanctl/x509/server.pem`
|
||||
- `/etc/cloud-router/pki/{server}_key.pem` → `/etc/swanctl/private/server.key`
|
||||
|
||||
The server cert name is read from `P2S_SERVER_NAME` in `/etc/default/cloud-router`
|
||||
(defaults to the hostname component of `LOCAL_FQDN`).
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 — Debconf Templates and Config Script
|
||||
|
||||
13. `debian/templates` — one `Template:` block per configurable value:
|
||||
|
||||
| Question | Type | Default |
|
||||
|---|---|---|
|
||||
| `cloud-router/local_addrs` | string | |
|
||||
| `cloud-router/local_fqdn` | string | |
|
||||
| `cloud-router/local_id_mode` | select | `fqdn` (choices: fqdn, public_ip, internal_ip) |
|
||||
| `cloud-router/local_cidrs` | string | |
|
||||
| `cloud-router/remote_addrs` | string | |
|
||||
| `cloud-router/remote_id` | string | |
|
||||
| `cloud-router/psk` | password | |
|
||||
| `cloud-router/remote_cidrs` | string | |
|
||||
| `cloud-router/router_int_gateway_ip` | string | |
|
||||
| `cloud-router/p2s_address_pool` | string | |
|
||||
| `cloud-router/wg_enabled` | boolean | `false` |
|
||||
| `cloud-router/wg_address` | string | `10.0.1.1/24` *(shown only when wg_enabled=true)* |
|
||||
| `cloud-router/wg_listen_port` | string | `51820` *(shown only when wg_enabled=true)* |
|
||||
|
||||
14. `debian/config` — pre-install shell script:
|
||||
- Sources `/usr/share/debconf/confmodule`
|
||||
- Calls `db_input` for each question in priority order
|
||||
- Shows `wg_address` and `wg_listen_port` conditionally: `db_get cloud-router/wg_enabled; if [ "$RET" = "true" ]`
|
||||
|
||||
---
|
||||
|
||||
## Phase 6 — Maintainer Scripts
|
||||
|
||||
15. `debian/postinst` (triggered on `configure`):
|
||||
1. `db_get` all debconf answers
|
||||
2. Derive `LOCAL_SUBNET` = first element of `LOCAL_CIDRS`
|
||||
3. Derive `P2S_SERVER_NAME` = hostname part of `LOCAL_FQDN`
|
||||
4. Write `/etc/default/cloud-router` (mode 0644, root:root) — non-secret config:
|
||||
```sh
|
||||
LOCAL_ADDRS="..."
|
||||
LOCAL_FQDN="..."
|
||||
LOCAL_ID_MODE="..."
|
||||
LOCAL_CIDRS="..."
|
||||
LOCAL_SUBNET="..."
|
||||
REMOTE_ADDRS="..."
|
||||
REMOTE_ID="..."
|
||||
REMOTE_CIDRS="..."
|
||||
ROUTER_INT_GATEWAY_IP="..."
|
||||
P2S_ADDRESS_POOL="..."
|
||||
P2S_SERVER_NAME="..."
|
||||
WG_ENABLED="..."
|
||||
WG_ADDRESS="..." # only relevant when WG_ENABLED=true
|
||||
WG_LISTEN_PORT="..." # only relevant when WG_ENABLED=true
|
||||
```
|
||||
5. Generate `/etc/swanctl/conf.d/remote-site.conf` (mode 0600, root:root) via bash heredoc —
|
||||
includes both the `connections {}` block and a `secrets { ike-remote-site { secret = "$PSK" } }`
|
||||
block; the file must be 0600 because it contains the PSK
|
||||
6. Generate `/etc/swanctl/conf.d/road-warrior.conf` via heredoc
|
||||
7. Generate `/etc/systemd/resolved.conf.d/p2s-forwarder.conf` via heredoc
|
||||
8. If `WG_ENABLED=true`: generate `/etc/wireguard/wg0.conf` via heredoc
|
||||
9. Generate `/etc/netplan/90-cloud-router.yaml` via heredoc
|
||||
10. `sysctl --system`
|
||||
11. `netplan apply`
|
||||
12. `systemctl daemon-reload`
|
||||
13. `systemctl enable --now cloud-router-setup.service`
|
||||
14. `ufw allow 22/tcp && ufw --force enable && ufw reload`
|
||||
15. `systemctl enable --now strongswan`
|
||||
16. `systemctl restart systemd-resolved`
|
||||
|
||||
16. `debian/prerm` (on `remove`):
|
||||
- `systemctl disable --now cloud-router-setup.service`
|
||||
- `systemctl disable --now strongswan`
|
||||
|
||||
---
|
||||
|
||||
## Phase 7 — Install Manifest
|
||||
|
||||
17. `debian/install` — maps `src/` tree entries to absolute filesystem paths, e.g.:
|
||||
```
|
||||
src/etc/sysctl.d/99-cloud-router.conf etc/sysctl.d/
|
||||
src/etc/xdg/nvim/init.vim etc/xdg/nvim/
|
||||
src/etc/systemd/system/cloud-router-setup.service etc/systemd/system/
|
||||
src/usr/local/sbin/cloud-router-setup usr/local/sbin/
|
||||
src/usr/local/lib/simple-ca.sh usr/local/lib/
|
||||
```
|
||||
|
||||
18. `debian/dirs` — directories that must pre-exist:
|
||||
```
|
||||
etc/cloud-router
|
||||
etc/cloud-router/pki
|
||||
etc/wireguard
|
||||
etc/swanctl/conf.d
|
||||
etc/swanctl/x509ca
|
||||
etc/swanctl/x509
|
||||
etc/swanctl/private
|
||||
etc/systemd/resolved.conf.d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 8 — Cloud-Init YAML
|
||||
|
||||
The `.deb` is delivered via a private apt repository (URL/GPG key TBD). The cloud-init
|
||||
user-data pre-seeds all debconf answers before the package is installed, so `postinst` runs
|
||||
fully unattended on first boot.
|
||||
|
||||
Template file `cloud-router-cloud-init.yaml.tpl` (rendered by `templatefile()` at provision
|
||||
time, Terraform variable names shown as `${…}`):
|
||||
|
||||
```yaml
|
||||
#cloud-config
|
||||
|
||||
apt:
|
||||
sources:
|
||||
cloud-router:
|
||||
source: "deb [signed-by=/etc/apt/keyrings/cloud-router.gpg] ${repo_url} ${ubuntu_codename} main"
|
||||
key: |
|
||||
${repo_gpg_key}
|
||||
|
||||
debconf_selections: |
|
||||
cloud-router cloud-router/local_addrs string ${local_addrs}
|
||||
cloud-router cloud-router/local_fqdn string ${fqdn}
|
||||
cloud-router cloud-router/local_id_mode select ${local_id_mode}
|
||||
cloud-router cloud-router/local_cidrs string ${local_cidrs}
|
||||
cloud-router cloud-router/remote_addrs string ${remote_addrs}
|
||||
cloud-router cloud-router/remote_id string ${remote_id}
|
||||
cloud-router cloud-router/psk password ${psk}
|
||||
cloud-router cloud-router/remote_cidrs string ${remote_cidrs}
|
||||
cloud-router cloud-router/router_int_gateway_ip string ${router_int_gateway_ip}
|
||||
cloud-router cloud-router/p2s_address_pool string ${p2s_address_pool}
|
||||
cloud-router cloud-router/wg_enabled boolean ${wg_enabled}
|
||||
cloud-router cloud-router/wg_address string ${wg_address}
|
||||
cloud-router cloud-router/wg_listen_port string ${wg_listen_port}
|
||||
|
||||
package_update: true
|
||||
|
||||
packages:
|
||||
- cloud-router
|
||||
```
|
||||
|
||||
Notes:
|
||||
- `debconf_selections` runs before `packages` in cloud-init's module order, so answers are
|
||||
in place before `postinst` executes
|
||||
- `DEBIAN_FRONTEND` does not need to be set explicitly; debconf detects the pre-seeded
|
||||
answers and skips interactive prompts automatically
|
||||
- `${ubuntu_codename}` (e.g. `noble`) must be passed as a Terraform variable or derived
|
||||
from the VM image; `${repo_gpg_key}` is the ASCII-armoured public key of the signing key
|
||||
- The template replaces the existing `router-cloud-init.tpl` in the `azure-router` module
|
||||
|
||||
---
|
||||
|
||||
## Verification
|
||||
|
||||
1. `dpkg-buildpackage -us -uc -b` from `linux-cloud-router/` completes without error; `.deb` produced
|
||||
2. `dpkg -i cloud-router_1.0.0-1_all.deb` on a fresh VM for each target Ubuntu release
|
||||
(22.04, 24.04, 26.04): debconf prompts appear; all deps satisfied
|
||||
3. `cat /etc/default/cloud-router` shows non-secret config; `/etc/swanctl/conf.d/remote-site.conf`
|
||||
and `road-warrior.conf` are present; `remote-site.conf` is mode 0600 and contains the
|
||||
`secrets {}` block with the PSK; `/etc/wireguard/wg0.conf` exists only when `WG_ENABLED=true`
|
||||
4. Source `simple-ca.sh`, then `make_ca "My Router CA"` → `/etc/cloud-router/pki/ca_cert.pem`
|
||||
created; `make_cert --type server router.example.com` → cert/key produced;
|
||||
`cloud-router-sync-pki` → files appear in `/etc/swanctl/`
|
||||
5. `make_pfx <cert-path>` → `.pfx` produced for a road-warrior client
|
||||
6. `systemctl status cloud-router-setup.service` is `active (exited)`; UFW `before.rules`
|
||||
contains the IPsec/NAT/forwarding sections; when `WG_ENABLED=true`, WireGuard keys are
|
||||
present at `/etc/wireguard/wg0.key` and `wg0.pub`
|
||||
|
||||
---
|
||||
|
||||
## Further Considerations
|
||||
|
||||
- **Ubuntu version compatibility** (must verify before finalising supported version list):
|
||||
items to check on each of 22.04, 24.04, 26.04:
|
||||
- `debhelper-compat (= 14)` — Ubuntu 22.04 ships debhelper 13.6 (max compat 13);
|
||||
24.04 ships 13.11 (compat 14 supported); may need to drop to compat 13 for 22.04 support
|
||||
- strongSwan package names (`strongswan-swanctl`, `charon-systemd`, plugin packages) —
|
||||
verify availability and correct names on each release
|
||||
- `wireguard-tools` availability and kernel module inclusion
|
||||
- netplan API and YAML schema changes between releases
|
||||
- `systemd-resolved` DNS stub behaviour differences
|
||||
|
||||
- **`dpkg-reconfigure` behaviour**: re-running `dpkg-reconfigure cloud-router` re-prompts
|
||||
all debconf questions and regenerates config files, overwriting any manual edits. Files
|
||||
"owned" by debconf (remote-site.conf, wg0.conf, netplan yaml, env) should be documented
|
||||
as managed; swanctl cert files (written via `simple-ca.sh`) are not touched by postinst.
|
||||
|
||||
- **`simple-ca.sh` key algorithm support**: currently RSA-4096 only. ECDSA/Ed25519 support
|
||||
is a planned future enhancement to `simple-ca.sh` itself and is independent of this package.
|
||||
|
||||
- **CRL / revocation**: `simple-ca.sh` has no revocation support. If road-warrior client
|
||||
revocation is needed, `strongswan-pki` can be added as an optional dependency for
|
||||
`pki --signcrl`. Treat as a future enhancement.
|
||||
|
||||
- **Build tooling**: `container` (Apple container CLI) is used to run `dpkg-buildpackage`
|
||||
inside Ubuntu containers. The source tree is bind-mounted into the container; the built
|
||||
`.deb` appears in the parent directory as usual. Inside the container: `apt-get install -y
|
||||
build-essential debhelper`. No Ubuntu host or VM needed for builds.
|
||||
|
||||
- **Development environment**: source files are authored on macOS. `container` handles all
|
||||
build and install testing — run the same loop against 22.04, 24.04, and 26.04 containers
|
||||
once the compatibility matrix is resolved.
|
||||
Reference in New Issue
Block a user