diff --git a/environments/global/.terraform.lock.hcl b/environments/global/.terraform.lock.hcl new file mode 100644 index 0000000..39ba811 --- /dev/null +++ b/environments/global/.terraform.lock.hcl @@ -0,0 +1,50 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "artifactapi.k8s.syd1.au.unkin.net/terraform-unkin/binarylane" { + version = "0.14.0" + constraints = "0.14.0" + hashes = [ + "h1:k72XOG+O2dyqOXoFYzdaOYzIBELekwNF1XRlCjZaSa0=", + "zh:d84c05d191a0c53c96b8fbac62f8ccd2d41678f008406dd656d29d2c10766b45", + ] +} + +provider "registry.opentofu.org/hashicorp/vault" { + version = "5.6.0" + constraints = "5.6.0" + hashes = [ + "h1:afl/41BzHl93OACbk2EnGHF/xVgHtzmEREcdejASJcM=", + "zh:437c9f3920cc29d7a453aa5342270d55c618ca4979718c34641a49af1437d5a8", + "zh:5aeec1c3ed1710d5594277250ae4202c97bf9d8462a3c672c689e96bec3d7c8a", + "zh:71a61b7667e1016b5ed524b5842c65be1ab1661f258faf7a7cec9db4ea44abd8", + "zh:769a946cc4d99752dce8db0c4e509a201c0063f266dd387164b239854392d1b7", + "zh:82b0173dc9f65035b4ae6faaea497239455e5562d5d8c24a1f882e4451234dcf", + "zh:8f3373ef3dd1e026424c194d46842c0451ee79aacc67d1a1087d8d1ef8e55e95", + "zh:984d15ebd2fdaa55608efff7d98d59e414a1cb8c7b899b883fbbfbeb5849b70d", + "zh:9b4194ab7cf28d22098c51436dbfea878e413c418d6dbcfd3689aeb580993e21", + "zh:ef30a4958328b9fa3d58845ddebe8bd574a533170a28dc7b73121d52f565563b", + ] +} + +provider "registry.opentofu.org/ojford/wireguard" { + version = "0.4.1" + constraints = "0.4.1" + hashes = [ + "h1:c09xyC2LPfUHgVG/nis4l1B51rBZOCJwfQIcn5RJ3/k=", + "zh:1c3e89cf19118fc07d7b04257251fc9897e722c16e0a0df7b07fcd261f8c12e7", + "zh:1cdcd10ba61520d7e21590ea4d96d188cefbce3dcdbf89b277696d6023ec7620", + "zh:26bde3cf4850ba8605c028c84bed6075d2175853a0744303f6051cc3b5c51dd1", + "zh:28c351481689762dcd653229aa5f847473f71568a8222fafbddda0c600377a46", + "zh:3bd7ab222f13286449342d35d6dd3dbe343f575e47d4c2e6f8f64149386070c7", + "zh:3d25381c96df52253e7767aae49f0bead427c79f34ecdfb38026335e03c12de2", + "zh:4017a40b50886d3f1f0e886f8eacd1924c5f03888c90862c55ba98bb376198b2", + "zh:561978966998a3703053ac990796d5a88c7c14afd77e623846941a66b6fc9c92", + "zh:5f8460880a5f9cc9f4cc7c7c3c41156b0dae3c2b56f29cc259b3dc75ad650fe1", + "zh:724592993a0cccd45e0290aba2a6b72f7bb668a4ae3b8d778bb5d379d4105863", + "zh:b4de1157176eaf1fcc624d812ae6134893d6c73f3d1d2325c94cd9e707678d7b", + "zh:c5530bc71beedba579be57ea580f4151ec87223f198147771a924debff2e255e", + "zh:d3c7863e5baaf05e76fa5db3d3f58ff94440408873e370770b6a0080981cec37", + "zh:ec0a7d3a540cfb92e15b9a7acc43446059a9254b5904b48c940564c70e2b8aa1", + ] +} diff --git a/modules/binarylane/.terraform.lock.hcl b/modules/binarylane/.terraform.lock.hcl new file mode 100644 index 0000000..39ba811 --- /dev/null +++ b/modules/binarylane/.terraform.lock.hcl @@ -0,0 +1,50 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "artifactapi.k8s.syd1.au.unkin.net/terraform-unkin/binarylane" { + version = "0.14.0" + constraints = "0.14.0" + hashes = [ + "h1:k72XOG+O2dyqOXoFYzdaOYzIBELekwNF1XRlCjZaSa0=", + "zh:d84c05d191a0c53c96b8fbac62f8ccd2d41678f008406dd656d29d2c10766b45", + ] +} + +provider "registry.opentofu.org/hashicorp/vault" { + version = "5.6.0" + constraints = "5.6.0" + hashes = [ + "h1:afl/41BzHl93OACbk2EnGHF/xVgHtzmEREcdejASJcM=", + "zh:437c9f3920cc29d7a453aa5342270d55c618ca4979718c34641a49af1437d5a8", + "zh:5aeec1c3ed1710d5594277250ae4202c97bf9d8462a3c672c689e96bec3d7c8a", + "zh:71a61b7667e1016b5ed524b5842c65be1ab1661f258faf7a7cec9db4ea44abd8", + "zh:769a946cc4d99752dce8db0c4e509a201c0063f266dd387164b239854392d1b7", + "zh:82b0173dc9f65035b4ae6faaea497239455e5562d5d8c24a1f882e4451234dcf", + "zh:8f3373ef3dd1e026424c194d46842c0451ee79aacc67d1a1087d8d1ef8e55e95", + "zh:984d15ebd2fdaa55608efff7d98d59e414a1cb8c7b899b883fbbfbeb5849b70d", + "zh:9b4194ab7cf28d22098c51436dbfea878e413c418d6dbcfd3689aeb580993e21", + "zh:ef30a4958328b9fa3d58845ddebe8bd574a533170a28dc7b73121d52f565563b", + ] +} + +provider "registry.opentofu.org/ojford/wireguard" { + version = "0.4.1" + constraints = "0.4.1" + hashes = [ + "h1:c09xyC2LPfUHgVG/nis4l1B51rBZOCJwfQIcn5RJ3/k=", + "zh:1c3e89cf19118fc07d7b04257251fc9897e722c16e0a0df7b07fcd261f8c12e7", + "zh:1cdcd10ba61520d7e21590ea4d96d188cefbce3dcdbf89b277696d6023ec7620", + "zh:26bde3cf4850ba8605c028c84bed6075d2175853a0744303f6051cc3b5c51dd1", + "zh:28c351481689762dcd653229aa5f847473f71568a8222fafbddda0c600377a46", + "zh:3bd7ab222f13286449342d35d6dd3dbe343f575e47d4c2e6f8f64149386070c7", + "zh:3d25381c96df52253e7767aae49f0bead427c79f34ecdfb38026335e03c12de2", + "zh:4017a40b50886d3f1f0e886f8eacd1924c5f03888c90862c55ba98bb376198b2", + "zh:561978966998a3703053ac990796d5a88c7c14afd77e623846941a66b6fc9c92", + "zh:5f8460880a5f9cc9f4cc7c7c3c41156b0dae3c2b56f29cc259b3dc75ad650fe1", + "zh:724592993a0cccd45e0290aba2a6b72f7bb668a4ae3b8d778bb5d379d4105863", + "zh:b4de1157176eaf1fcc624d812ae6134893d6c73f3d1d2325c94cd9e707678d7b", + "zh:c5530bc71beedba579be57ea580f4151ec87223f198147771a924debff2e255e", + "zh:d3c7863e5baaf05e76fa5db3d3f58ff94440408873e370770b6a0080981cec37", + "zh:ec0a7d3a540cfb92e15b9a7acc43446059a9254b5904b48c940564c70e2b8aa1", + ] +} diff --git a/modules/binarylane/.tflint.hcl b/modules/binarylane/.tflint.hcl deleted file mode 100644 index 3a2ace1..0000000 --- a/modules/binarylane/.tflint.hcl +++ /dev/null @@ -1,3 +0,0 @@ -rule "terraform_unused_declarations" { - enabled = false -} diff --git a/modules/binarylane/cloud-init.yaml.tftpl b/modules/binarylane/cloud-init.yaml.tftpl new file mode 100644 index 0000000..78cbd88 --- /dev/null +++ b/modules/binarylane/cloud-init.yaml.tftpl @@ -0,0 +1,37 @@ +#cloud-config +hostname: ${hostname} +fqdn: ${fqdn} +prefer_fqdn_over_hostname: true + +write_files: + - path: /etc/wireguard/wg0.conf + permissions: "0600" + content: | + [Interface] + Address = ${wg_address}/24 + ListenPort = ${wg_port} + PrivateKey = ${wg_private_key} + + [Peer] + PublicKey = ${hub_public_key} + Endpoint = ${hub_endpoint} + AllowedIPs = ${allowed_ips} + PersistentKeepalive = 25 + - path: /etc/NetworkManager/conf.d/90-dns-none.conf + content: | + [main] + dns=none + +runcmd: + - dnf install -y wireguard-tools + - systemctl enable --now wg-quick@wg0 + - systemctl reload NetworkManager + - printf 'search main.unkin.net\nnameserver 198.18.200.7\n' > /etc/resolv.conf + - rpm -q puppet-agent || dnf install -y https://yum.puppet.com/puppet8-release-el-9.noarch.rpm + - dnf install -y puppet-agent + - /opt/puppetlabs/bin/puppet config set --section main certname ${fqdn} + - /opt/puppetlabs/bin/puppet config set --section main server puppet.k8s.syd1.au.unkin.net + - /opt/puppetlabs/bin/puppet config set --section main ca_server puppetca.k8s.syd1.au.unkin.net + - /opt/puppetlabs/bin/puppet config set --section main report_server puppet.k8s.syd1.au.unkin.net + - /opt/puppetlabs/bin/puppet config set --section main environment production + - systemctl enable --now puppet diff --git a/modules/binarylane/main.tf b/modules/binarylane/main.tf new file mode 100644 index 0000000..01562e3 --- /dev/null +++ b/modules/binarylane/main.tf @@ -0,0 +1,142 @@ +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 + + # only these destinations route over wg0; everything else (incl. SMTP) stays public + wg_allowed_ips = [ + "10.10.13.1/32", # prodnxsr0020 tunnel address + "10.10.12.200/32", # management ssh client (wireguard workstation) + "198.18.200.2/32", # puppetca.k8s.syd1.au.unkin.net + "198.18.200.3/32", # puppet.k8s.syd1.au.unkin.net + "198.18.200.4/32", # artifactapi.k8s.syd1.au.unkin.net (package repos) + "198.18.200.7/32", # bind-internal resolvers + "198.18.200.17/32", # rspamd milter :11332 + "198.18.25.78/32", # vault.service.consul + "198.18.26.197/32", # vault.service.consul + "198.18.27.116/32", # vault.service.consul + "198.18.28.248/32", # vault.service.consul + "198.18.29.92/32", # vault.service.consul + ] + + vpcs = { + syd = "10.10.14.0/24" + mel = "10.10.15.0/24" + } + + gateways = { + mta1 = { region = "syd", wg_address = "10.10.13.11" } + mta2 = { region = "mel", wg_address = "10.10.13.12" } + } +} + +resource "binarylane_vpc" "dmz" { + for_each = local.vpcs + + name = "dmz-${each.key}" + ip_range = each.value +} + +resource "wireguard_asymmetric_key" "hub" {} + +resource "wireguard_asymmetric_key" "gateway" { + for_each = local.gateways +} + +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" "gateway" { + for_each = local.gateways + + mount = "kv" + name = "service/wireguard/binarylane/${each.key}" + data_json = jsonencode({ + private_key = wireguard_asymmetric_key.gateway[each.key].private_key + public_key = wireguard_asymmetric_key.gateway[each.key].public_key + address = "${each.value.wg_address}/24" + listen_port = local.wg_port + }) +} + +resource "binarylane_server" "gateway" { + for_each = local.gateways + + name = "${each.key}.edge.unkin.net" + region = each.value.region + image = "alma-9" + size = "std-min" + public_ipv4_count = 1 + vpc_id = binarylane_vpc.dmz[each.value.region].id + port_blocking = false + backups = false + ipv6 = false + + user_data = templatefile("${path.module}/cloud-init.yaml.tftpl", { + hostname = each.key + fqdn = "${each.key}.edge.unkin.net" + wg_address = each.value.wg_address + wg_port = local.wg_port + wg_private_key = wireguard_asymmetric_key.gateway[each.key].private_key + hub_public_key = wireguard_asymmetric_key.hub.public_key + hub_endpoint = "${local.hub_endpoint}:${local.hub_port}" + allowed_ips = join(", ", local.wg_allowed_ips) + }) +} + +resource "binarylane_server_reverse_name" "gateway" { + for_each = local.gateways + + server_id = binarylane_server.gateway[each.key].id + ipv4_address = binarylane_server.gateway[each.key].public_ipv4_addresses[0] + reverse_name = "${each.key}.edge.unkin.net" +} + +# SSH has no public rule: management reaches sshd through wg0, which this firewall never sees. +resource "binarylane_server_firewall_rules" "gateway" { + for_each = local.gateways + + server_id = binarylane_server.gateway[each.key].id + firewall_rules = [ + { + description = "mail and https" + protocol = "tcp" + source_addresses = ["0.0.0.0/0"] + destination_addresses = [binarylane_server.gateway[each.key].public_ipv4_addresses[0]] + destination_ports = ["25", "465", "587", "993", "443"] + action = "accept" + }, + { + description = "wireguard from home gateway" + protocol = "udp" + source_addresses = ["${local.hub_endpoint}/32"] + destination_addresses = [binarylane_server.gateway[each.key].public_ipv4_addresses[0]] + destination_ports = [tostring(local.wg_port)] + action = "accept" + }, + { + description = "icmp" + protocol = "icmp" + source_addresses = ["0.0.0.0/0"] + destination_addresses = [binarylane_server.gateway[each.key].public_ipv4_addresses[0]] + action = "accept" + }, + { + description = "default deny" + protocol = "all" + source_addresses = ["0.0.0.0/0"] + destination_addresses = [binarylane_server.gateway[each.key].public_ipv4_addresses[0]] + action = "drop" + }, + ] +} diff --git a/modules/binarylane/outputs.tf b/modules/binarylane/outputs.tf new file mode 100644 index 0000000..d2ea70c --- /dev/null +++ b/modules/binarylane/outputs.tf @@ -0,0 +1,15 @@ +output "public_ipv4" { + description = "Public IPv4 per gateway." + value = { for k, v in binarylane_server.gateway : k => v.public_ipv4_addresses[0] } +} + +output "wireguard" { + description = "Tunnel address and WireGuard public key per gateway, plus the prodnxsr0020 end." + value = { + hub = { address = local.hub_address, public_key = wireguard_asymmetric_key.hub.public_key, listen_port = local.hub_port } + gateways = { for k, v in local.gateways : k => { + address = v.wg_address + public_key = wireguard_asymmetric_key.gateway[k].public_key + } } + } +} diff --git a/modules/binarylane/providers.tf b/modules/binarylane/providers.tf new file mode 100644 index 0000000..68e53cf --- /dev/null +++ b/modules/binarylane/providers.tf @@ -0,0 +1,8 @@ +provider "binarylane" { + api_token = var.binarylane_token +} + +# VAULT_ADDR / VAULT_TOKEN come from the Makefile; the k8s auth role token cannot mint child tokens. +provider "vault" { + skip_child_token = true +} diff --git a/modules/binarylane/versions.tf b/modules/binarylane/versions.tf index ff7164f..e323c20 100644 --- a/modules/binarylane/versions.tf +++ b/modules/binarylane/versions.tf @@ -1,3 +1,18 @@ terraform { required_version = ">= 1.10" + + required_providers { + binarylane = { + source = "artifactapi.k8s.syd1.au.unkin.net/terraform-unkin/binarylane" + version = "0.14.0" + } + vault = { + source = "hashicorp/vault" + version = "5.6.0" + } + wireguard = { + source = "OJFord/wireguard" + version = "0.4.1" + } + } }