Give each vpn host its own p2p wg link to prodnxsr0020
ci/woodpecker/pr/plan Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful

This commit is contained in:
2026-10-12 22:32:03 +11:00
parent cc72e6c8d2
commit 29f3a53143
4 changed files with 46 additions and 33 deletions
+7 -5
View File
@@ -24,8 +24,10 @@ role in terraform-vault.
## Mail edge
Each DMZ VPC (`syd`, `mel`) holds a postfix edge host and a vpn host. The vpn
host runs wg0 to prodnxsr0020 and forwards the lab routes for its VPC; the edge
host reaches the lab through it and has no WireGuard. 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.
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-<region>`.
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.
+3 -3
View File
@@ -9,13 +9,13 @@ write_files:
permissions: "0600"
content: |
[Interface]
Address = ${vpn.address}/24
Address = ${vpn.address}
ListenPort = ${vpn.port}
PrivateKey = ${vpn.private_key}
[Peer]
PublicKey = ${vpn.hub_key}
Endpoint = ${vpn.hub}
PublicKey = ${vpn.router_key}
Endpoint = ${vpn.router}
AllowedIPs = ${vpn.allowed_ips}
PersistentKeepalive = 25
- path: /etc/sysctl.d/90-ip-forward.conf
+24 -22
View File
@@ -1,10 +1,8 @@
locals {
# home gateway (prodnxsr0020) end of the mail DMZ tunnel
hub_endpoint = "103.216.191.185"
hub_port = 51821
hub_address = "10.10.13.1"
wg_port = 51820
domain = "main.unkin.net"
# home WAN address of prodnxsr0020, which terminates one wg link per vpn host
router_endpoint = "103.216.191.185"
wg_port = 51820
domain = "main.unkin.net"
# lab destinations reached through each region's vpn host; SMTP stays on the public internet
lab_routes = [
@@ -18,8 +16,8 @@ locals {
}
vpns = {
ausyd2nxvm2131 = { region = "syd", vpc_ip = "10.10.14.11", wg_address = "10.10.13.11" }
aumel1nxvm2133 = { region = "mel", vpc_ip = "10.10.15.11", wg_address = "10.10.13.12" }
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" }
}
edges = {
@@ -46,20 +44,24 @@ resource "binarylane_vpc_route_entries" "dmz" {
}]
}
resource "wireguard_asymmetric_key" "hub" {}
resource "wireguard_asymmetric_key" "router" {
for_each = local.vpns
}
resource "wireguard_asymmetric_key" "vpn" {
for_each = local.vpns
}
resource "vault_kv_secret_v2" "hub" {
resource "vault_kv_secret_v2" "router" {
for_each = local.vpns
mount = "kv"
name = "service/wireguard/binarylane/prodnxsr0020"
name = "service/wireguard/binarylane/prodnxsr0020-${each.value.region}"
data_json = jsonencode({
private_key = wireguard_asymmetric_key.hub.private_key
public_key = wireguard_asymmetric_key.hub.public_key
address = "${local.hub_address}/24"
listen_port = local.hub_port
private_key = wireguard_asymmetric_key.router[each.key].private_key
public_key = wireguard_asymmetric_key.router[each.key].public_key
address = "${each.value.router_address}/31"
listen_port = each.value.router_port
})
}
@@ -71,7 +73,7 @@ resource "vault_kv_secret_v2" "vpn" {
data_json = jsonencode({
private_key = wireguard_asymmetric_key.vpn[each.key].private_key
public_key = wireguard_asymmetric_key.vpn[each.key].public_key
address = "${each.value.wg_address}/24"
address = "${each.value.wg_address}/31"
listen_port = local.wg_port
})
}
@@ -94,12 +96,12 @@ resource "binarylane_server" "vpn" {
hostname = each.key
fqdn = "${each.key}.${local.domain}"
vpn = {
address = each.value.wg_address
address = "${each.value.wg_address}/31"
port = local.wg_port
private_key = wireguard_asymmetric_key.vpn[each.key].private_key
hub_key = wireguard_asymmetric_key.hub.public_key
hub = "${local.hub_endpoint}:${local.hub_port}"
allowed_ips = join(", ", concat(["${local.hub_address}/32"], local.lab_routes))
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"]))
}
routes = null
})
@@ -197,9 +199,9 @@ resource "binarylane_server_firewall_rules" "vpn" {
server_id = binarylane_server.vpn[each.key].id
firewall_rules = [
{
description = "wireguard from home gateway"
description = "wireguard from prodnxsr0020"
protocol = "udp"
source_addresses = ["${local.hub_endpoint}/32"]
source_addresses = ["${local.router_endpoint}/32"]
destination_addresses = [binarylane_server.vpn[each.key].public_ipv4_addresses[0], each.value.vpc_ip]
destination_ports = [tostring(local.wg_port)]
action = "accept"
+12 -3
View File
@@ -16,7 +16,16 @@ output "hosts" {
)
}
output "hub" {
description = "prodnxsr0020 end of the tunnels."
value = { address = local.hub_address, public_key = wireguard_asymmetric_key.hub.public_key, listen_port = local.hub_port }
output "router_links" {
description = "Per-region p2p wg link as prodnxsr0020 sees it; 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_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}"
} }
}