Skip to content

Commit abc0187

Browse files
authored
Allow multiple root domains for hosted sites, external dns, and cert manager (#181)
* Allow multiple root domains for hosted sites, external dns, and cert manager * Fix error message for validation * Fix error message for validation * Removed unnecessary domain_name reference * Bumped version on sendgrid module, changed default prefix to blank
1 parent 3a05dfe commit abc0187

15 files changed

Lines changed: 134 additions & 5980 deletions

File tree

templates/kubernetes/terraform/environments/prod/application_iam_policy.tf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ data "aws_iam_policy_document" "resource_access_backendservice" {
88
]
99
resources = ["arn:aws:ec2:::prod-*"]
1010
}
11+
<% if eq (index .Params `fileUploads`) "yes" %>
1112
statement {
1213
effect = "Allow"
1314
actions = [
@@ -16,5 +17,6 @@ data "aws_iam_policy_document" "resource_access_backendservice" {
1617
]
1718
resources = ["arn:aws:s3:::files.${local.domain_name}/*"]
1819
}
20+
<% end %>
1921
# can be more statements here
2022
}

templates/kubernetes/terraform/environments/prod/main.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ module "kubernetes" {
4343
# Authenticate with the EKS cluster via the cluster id
4444
cluster_name = "${local.project}-prod-${local.region}"
4545

46-
external_dns_zone = local.domain_name
46+
external_dns_zones = [local.domain_name]
4747
external_dns_owner_id = "${local.project}-prod-${local.region}"
4848

4949
# Registration email for LetsEncrypt
@@ -62,7 +62,7 @@ module "kubernetes" {
6262
service_account = "backend-service"
6363
namespace = local.project
6464
policy = data.aws_iam_policy_document.resource_access_backendservice
65-
}
65+
},
6666
# Add additional mappings here
6767
]
6868

templates/kubernetes/terraform/environments/stage/application_iam_policy.tf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ data "aws_iam_policy_document" "resource_access_backendservice" {
88
]
99
resources = ["arn:aws:ec2:::stage-*"]
1010
}
11+
<% if eq (index .Params `fileUploads`) "yes" %>
1112
statement {
1213
effect = "Allow"
1314
actions = [
@@ -16,5 +17,6 @@ data "aws_iam_policy_document" "resource_access_backendservice" {
1617
]
1718
resources = ["arn:aws:s3:::files.${local.domain_name}/*"]
1819
}
20+
<% end %>
1921
# can be more statements here
2022
}

templates/kubernetes/terraform/environments/stage/main.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ module "kubernetes" {
4242
# Authenticate with the EKS cluster via the cluster id
4343
cluster_name = "${local.project}-stage-${local.region}"
4444

45-
external_dns_zone = local.domain_name
45+
external_dns_zones = [local.domain_name]
4646
external_dns_owner_id = "${local.project}-stage-${local.region}"
4747

4848
# Registration email for LetsEncrypt
@@ -61,7 +61,7 @@ module "kubernetes" {
6161
service_account = "backend-service"
6262
namespace = local.project
6363
policy = data.aws_iam_policy_document.resource_access_backendservice
64-
}
64+
},
6565
# Add additional mappings here
6666
]
6767

templates/kubernetes/terraform/modules/kubernetes/cert_manager.tf

Lines changed: 79 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,68 @@ locals {
22
cert_manager_version = "1.1.0"
33
cluster_issuer_name = var.cert_manager_use_production_acme_environment ? "clusterissuer-letsencrypt-production" : "clusterissuer-letsencrypt-staging"
44
cert_manager_acme_server = var.cert_manager_use_production_acme_environment ? "https://acme-v02.api.letsencrypt.org/directory" : "https://acme-staging-v02.api.letsencrypt.org/directory"
5+
6+
# Enable the HTTP-01 challenge provider to resolve a challenge through the ingress
7+
cluster_issuer_http = yamlencode({
8+
"apiVersion" = "cert-manager.io/v1"
9+
"kind" = "ClusterIssuer"
10+
"metadata" = {
11+
"name" = local.cluster_issuer_name
12+
}
13+
"spec" = {
14+
"acme" = {
15+
"email" = var.cert_manager_acme_registration_email
16+
"privateKeySecretRef" = {
17+
"name" = "clusterissuer-letsencrypt-${var.environment}-secret" # Name of a secret used to store the ACME account private key
18+
}
19+
"server" = local.cert_manager_acme_server # Email address used for ACME registration
20+
"solvers" = [
21+
{
22+
"http01" = {
23+
"ingress" = {
24+
"class" = "nginx"
25+
}
26+
}
27+
},
28+
]
29+
}
30+
}
31+
})
32+
33+
# Create solver records for each domain
34+
dns_solvers = [for index, domain in var.external_dns_zones : {
35+
"dns01" = {
36+
"route53" = {
37+
"hostedZoneID" = data.aws_route53_zone.zones[index].zone_id
38+
"region" = var.region
39+
}
40+
}
41+
"selector" = {
42+
"dnsZones" = [
43+
domain,
44+
]
45+
}
46+
}
47+
]
48+
49+
# Enable the DNS-01 challenge provider to resolve a challenge by creating route53 records
50+
cluster_issuer_dns = yamlencode({
51+
"apiVersion" = "cert-manager.io/v1"
52+
"kind" = "ClusterIssuer"
53+
"metadata" = {
54+
"name" = "${local.cluster_issuer_name}-dns"
55+
}
56+
"spec" = {
57+
"acme" = {
58+
"email" = var.cert_manager_acme_registration_email # Email address used for ACME registration
59+
"privateKeySecretRef" = {
60+
"name" = "clusterissuer-letsencrypt-${var.environment}-secret" # Name of a secret used to store the ACME account private key
61+
}
62+
"server" = local.cert_manager_acme_server
63+
"solvers" = local.dns_solvers
64+
}
65+
}
66+
})
567
}
668

