Rewrite PIA VPN as multi-container bridge architecture
All checks were successful
Check Flake / check-flake (push) Successful in 3m15s
All checks were successful
Check Flake / check-flake (push) Successful in 3m15s
Replace the single VPN container (veth pair, host-side auth scripts) with a multi-container setup on a shared bridge network: - Dedicated VPN container handles all PIA auth, WireGuard config, NAT, and optional port forwarding DNAT - Service containers default-route through VPN container (leak-proof by topology) - Host runs tinyproxy on bridge for PIA API bootstrap before WG is up - WG interface is still created in host netns and moved into VPN container namespace - Monthly renewal to ensure that connection stays up (PIA allows connections to last up to 2 months) - Drop OpenVPN support entirely
This commit is contained in:
89
common/network/pia-vpn/README.md
Normal file
89
common/network/pia-vpn/README.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# PIA VPN Multi-Container Module
|
||||
|
||||
Routes service containers through a PIA WireGuard VPN using a shared bridge network.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
internet
|
||||
│
|
||||
┌──────┴──────┐
|
||||
│ Host │
|
||||
│ tinyproxy │ ← PIA API bootstrap proxy
|
||||
│ 10.100.0.1 │
|
||||
└──────┬───────┘
|
||||
│ br-vpn (no IPMasquerade)
|
||||
┌────────────┼──────────────┐
|
||||
│ │ │
|
||||
┌──────┴──────┐ ┌───┴────┐ ┌─────┴──────┐
|
||||
│ VPN ctr │ │ servarr│ │transmission│
|
||||
│ 10.100.0.2 │ │ .11 │ │ .10 │
|
||||
│ piaw (WG) │ │ │ │ │
|
||||
│ gateway+NAT │ └────────┘ └────────────┘
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
- **Host** creates the WG interface (encrypted UDP stays in host netns) and runs tinyproxy on the bridge so the VPN container can bootstrap PIA auth before WG is up.
|
||||
- **VPN container** authenticates with PIA via the proxy, configures WG, sets up NAT (masquerade bridge→WG) and optional port forwarding DNAT.
|
||||
- **Service containers** default-route through the VPN container. No WG interface = no internet if VPN is down = leak-proof by topology.
|
||||
- **Host** reaches containers directly on the bridge for nginx reverse proxying.
|
||||
|
||||
## Key design decisions
|
||||
|
||||
- **Bridge, not veth pairs**: All containers share one bridge (`br-vpn`), so the VPN container can act as a single gateway. The host does NOT masquerade bridge traffic — only the VPN container does (through WG).
|
||||
- **Port forwarding is implicit**: If any container sets `receiveForwardedPort`, the VPN container automatically handles PIA port forwarding and DNAT. No separate toggle needed.
|
||||
- **DNS through WG**: Service containers use the VPN container as their DNS server. The VPN container runs `systemd-resolved` listening on its bridge IP, forwarding queries through the WG tunnel.
|
||||
- **Monthly renewal**: `pia-vpn-setup` uses `Type=simple` + `Restart=always` + `RuntimeMaxSec=30d` to periodically re-authenticate with PIA and get a fresh port forwarding signature (signatures expire after ~2 months). Service containers are unaffected during renewal.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `default.nix` | Options, bridge, tinyproxy, host firewall, WG interface creation, assertions |
|
||||
| `vpn-container.nix` | VPN container: PIA auth, WG config, NAT, DNAT, port refresh timer |
|
||||
| `service-container.nix` | Generates service containers with static IP and gateway→VPN |
|
||||
| `scripts.nix` | Bash function library for PIA API calls and WG configuration |
|
||||
| `ca.rsa.4096.crt` | PIA CA certificate for API TLS verification |
|
||||
|
||||
## Usage
|
||||
|
||||
```nix
|
||||
pia-vpn = {
|
||||
enable = true;
|
||||
serverLocation = "swiss";
|
||||
|
||||
containers.my-service = {
|
||||
ip = "10.100.0.10";
|
||||
mounts."/data".hostPath = "/data";
|
||||
config = { services.my-app.enable = true; };
|
||||
|
||||
# Optional: receive PIA's forwarded port (at most one container)
|
||||
receiveForwardedPort = { port = 8080; protocol = "both"; };
|
||||
onPortForwarded = ''
|
||||
echo "PIA assigned port $PORT, forwarding to $TARGET_IP:8080"
|
||||
'';
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
```bash
|
||||
# Check VPN container status
|
||||
machinectl shell pia-vpn
|
||||
systemctl status pia-vpn-setup
|
||||
journalctl -u pia-vpn-setup
|
||||
|
||||
# Verify WG tunnel
|
||||
wg show
|
||||
|
||||
# Check NAT/DNAT rules
|
||||
iptables -t nat -L -v
|
||||
iptables -L FORWARD -v
|
||||
|
||||
# From a service container — verify VPN routing
|
||||
curl ifconfig.me
|
||||
|
||||
# Port refresh logs
|
||||
journalctl -u pia-vpn-port-refresh
|
||||
```
|
||||
Reference in New Issue
Block a user