# 🤖 The delegated Route53 hosted zone for dev.11ch.at — the region-neutral base # domain for multi-edge Phase 1 (docs/multi-edge.md). It is NS-delegated from the # prod 11ch.at DNSimple zone (176940) in dns.tf, so DNSimple stays the registrar # and authoritative for everything ELSE under 11ch.at; only the dev.11ch.at # subtree lives here, in the dev AWS account. # # Why a Route53 zone at all: the dev boxes need a *.dev.11ch.at WILDCARD cert # (both boxes must terminate TLS for any region-neutral host, since either may own # it by home_region or proxy to the box that does — mesh.go). A wildcard cert is # DNS-01 only, and Route53's per-zone IAM lets a box solve that challenge with its # AWS role/user — NO DNS token on disk (route53dns.go, LCHAT_DNS_PROVIDER=route53). # DNSimple's account-scoped tokens can't be confined to one zone, which is the # friction this sidesteps. DNSimple remains a fully-supported provider everywhere # else — this is the AWS off-ramp, additive, never a replacement. resource "aws_route53_zone" "dev" { name = "dev.11ch.at" comment = "dev mesh — region-neutral space addressing (docs/multi-edge.md)" } # The region-neutral wildcard: new spaces mint as .dev.11ch.at — ONE # namespace, no country label (the global id ledger keeps ids fleet-unique, # idalloc). Points at the Dublin (ie) box, the mesh front door: it receives any # region-neutral host and either serves it (home_region == ie) or reverse-proxies # to the box whose region owns it (mesh.go proxyTo). A single-label wildcard, so # it never shadows the deeper two-label *.ie.dev / *.fi.dev records below. resource "aws_route53_record" "dev_wildcard" { zone_id = aws_route53_zone.dev.zone_id name = "*.dev.11ch.at" type = "A" ttl = 60 records = [aws_eip.ie_node.public_ip] } # The APEX of the dev subtree — dev.11ch.at itself, with no label in front. A # wildcard does NOT cover the bare name it wildcards, so without this record # dev.11ch.at resolves nowhere no matter how many *.dev.11ch.at hosts exist. # # It exists for the short-link redirector (lchat-links.service, issue #905), # which is an apex service by nature: production serves 11ch.at itself, so the # dev mesh has to serve dev.11ch.at itself to rehearse it at all. Nothing else # needs the apex today. # # This is the dev-side mirror of a real production gap: 11ch.at's own apex A # record and route are what were missing, so the redirector had nowhere to be # reached even after its code merged — a wildcard cert on *.11ch.at made it # look configured while the apex failed the TLS handshake outright. resource "aws_route53_record" "dev_apex" { zone_id = aws_route53_zone.dev.zone_id name = "dev.11ch.at" type = "A" ttl = 60 records = [aws_eip.ie_node.public_ip] } # The two per-country wildcards, MOVED here from DNSimple (they were in the old # dns.tf). The delegation makes DNSimple non-authoritative for the whole # dev.11ch.at subtree, so these must live in THIS zone or every existing # *.ie.dev / *.fi.dev host — including each box's own one..dev.11ch.at self # URL (LCHAT_MESH_SELF) — would stop resolving. Kept so the existing per-country # hosts and the mesh peer-dial addresses survive the cutover unchanged. resource "aws_route53_record" "ie_wildcard" { zone_id = aws_route53_zone.dev.zone_id name = "*.ie.dev.11ch.at" type = "A" ttl = 60 records = [aws_eip.ie_node.public_ip] } resource "aws_route53_record" "fi_wildcard" { zone_id = aws_route53_zone.dev.zone_id name = "*.fi.dev.11ch.at" type = "A" ttl = 60 records = [var.fi_node_ip] } # ---- DNS-01 permissions for the *.dev.11ch.at wildcard cert -------------------- # The boxes publish then delete an _acme-challenge TXT in THIS zone # (route53dns.go). ListHostedZonesByName and GetChange take no resource-level # permissions in AWS (they're account-wide by design), so they're granted on "*"; # the one MUTATING op — ChangeResourceRecordSets — is locked to this single zone. # A leaked box credential could therefore touch dev.11ch.at records and nothing # else in the account: not prod DNS (it's in DNSimple), not any other zone. data "aws_iam_policy_document" "route53_dns01" { statement { sid = "DiscoverZoneAndChange" actions = ["route53:ListHostedZonesByName", "route53:GetChange"] resources = ["*"] } statement { sid = "WriteChallengeInDevZoneOnly" actions = ["route53:ChangeResourceRecordSets"] resources = [aws_route53_zone.dev.arn] } } # Dublin (ie) reaches Route53 through its EC2 instance role (no keys on the box). resource "aws_iam_role_policy" "ie_node_route53" { name = "eleven-dev-mesh-route53-dns01" role = aws_iam_role.ie_node.id policy = data.aws_iam_policy_document.route53_dns01.json } # Helsinki (fi) is Hetzner — it can't assume an EC2 role, so the same policy # attaches to its scoped IAM user (the S3 credential it already carries lives in # the vault; this just widens that user to the one DNS zone). resource "aws_iam_user_policy" "fi_node_route53" { name = "eleven-dev-mesh-route53-dns01" user = aws_iam_user.fi_node.name policy = data.aws_iam_policy_document.route53_dns01.json }