From 04f6fd69d342bab6bfd8afb2a0b6d15c7ddf7cd8 Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Mon, 12 Oct 2026 22:39:17 +1100 Subject: [PATCH] Address vpn wg links and loopbacks from 198.18.2.160/27 for ospf --- README.md | 4 +++- modules/binarylane/cloud-init.yaml.tftpl | 21 +++++++++++++++++++++ modules/binarylane/main.tf | 12 +++++++++--- modules/binarylane/outputs.tf | 10 +++++++--- 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f84709c..ca65c74 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,9 @@ Each DMZ VPC (`syd`, `mel`) holds a postfix edge host and a vpn host. The vpn host runs wg0 as a /31 point-to-point link to its own prodnxsr0020 interface (`wg-syd` on 51821, `wg-mel` on 51822; `router_links` output) and forwards the lab routes for its VPC; the edge host reaches the lab through it and has no -WireGuard. Router keys live at `kv/service/wireguard/binarylane/prodnxsr0020-`. +WireGuard. Link /31s and vpn loopbacks (dum0) come from 198.18.2.160/27. Lab +traffic is sourced from the /31 until Puppet brings up FRR, which peers OSPF +over wg0 and advertises the loopback and VPC to the router. Router keys live at `kv/service/wireguard/binarylane/prodnxsr0020-`. wg0 is bootstrapped from a key in `user_data` (readable via the BinaryLane API); Puppet then manages it from Vault, so rotate by replacing `wireguard_asymmetric_key.vpn` here and letting Puppet roll it out. diff --git a/modules/binarylane/cloud-init.yaml.tftpl b/modules/binarylane/cloud-init.yaml.tftpl index e86da2b..3dbab72 100644 --- a/modules/binarylane/cloud-init.yaml.tftpl +++ b/modules/binarylane/cloud-init.yaml.tftpl @@ -12,12 +12,31 @@ write_files: Address = ${vpn.address} ListenPort = ${vpn.port} PrivateKey = ${vpn.private_key} + # source lab traffic from the /31: the router only learns the loopback and vpc once ospf is up + Table = off +%{ for r in vpn.routes ~} + PostUp = ip route replace ${r} dev wg0 src ${vpn.src} +%{ endfor ~} [Peer] PublicKey = ${vpn.router_key} Endpoint = ${vpn.router} AllowedIPs = ${vpn.allowed_ips} PersistentKeepalive = 25 + - path: /etc/NetworkManager/system-connections/dum0.nmconnection + permissions: "0600" + content: | + [connection] + id=dum0 + type=dummy + interface-name=dum0 + + [ipv4] + method=manual + address1=${vpn.loopback} + + [ipv6] + method=disabled - path: /etc/sysctl.d/90-ip-forward.conf content: | net.ipv4.ip_forward = 1 @@ -39,6 +58,8 @@ write_files: %{ if vpn != null ~} rpm -q wireguard-tools >/dev/null || dnf install -y wireguard-tools sysctl -p /etc/sysctl.d/90-ip-forward.conf + nmcli connection reload + nmcli connection up dum0 systemctl enable --now wg-quick@wg0 wg show wg0 latest-handshakes | awk '$2 > 0 { ok = 1 } END { exit !ok }' %{ endif ~} diff --git a/modules/binarylane/main.tf b/modules/binarylane/main.tf index 0f9ef76..86b598b 100644 --- a/modules/binarylane/main.tf +++ b/modules/binarylane/main.tf @@ -10,14 +10,17 @@ locals { "10.10.12.200/32", # management ssh host ] + # ospf hellos over the p2p links; wg has one peer per interface so this routes to it + ospf_allspf = "224.0.0.5/32" + vpcs = { syd = "10.10.14.0/24" mel = "10.10.15.0/24" } vpns = { - ausyd2nxvm2131 = { region = "syd", vpc_ip = "10.10.14.11", router_port = 51821, router_address = "10.10.13.0", wg_address = "10.10.13.1" } - aumel1nxvm2133 = { region = "mel", vpc_ip = "10.10.15.11", router_port = 51822, router_address = "10.10.13.2", wg_address = "10.10.13.3" } + ausyd2nxvm2131 = { region = "syd", vpc_ip = "10.10.14.11", router_port = 51821, router_address = "198.18.2.176", wg_address = "198.18.2.177", loopback = "198.18.2.161" } + aumel1nxvm2133 = { region = "mel", vpc_ip = "10.10.15.11", router_port = 51822, router_address = "198.18.2.178", wg_address = "198.18.2.179", loopback = "198.18.2.162" } } edges = { @@ -97,11 +100,14 @@ resource "binarylane_server" "vpn" { fqdn = "${each.key}.${local.domain}" vpn = { address = "${each.value.wg_address}/31" + loopback = "${each.value.loopback}/32" + routes = local.lab_routes + src = each.value.wg_address port = local.wg_port private_key = wireguard_asymmetric_key.vpn[each.key].private_key router_key = wireguard_asymmetric_key.router[each.key].public_key router = "${local.router_endpoint}:${each.value.router_port}" - allowed_ips = join(", ", concat(local.lab_routes, ["${each.value.router_address}/32"])) + allowed_ips = join(", ", concat(local.lab_routes, [local.ospf_allspf])) } routes = null }) diff --git a/modules/binarylane/outputs.tf b/modules/binarylane/outputs.tf index e04854e..0bf347d 100644 --- a/modules/binarylane/outputs.tf +++ b/modules/binarylane/outputs.tf @@ -1,5 +1,5 @@ output "hosts" { - description = "Hostname, public IPv4, VPC IPv4 and (vpn hosts) WireGuard public key per host." + description = "Hostname, public IPv4, VPC IPv4 and (vpn hosts) wg address, loopback and public key per host." value = merge( { for k, v in binarylane_server.edge : k => { hostname = v.name @@ -11,21 +11,25 @@ output "hosts" { public_ipv4 = v.public_ipv4_addresses[0] vpc_ipv4 = local.vpns[k].vpc_ip wg_address = local.vpns[k].wg_address + loopback = local.vpns[k].loopback wg_pubkey = wireguard_asymmetric_key.vpn[k].public_key } }, ) } output "router_links" { - description = "Per-region p2p wg link as prodnxsr0020 sees it; consumed by its Puppet config." + description = "Per-region p2p wg/ospf link as prodnxsr0020 sees it; vpc_cidr arrives via ospf. Consumed by its Puppet config." value = { for k, v in local.vpns : v.region => { interface = "wg-${v.region}" listen_port = v.router_port router_address = "${v.router_address}/31" - peer_address = v.wg_address + peer_address = "${v.wg_address}/31" + peer_loopback = "${v.loopback}/32" + allowed_ips = ["${v.wg_address}/32", "${v.loopback}/32", local.vpcs[v.region], local.ospf_allspf] peer_public_key = wireguard_asymmetric_key.vpn[k].public_key peer_endpoint = "${binarylane_server.vpn[k].public_ipv4_addresses[0]}:${local.wg_port}" vpc_cidr = local.vpcs[v.region] private_key_vault_path = "kv/${vault_kv_secret_v2.router[k].name}" + peer_key_vault_path = "kv/${vault_kv_secret_v2.vpn[k].name}" } } }