Skip to content

Commit adb0120

Browse files
feat: dual-mode TLS for NGINX Gateway Fabric AWS module
Adds ACM mode detection: when domains have ACM ARNs as certificate_reference and no ACK controller is available, TLS terminates at the NLB instead of the Gateway pod. - Detects ACM mode via certificate_reference + ACK controller presence - Adds NLB ssl-cert/ssl-ports annotations in ACM mode - Passes var.instance as-is in ACM mode (no domain rewriting) - Gates ACK Certificate CRD resources on ACK controller availability - Passes external_tls_termination=true to base utility module Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 693c1bf commit adb0120

1 file changed

Lines changed: 34 additions & 11 deletions

File tree

  • modules/ingress/nginx_gateway_fabric_legacy_aws/1.0

modules/ingress/nginx_gateway_fabric_legacy_aws/1.0/main.tf

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,32 @@ locals {
1919
if can(domain.certificate_reference) && length(regexall("arn:aws:acm:", lookup(domain, "certificate_reference", ""))) > 0
2020
}
2121

22+
# Detect ACK ACM controller availability
23+
use_ack_acm = try(var.inputs.ack_acm_controller_details, null) != null
24+
25+
# ACM mode: when ACM domains exist but no ACK controller,
26+
# TLS terminates at the NLB instead of at the Gateway pod
27+
acm_mode = !local.use_ack_acm && length(local.acm_cert_domains) > 0
28+
29+
# ACM ARNs to attach to NLB for TLS termination
30+
acm_cert_arns = local.acm_mode ? [
31+
for domain_key, domain in local.acm_cert_domains : domain.certificate_reference
32+
] : []
33+
2234
# K8s secret name for ACM cert domains — the ACK Certificate CRD exports cert to this secret
35+
# Only relevant when ACK controller is available
2336
acm_cert_secret_names = {
2437
for domain_key, domain in local.acm_cert_domains :
2538
domain_key => "${local.name}-${domain_key}-acm-tls"
2639
}
2740

28-
# Rewrite instance domains: replace ACM ARN certificate_reference with K8s secret name
29-
# The base module only sees K8s secret names — never ACM ARNs
41+
# Rewrite instance domains based on TLS termination mode:
42+
# - ACK path: replace ACM ARN with K8s secret name (TLS at Gateway)
43+
# - ACM mode: no rewriting needed (base module ignores certificate_reference with external_tls_termination)
44+
# - Otherwise: pass through unchanged
3045
acm_modified_domains = {
3146
for domain_key, domain in lookup(var.instance.spec, "domains", {}) :
32-
domain_key => contains(keys(local.acm_cert_secret_names), domain_key) ? merge(domain, {
47+
domain_key => local.use_ack_acm && contains(keys(local.acm_cert_secret_names), domain_key) ? merge(domain, {
3348
certificate_reference = local.acm_cert_secret_names[domain_key]
3449
}) : domain
3550
}
@@ -80,8 +95,9 @@ locals {
8095
)
8196

8297
# Build modified instance with rewritten domains
83-
# When DNS-01 is active: disable_base_domain=true (we re-add it ourselves with certificate_reference)
84-
modified_instance = merge(var.instance, {
98+
# ACM mode: pass var.instance as-is — base module ignores certificate_reference when external_tls_termination=true
99+
# cert-manager/ACK mode: apply domain rewrites and DNS-01 overrides
100+
modified_instance = local.acm_mode ? var.instance : merge(var.instance, {
85101
spec = merge(var.instance.spec, {
86102
domains = local.modified_domains
87103
disable_base_domain = local.use_dns01 && !lookup(var.instance.spec, "disable_base_domain", false) ? true : lookup(var.instance.spec, "disable_base_domain", false)
@@ -119,7 +135,12 @@ locals {
119135
"service.beta.kubernetes.io/aws-load-balancer-nlb-target-type" = "ip"
120136
"service.beta.kubernetes.io/aws-load-balancer-backend-protocol" = "tcp"
121137
"service.beta.kubernetes.io/aws-load-balancer-target-group-attributes" = lookup(var.instance.spec, "private", false) ? "proxy_protocol_v2.enabled=true,preserve_client_ip.enabled=false" : "proxy_protocol_v2.enabled=true,preserve_client_ip.enabled=true"
122-
}
138+
},
139+
# ACM mode: attach ACM certs to NLB for TLS termination
140+
local.acm_mode ? {
141+
"service.beta.kubernetes.io/aws-load-balancer-ssl-cert" = join(",", local.acm_cert_arns)
142+
"service.beta.kubernetes.io/aws-load-balancer-ssl-ports" = "443"
143+
} : {}
123144
)
124145
}
125146

@@ -132,7 +153,8 @@ module "nginx_gateway_fabric" {
132153
environment = var.environment
133154
inputs = local.merged_inputs
134155

135-
service_annotations = local.aws_annotations
156+
service_annotations = local.aws_annotations
157+
external_tls_termination = local.acm_mode
136158

137159
load_balancer_class = "service.k8s.aws/nlb"
138160

@@ -149,10 +171,10 @@ module "nginx_gateway_fabric" {
149171

150172
# ACK ACM Certificate CRD resources — creates ACM certificates via ACK controller
151173
# and exports them to K8s TLS secrets for Gateway listener consumption.
152-
# Only created for domains whose certificate_reference is an ACM ARN.
153-
# Requires the ack_acm_controller to be deployed (optional input).
174+
# Only created when ACK controller is available and domains have ACM ARNs.
175+
# When ACK is not available, ACM certs are attached directly to the NLB instead.
154176
module "ack_acm_certificate" {
155-
for_each = local.acm_cert_domains
177+
for_each = local.use_ack_acm ? local.acm_cert_domains : {}
156178

157179
source = "github.com/Facets-cloud/facets-utility-modules//any-k8s-resource"
158180
name = "${local.name}-acm-cert-${each.key}"
@@ -187,8 +209,9 @@ module "ack_acm_certificate" {
187209

188210
# Pre-create empty TLS secrets for ACK ACM certificate export
189211
# ACK ACM controller requires the target secret to exist before it can export
212+
# Only created when ACK controller is available
190213
resource "kubernetes_secret_v1" "acm_cert" {
191-
for_each = local.acm_cert_domains
214+
for_each = local.use_ack_acm ? local.acm_cert_domains : {}
192215

193216
metadata {
194217
name = local.acm_cert_secret_names[each.key]

0 commit comments

Comments
 (0)