feat(edge): Terraform for the China-facing relay and Cloudflare DNS
Adds terraform/edge, a root module with two providers: - Cloudflare 5.28.1: CNAMEs to the existing tunnel, plus an opt-in resource that can take the tunnel ingress list over when manage_tunnel_config is true. It is off by default because that resource REPLACES the whole list, and the tunnel is remotely managed from the dashboard today. - Tencent Cloud 1.83.38: VPC, subnet, security group with three rules, key pair, one CVM relay with cloud-init (WireGuard listener, forwarding sysctls, Caddy), and DNSPod A records for the .cn hostnames. The design keeps the homelab inbound-free: the relay terminates TLS in China and forwards over a WireGuard tunnel the cluster dials outbound. Cloudflare keeps serving every existing hostname untouched. Verified: terraform init succeeds for both providers, fmt clean, validate reports 0 errors, and plan resolves to 10 creates with placeholder variables. Provider-side name validation caught a real bug during that run: key pair and security group names allow only letters, digits and underscore, so the derived names use underscores. Not applied. Credentials come from CLOUDFLARE_API_TOKEN, TENCENTCLOUD_SECRET_ID and TENCENTCLOUD_SECRET_KEY; the WireGuard private key is TF_VAR only. State is local and git-ignored like terraform/zitadel. Blocked on facts this module cannot invent: the real .cn domain, whether it has an ICP filing, the CVM image ID, the operator CIDR, and the homelab WireGuard public key.
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
# The relay: one small CVM in Tencent Cloud that terminates TLS for the .cn
|
||||
# domain and forwards over WireGuard to the homelab ingress.
|
||||
#
|
||||
# Why a relay and not Cloudflare: mainland clients reach a Tencent edge far
|
||||
# faster than a Cloudflare edge, and Cloudflare's China network is an
|
||||
# Enterprise product. The homelab keeps zero inbound exposure because the
|
||||
# tunnel is dialled outbound from the cluster.
|
||||
#
|
||||
# ICP FILING: a mainland resource serving HTTP/HTTPS for a domain requires an
|
||||
# ICP filing naming that domain. Without it this instance accepts the
|
||||
# connection and the mainland network resets the HTTP layer. Put the relay in a
|
||||
# non-mainland region (ap-hongkong, ap-singapore) when no filing exists.
|
||||
|
||||
resource "tencentcloud_vpc" "edge" {
|
||||
name = "${replace(var.china_domain, ".", "_")}_edge"
|
||||
cidr_block = "10.66.0.0/16"
|
||||
}
|
||||
|
||||
resource "tencentcloud_subnet" "edge" {
|
||||
name = "${replace(var.china_domain, ".", "_")}_edge"
|
||||
vpc_id = tencentcloud_vpc.edge.id
|
||||
availability_zone = var.tencent_availability_zone
|
||||
cidr_block = "10.66.1.0/24"
|
||||
}
|
||||
|
||||
resource "tencentcloud_key_pair" "edge" {
|
||||
key_name = "${replace(var.china_domain, ".", "_")}_edge"
|
||||
public_key = var.relay_ssh_public_key
|
||||
}
|
||||
|
||||
resource "tencentcloud_security_group" "edge" {
|
||||
name = "${replace(var.china_domain, ".", "_")}_edge"
|
||||
description = "TLS + WireGuard for the ${var.china_domain} relay; SSH from the operator only"
|
||||
}
|
||||
|
||||
resource "tencentcloud_security_group_rule" "ssh" {
|
||||
security_group_id = tencentcloud_security_group.edge.id
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
port_range = "22"
|
||||
cidr_ip = var.relay_admin_cidr
|
||||
policy = "accept"
|
||||
}
|
||||
|
||||
resource "tencentcloud_security_group_rule" "tls" {
|
||||
security_group_id = tencentcloud_security_group.edge.id
|
||||
type = "ingress"
|
||||
ip_protocol = "tcp"
|
||||
port_range = "80,443"
|
||||
cidr_ip = "0.0.0.0/0"
|
||||
policy = "accept"
|
||||
}
|
||||
|
||||
resource "tencentcloud_security_group_rule" "wireguard" {
|
||||
security_group_id = tencentcloud_security_group.edge.id
|
||||
type = "ingress"
|
||||
ip_protocol = "udp"
|
||||
port_range = tostring(var.wireguard_port)
|
||||
cidr_ip = var.relay_admin_cidr
|
||||
policy = "accept"
|
||||
}
|
||||
|
||||
# cloud-init: WireGuard listener, forwarding sysctls, and Caddy for TLS.
|
||||
# The relay never holds application credentials; it forwards bytes.
|
||||
locals {
|
||||
relay_user_data = <<-EOT
|
||||
#cloud-config
|
||||
package_update: true
|
||||
packages: [wireguard, wireguard-tools, caddy]
|
||||
write_files:
|
||||
- path: /etc/wireguard/wg0.conf
|
||||
permissions: "0600"
|
||||
content: |
|
||||
[Interface]
|
||||
Address = ${var.wireguard_relay_address}
|
||||
ListenPort = ${var.wireguard_port}
|
||||
PrivateKey = ${var.wireguard_relay_private_key}
|
||||
[Peer]
|
||||
PublicKey = ${var.wireguard_homelab_public_key}
|
||||
AllowedIPs = ${var.wireguard_homelab_address}
|
||||
PersistentKeepalive = 25
|
||||
- path: /etc/sysctl.d/99-wireguard.conf
|
||||
content: |
|
||||
net.ipv4.ip_forward = 1
|
||||
runcmd:
|
||||
- sysctl --system
|
||||
- systemctl enable --now wg-quick@wg0
|
||||
- systemctl enable --now caddy
|
||||
EOT
|
||||
}
|
||||
|
||||
resource "tencentcloud_instance" "relay" {
|
||||
instance_name = "${replace(var.china_domain, ".", "_")}_edge"
|
||||
availability_zone = var.tencent_availability_zone
|
||||
image_id = var.relay_image_id
|
||||
instance_type = var.relay_instance_type
|
||||
|
||||
vpc_id = tencentcloud_vpc.edge.id
|
||||
subnet_id = tencentcloud_subnet.edge.id
|
||||
security_groups = [tencentcloud_security_group.edge.id]
|
||||
key_ids = [tencentcloud_key_pair.edge.id]
|
||||
allocate_public_ip = true
|
||||
|
||||
# Outbound only needs a few Mbps; inbound TLS is the same order.
|
||||
internet_max_bandwidth_out = 20
|
||||
system_disk_type = "CLOUD_PREMIUM"
|
||||
system_disk_size = 50
|
||||
|
||||
user_data = local.relay_user_data
|
||||
|
||||
tags = {
|
||||
role = "china-edge"
|
||||
domain = var.china_domain
|
||||
}
|
||||
}
|
||||
|
||||
# DNS for the .cn domain lives at DNSPod (Tencent). Each subdomain resolves to
|
||||
# the relay's public address, and the relay proxies to the homelab over the
|
||||
# tunnel. The "中国境内" line is the default line; no separate line records.
|
||||
resource "tencentcloud_dnspod_record" "china" {
|
||||
for_each = toset(var.china_hostnames)
|
||||
|
||||
domain = var.china_domain
|
||||
sub_domain = each.value
|
||||
record_type = "A"
|
||||
record_line = "默认"
|
||||
value = tencentcloud_instance.relay.public_ip
|
||||
ttl = 600
|
||||
remark = "China edge relay; origin is the homelab ingress over WireGuard"
|
||||
}
|
||||
Reference in New Issue
Block a user