769
resource "kubernetes_namespace" "cert_manager" {
@@ -11,8 +73,9 @@ resource "kubernetes_namespace" "cert_manager" {
1173
}
1274

1375
# Reference an existing route53 zone
14-
data "aws_route53_zone" "public" {
15-
name = var.external_dns_zone
76+
data "aws_route53_zone" "zones" {
77+
count = length(var.external_dns_zones)
78+
name = var.external_dns_zones[count.index]
1679
}
1780

1881
resource "helm_release" "cert_manager" {
@@ -36,28 +99,26 @@ resource "helm_release" "cert_manager" {
3699
}
37100
}
38101

39-
# Cert-manager issuer manifest
40-
data "template_file" "cert_manager_issuer" {
41-
template = file("${path.module}/files/cert_manager_issuer.yaml.tpl")
42-
vars = {
43-
name = local.cluster_issuer_name
44-
environment = var.environment
45-
acme_registration_email = var.cert_manager_acme_registration_email
46-
acme_server = local.cert_manager_acme_server
47-
region = var.region
48-
hosted_zone_id = data.aws_route53_zone.public.zone_id
102+
# Manually kubectl apply the cert-manager issuers, as the kubernetes terraform provider
103+
# does not have support for custom resources.
104+
resource "null_resource" "cert_manager_http_issuer" {
105+
triggers = {
106+
manifest_sha1 = sha1(local.cluster_issuer_http)
107+
}
108+
# local exec call requires kubeconfig to be updated
109+
provisioner "local-exec" {
110+
command = "kubectl apply ${local.k8s_exec_context} -f - <<EOF\n${local.cluster_issuer_http}\nEOF"
49111
}
112+
depends_on = [helm_release.cert_manager]
50113
}
51114

52-
# Manually kubectl apply the cert-manager issuer, as the kubernetes terraform provider
53-
# does not have support for custom resources.
54-
resource "null_resource" "cert_manager_issuer" {
115+
resource "null_resource" "cert_manager_dns_issuer" {
55116
triggers = {
56-
manifest_sha1 = sha1(data.template_file.cert_manager_issuer.rendered)
117+
manifest_sha1 = sha1(local.cluster_issuer_dns)
57118
}
58119
# local exec call requires kubeconfig to be updated
59120
provisioner "local-exec" {
60-
command = "kubectl apply ${local.k8s_exec_context} -f - <<EOF\n${data.template_file.cert_manager_issuer.rendered}\nEOF"
121+
command = "kubectl apply ${local.k8s_exec_context} -f - <<EOF\n${local.cluster_issuer_dns}\nEOF"
61122
}
62123
depends_on = [helm_release.cert_manager]
63124
}
@@ -100,7 +161,7 @@ data "aws_iam_policy_document" "cert_manager_policy_doc" {
100161
"route53:ListResourceRecordSets"
101162
]
102163

103-
resources = ["arn:aws:route53:::hostedzone/${data.aws_route53_zone.public.zone_id}"]
164+
resources = [for domain in var.external_dns_zones : "arn:aws:route53:::hostedzone/${domain}"]
104165
}
105166

106167
statement {

templates/kubernetes/terraform/modules/kubernetes/external_dns.tf

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ data "aws_iam_policy_document" "external_dns_policy_doc" {
3434

3535
actions = ["route53:ChangeResourceRecordSets"]
3636

37-
resources = ["arn:aws:route53:::hostedzone/*"]
37+
resources = [for domain in var.external_dns_zones : "arn:aws:route53:::hostedzone/${domain}"]
3838
}
3939
}
4040

@@ -112,17 +112,18 @@ resource "kubernetes_deployment" "external_dns" {
112112
container {
113113
name = "external-dns"
114114
image = "registry.opensource.zalan.do/teapot/external-dns:latest"
115-
args = [
115+
args = concat([
116116
"--source=ingress",
117117
"--source=service",
118-
"--domain-filter=${var.external_dns_zone}", # Give access only to the specified zone
119118
"--provider=aws",
120119
"--aws-zone-type=public",
121120
"--policy=upsert-only", # Prevent ExternalDNS from deleting any records
122121
"--registry=txt",
123122
"--txt-owner-id=${var.cluster_name}", # ID of txt record to manage state
124123
"--aws-batch-change-size=2", # Set the batch size to 2 so that a single record failure won't block other updates
125-
]
124+
],
125+
# Give access only to the specified zones
126+
[for domain in var.external_dns_zones : "--domain-filter=${domain}"])
126127
}
127128

128129
security_context {

0 commit comments

Comments
 (0)