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" # lab destinations reached through each region's vpn host; SMTP stays on the public internet lab_routes = [ "198.18.0.0/16", "10.10.12.200/32", # management ssh host ] vpcs = { syd = "10.10.14.0/24" mel = "10.10.15.0/24" } 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" } } edges = { ausyd2nxvm2130 = { region = "syd", vpc_ip = "10.10.14.10", vpn = "ausyd2nxvm2131", ptr = "mta1.edge.unkin.net" } aumel1nxvm2132 = { region = "mel", vpc_ip = "10.10.15.10", vpn = "aumel1nxvm2133", ptr = "mta2.edge.unkin.net" } } } resource "binarylane_vpc" "dmz" { for_each = local.vpcs name = "dmz-${each.key}" ip_range = each.value } resource "binarylane_vpc_route_entries" "dmz" { for_each = local.vpns vpc_id = binarylane_vpc.dmz[each.value.region].id route_entries = [for dst in local.lab_routes : { description = "lab via ${each.key}" destination = dst router = binarylane_server.vpn[each.key].private_ipv4_addresses[0] }] } resource "wireguard_asymmetric_key" "hub" {} resource "wireguard_asymmetric_key" "vpn" { for_each = local.vpns } resource "vault_kv_secret_v2" "hub" { mount = "kv" name = "service/wireguard/binarylane/prodnxsr0020" 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 }) } resource "vault_kv_secret_v2" "vpn" { for_each = local.vpns mount = "kv" name = "service/wireguard/binarylane/${each.key}" 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" listen_port = local.wg_port }) } resource "binarylane_server" "vpn" { for_each = local.vpns name = "${each.key}.${local.domain}" region = each.value.region image = "alma-9" size = "std-min" public_ipv4_count = 1 vpc_id = binarylane_vpc.dmz[each.value.region].id vpc_ipv4_address = each.value.vpc_ip source_and_destination_check = false backups = false ipv6 = false user_data = templatefile("${path.module}/cloud-init.yaml.tftpl", { hostname = each.key fqdn = "${each.key}.${local.domain}" vpn = { address = each.value.wg_address 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)) } routes = null }) # user_data/image changes reinstall the server in place, wiping its disk lifecycle { ignore_changes = [user_data, image] prevent_destroy = true } } resource "binarylane_server" "edge" { for_each = local.edges name = "${each.key}.${local.domain}" region = each.value.region image = "alma-9" size = "std-min" public_ipv4_count = 1 vpc_id = binarylane_vpc.dmz[each.value.region].id vpc_ipv4_address = each.value.vpc_ip port_blocking = false backups = false ipv6 = false user_data = templatefile("${path.module}/cloud-init.yaml.tftpl", { hostname = each.key fqdn = "${each.key}.${local.domain}" vpn = null routes = { vpc = local.vpcs[each.value.region] list = join(", ", [ for dst in local.lab_routes : "${dst} ${local.vpns[each.value.vpn].vpc_ip} src=${each.value.vpc_ip}" ]) } }) # user_data/image changes reinstall the server in place, wiping its disk lifecycle { ignore_changes = [user_data, image] prevent_destroy = true } } resource "binarylane_server_reverse_name" "edge" { for_each = local.edges server_id = binarylane_server.edge[each.key].id ipv4_address = binarylane_server.edge[each.key].public_ipv4_addresses[0] reverse_name = each.value.ptr } resource "binarylane_server_firewall_rules" "edge" { for_each = local.edges server_id = binarylane_server.edge[each.key].id firewall_rules = [ { description = "smtp" protocol = "tcp" source_addresses = ["0.0.0.0/0"] destination_addresses = [binarylane_server.edge[each.key].public_ipv4_addresses[0], each.value.vpc_ip] destination_ports = ["25"] action = "accept" }, { description = "ssh from the lab via ${each.value.vpn}" protocol = "tcp" source_addresses = ["${local.vpns[each.value.vpn].vpc_ip}/32", "10.10.12.200/32"] destination_addresses = [each.value.vpc_ip] destination_ports = ["22"] action = "accept" }, { description = "icmp" protocol = "icmp" source_addresses = ["0.0.0.0/0"] destination_addresses = [binarylane_server.edge[each.key].public_ipv4_addresses[0], each.value.vpc_ip] action = "accept" }, { description = "default deny" protocol = "all" source_addresses = ["0.0.0.0/0"] destination_addresses = [binarylane_server.edge[each.key].public_ipv4_addresses[0], each.value.vpc_ip] action = "drop" }, ] } # SSH has no rule: management reaches sshd through wg0, which this firewall never sees. resource "binarylane_server_firewall_rules" "vpn" { for_each = local.vpns server_id = binarylane_server.vpn[each.key].id firewall_rules = [ { description = "wireguard from home gateway" protocol = "udp" source_addresses = ["${local.hub_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" }, { description = "forward dmz to lab" protocol = "all" source_addresses = [local.vpcs[each.value.region]] destination_addresses = local.lab_routes action = "accept" }, { description = "icmp" protocol = "icmp" source_addresses = ["0.0.0.0/0"] destination_addresses = [binarylane_server.vpn[each.key].public_ipv4_addresses[0], each.value.vpc_ip] action = "accept" }, { description = "default deny" protocol = "all" source_addresses = ["0.0.0.0/0"] destination_addresses = [binarylane_server.vpn[each.key].public_ipv4_addresses[0], each.value.vpc_ip] action = "drop" }, ] }