carlos / platform Public

infra/region-host: route53 grant narrowed to ACME challenge TXTs; validate zone ids

Paul Campbell bbd3f142fa56ecbcd14b2e241473defef8fa0be9 parent bfbe48d
2 files changed, +17 −0
  • infra/modules/region-host/main.tf +13 −0
  • infra/modules/region-host/variables.tf +4 −0
diff --git a/infra/modules/region-host/main.tf b/infra/modules/region-host/main.tf
index 8d378e90a..21afdcbca 100644
--- a/infra/modules/region-host/main.tf
+++ b/infra/modules/region-host/main.tf
@@ -113,6 +113,19 @@ resource "aws_iam_role_policy" "host_route53" {
Effect = "Allow"
Action = ["route53:ChangeResourceRecordSets"]
Resource = [for z in var.route53_zone_ids : "arn:aws:route53:::hostedzone/${z}"]
+ # The box may touch ONLY _acme-challenge TXT records, and only via the
+ # two operations the edge issues (UPSERT in present, DELETE in cleanup).
+ # Without these conditions a zone grant is the whole zone — apex, MX,
+ # NS — and this policy is the only bound on the box's DNS authority.
+ Condition = {
+ "ForAllValues:StringEquals" = {
+ "route53:ChangeResourceRecordSetsRecordTypes" = ["TXT"]
+ "route53:ChangeResourceRecordSetsActions" = ["UPSERT", "DELETE"]
+ }
+ "ForAllValues:StringLike" = {
+ "route53:ChangeResourceRecordSetsNormalizedRecordNames" = ["_acme-challenge.*"]
+ }
+ }
},
{
Effect = "Allow"
diff --git a/infra/modules/region-host/variables.tf b/infra/modules/region-host/variables.tf
index e73e783be..5003e4d35 100644
--- a/infra/modules/region-host/variables.tf
+++ b/infra/modules/region-host/variables.tf
@@ -71,4 +71,8 @@ variable "route53_zone_ids" {
description = "Hosted-zone IDs the host may write ACME DNS-01 challenge records into (wildcard certs, CARLOS_DNS_PROVIDER=route53). Empty = no Route53 access."
type = list(string)
default = []
+ validation {
+ condition = alltrue([for z in var.route53_zone_ids : can(regex("^Z[A-Z0-9]+$", z))])
+ error_message = "route53_zone_ids entries must be bare hosted-zone ids (Z...), not /hostedzone/-prefixed paths or wildcards."
+ }
}