Skip to content

Commit 474d171

Browse files
jms200claude
andauthored
feat: create all connectivity SG rules unconditionally (DND-1522) (#58)
* feat!: create all connectivity SG rules unconditionally (DND-1522) Connectivity to an STSaaS environment is never a per-environment decision, but the module treated all of it as opt-in. Drop the four enable_* toggles (all default false — their only correct value was true) and create their SG ingress rules unconditionally: - enable_argocd_management_eks_access - enable_vpn_eks_api_access - enable_ci_runners_eks_api_access - enable_vpn_redis_access Add a matching unconditional VPN ingress on mysql_sg (port 3306), mirroring redis_vpn in modules/comet_elasticache, so Aurora is reachable from the VPN through the module (new vpn_client_cidr var on comet_rds + root passthrough). Re-export mysql_sg_id from the root outputs.tf so wrappers no longer have to look the SG up by name (comet_rds already output it; root did not). The CIDR inputs (argocd_management_cidrs, vpn_client_cidr, ci_runners_cidr) stay as variables — those are real values. BREAKING CHANGE: the four enable_* input variables are removed. Wrappers that still set them must drop them (their value is now always applied). Warrants a v6.0.0 bump. Only stsaasuat runs v5.x today; every other env picks this up when it migrates. The next stsaasuat apply will add the SG rules. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: address review — moved block for redis_vpn, redis_sg_id output, CIDR dedup Review feedback on DND-1522. 1. redis_vpn moved block. Dropping count changed the state address from redis_vpn[0] to redis_vpn, which plans an unordered destroy + create of two unrelated addresses — it can race into InvalidPermission.Duplicate, and even on the happy path leaves a window with no VPN ingress to Redis. This is live on stsaasuat (enable_vpn_redis_access = true). The address is internal to the module, so without the moved block every migrating env needs a hand-run terraform state mv. No-op where the toggle was off. 2. Export redis_sg_id. The motivation for re-exporting mysql_sg_id applies identically to Redis, and more widely: four wrappers (fetch, netflix, si, waystar) look the Redis SG up by name versus two for MySQL. Wrappers are edited on the version bump anyway, so shipping both now avoids a second pass. 3. Dedupe eks_api_ingress_rules across all sources, not just within argocd_management_cidrs. AWS dedupes ingress on (protocol, port range, source), so a cross-source CIDR collision would fail the apply, and the enable_* toggles that used to make it avoidable are gone. Candidates are now ordered and the first per CIDR wins. Verified against the fleet defaults: the for_each keys are byte-for-byte unchanged, so there is no state churn. Also fixes the comment naming cluster_security_group_id where the code uses cluster_primary_security_group_id, and makes vpn_client_cidr a required input in the three submodules — the root always passes a value, so the submodule defaults were dead copies that could drift. Refs DND-1522 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: validate connectivity CIDRs are canonical and not overly broad Review feedback. Removing the enable_* toggles means a bad CIDR can no longer be neutralised by leaving the rule off — it always lands in an SG ingress rule, so the value is now validated at the root, where operators set it. Canonical form is required (network address, no host bits, unpadded prefix). The eks_api for_each keys and the duplicate filter derive from the raw string, so 10.126.0.0/015 and 10.126.0.0/15 would be two Terraform addresses for one AWS rule and collide as InvalidPermission.Duplicate. Rejecting beats silently canonicalising: it fails at plan rather than rewriting operator intent. Prefixes broader than /8 are rejected, which covers 0.0.0.0/0. Deliberately not an RFC1918 or fleet-range allowlist — a CI-runner NAT egress address is legitimately a public /32, and hardcoding the fleet's ranges into a reusable module recreates the "only one correct value" input DND-1522 removes. Verified against the root module: 0.0.0.0/0, 10.126.0.0/015 and 10.126.0.1/15 are all rejected with their specific message; the four fleet CIDRs and a public /32 pass. Refs DND-1522 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: reject IPv6 CIDRs at plan; drop remaining submodule CIDR defaults Three non-blocking review items from @liyaka on #58. 1. IPv6 passed validation. cidrhost() and split() are family-agnostic, so 2001:db8::/32 satisfied both conditions and only failed at apply, against cidr_ipv4, with an AWS error rather than the variable message that already said "canonical IPv4 CIDR". Add can(cidrnetmask(...)) to the canonical condition on all three inputs — it errors on IPv6 and accepts every IPv4 case, including the public /32 a CI-runner NAT egress can legitimately be. 2. The anti-drift rationale was applied to one variable of three. vpn_client_cidr was made a required submodule input with the default only at the root, but argocd_management_cidrs and ci_runners_cidr kept their comet_eks defaults, and ci_runners_cidr's description was never updated. Drop both defaults and align both descriptions. No behaviour change: the root passes all three unconditionally (main.tf:361-363). 3. Document the dedup ordering hazard, not just its rationale. Introducing a CIDR collision moves the surviving rule's Terraform address — vpn_client_cidr equal to an argocd CIDR retires the "vpn" key, so an env holding that rule plans a destroy plus a create at the new address and loses VPN reach to the EKS API for the apply. Comment only. Verified by extracting the three variable blocks verbatim into an isolated module and planning each case: IPv6 (v4 and v6 forms), host bits, padded prefix and 0.0.0.0/0 all rejected with the right message; fleet defaults and a public /32 accepted. terraform fmt clean; terraform validate Success with only the pre-existing upstream iam-role-for-service-accounts-eks warnings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: derive rds_proxy_allowed_cidrs from vpn_client_cidr Baz review on #58: rds_proxy_allowed_cidrs carried its own hardcoded ["10.126.0.0/15"] default, duplicated again in the submodule, independent of vpn_client_cidr. Renumber the VPN and the direct mysql_vpn rule this PR adds moves while the proxy keeps trusting the old range — the new pool can reach Aurora directly but is refused through the proxy, and the retired pool keeps proxy access. Exactly the drift the DND-1522 anti-duplication rationale is about, and this PR is what creates the divergence: before it, vpn_client_cidr had nothing to do with MySQL, so an independent proxy CIDR contradicted nothing. Make the root input a documented tri-state instead of concatenating, because the submodule documents [] as "SG-only ingress" and an unconditional concat would silently break that escape hatch: null (default) -> [var.vpn_client_cidr] both MySQL paths open to one pool [] -> [] SG-only ingress, preserved [c, ...] -> verbatim explicit override still wins Also drop the submodule's duplicate default (required input, default only at the root) and add the same canonical-IPv4 + /8-or-narrower validations the other three CIDR inputs got, null-tolerant. No state churn: under defaults the resolved list is byte-identical to the old hardcoded value, and proxy_from_cidr keys on the CIDR string, so every for_each key is unchanged. No consumer is affected either way — no env in comet-devops sets enable_rds_proxy yet. Verified each branch by plan (defaults, [], renumbered VPN, explicit list) and each validation case (IPv6, host bits, padded prefix, 0.0.0.0/0 rejected; [], null and multi-entry IPv4 accepted). fmt clean; validate Success with only the pre-existing upstream iam-role-for-service-accounts-eks warnings. Not fixed here: vpc_interface_endpoints_allowed_cidrs and tgw_propagated_cidrs hardcode the same management surface (variables.tf:1378,1396 + comet_vpc mirrors). They predate this PR, sit in the VPC layer rather than this connectivity-SG class, and tgw_propagated_cidrs is entangled with the TGW toggles already deferred. Noted as follow-up. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: resolve rds_proxy_allowed_cidrs' tri-state in a local Baz review on #58, and a real bug in 536ccf5. Making the default null left main.tf:122's precondition calling length(var.rds_proxy_allowed_cidrs), and null is not a list, so it raises "Invalid function argument" at plan. Narrower than reported, and worse placed. HCL's || does short-circuit (verified), so enable_eks = true or a non-empty rds_proxy_allowed_sg_ids never reaches the third operand. It breaks only with enable_eks = false and no rds_proxy_allowed_sg_ids — which is exactly the configuration this precondition exists to explain. The operator with no ingress source got a type error about length() instead of "enable_rds_proxy requires at least one ingress source". Resolve the tri-state once into local.rds_proxy_effective_cidrs and use it for both the precondition and the module argument. This also keeps the source count honest: under null the proxy does get one CIDR source, so null must satisfy that check, not fail it. coalesce(var.rds_proxy_allowed_cidrs, []) would have fixed the crash and got that backwards, firing "no ingress source" on the default. Full precondition matrix by plan, before vs after: enable_eks sg_ids cidrs before after true [] null pass pass false [] null Invalid function argument pass false [sg-1] null pass (short-circuits) pass false [] [] no ingress source no ingress source false [] [10.4.0.0/16] pass pass One cell changes, from a crash to the correct pass. The genuine no-ingress case still fires. fmt clean; validate Success with only the pre-existing upstream iam-role-for-service-accounts-eks warnings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 2e396b7 commit 474d171

11 files changed

Lines changed: 216 additions & 96 deletions

File tree

‎main.tf‎

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,23 @@ locals {
2121
var.enable_rds ? random_password.rds_master[0].result : null
2222
)
2323

24+
# Resolve rds_proxy_allowed_cidrs' tri-state once (DND-1522). The proxy is a second
25+
# path to the same Aurora cluster the mysql_vpn rule opens directly, so its CIDR
26+
# ingress follows vpn_client_cidr instead of carrying its own copy of the pool: a
27+
# duplicated default drifts on the first renumber, moving the direct path while the
28+
# proxy keeps trusting the retired range. null means follow; [] still means SG-only
29+
# ingress; an explicit list still wins.
30+
#
31+
# This has to be a local, not an inline conditional at each use. The
32+
# rds_proxy_validation precondition counts ingress sources with
33+
# length(rds_proxy_allowed_cidrs), and null is not a list — so resolving inline at the
34+
# module argument only would leave length(null) to raise "Invalid function argument"
35+
# in precisely the case the precondition exists to explain (enable_ec2 and enable_eks
36+
# both false, no rds_proxy_allowed_sg_ids). Deriving the effective list also keeps the count
37+
# honest: under null the proxy does get one CIDR source, so null must satisfy that
38+
# check rather than fail it — which coalesce(..., []) would get backwards.
39+
rds_proxy_effective_cidrs = var.rds_proxy_allowed_cidrs == null ? [var.vpn_client_cidr] : var.rds_proxy_allowed_cidrs
40+
2441
# Mirrors the comet_rds_proxy count exactly, so mysql_host can never dereference
2542
# module.comet_rds_proxy[0] when the module isn't instantiated.
2643
rds_proxy_provisioned = var.enable_rds_proxy && var.enable_rds
@@ -128,7 +145,7 @@ resource "terraform_data" "rds_proxy_validation" {
128145
# being able to connect. enable_ec2 / enable_eks auto-wire the app's SG; without
129146
# either, an explicit SG or CIDR has to be supplied.
130147
precondition {
131-
condition = var.enable_ec2 || var.enable_eks || length(var.rds_proxy_allowed_sg_ids) > 0 || length(var.rds_proxy_allowed_cidrs) > 0
148+
condition = var.enable_ec2 || var.enable_eks || length(var.rds_proxy_allowed_sg_ids) > 0 || length(local.rds_proxy_effective_cidrs) > 0
132149
error_message = "enable_rds_proxy requires at least one ingress source: enable_ec2=true or enable_eks=true (auto-wires the application SG), or non-empty rds_proxy_allowed_sg_ids, or non-empty rds_proxy_allowed_cidrs. Otherwise the proxy has no ingress rules and is unreachable."
133150
}
134151
precondition {
@@ -393,13 +410,10 @@ module "comet_eks" {
393410
# Karpenter Helm chart
394411
karpenter_extra_tags = var.eks_karpenter_extra_tags
395412

396-
# EKS API ingress — standardized fleet-wide access
397-
enable_argocd_management_eks_access = var.enable_argocd_management_eks_access
398-
argocd_management_cidrs = var.argocd_management_cidrs
399-
enable_vpn_eks_api_access = var.enable_vpn_eks_api_access
400-
vpn_client_cidr = var.vpn_client_cidr
401-
enable_ci_runners_eks_api_access = var.enable_ci_runners_eks_api_access
402-
ci_runners_cidr = var.ci_runners_cidr
413+
# EKS API ingress — standardized fleet-wide access (opened unconditionally, DND-1522)
414+
argocd_management_cidrs = var.argocd_management_cidrs
415+
vpn_client_cidr = var.vpn_client_cidr
416+
ci_runners_cidr = var.ci_runners_cidr
403417

404418
}
405419

@@ -429,8 +443,7 @@ module "comet_elasticache" {
429443
elasticache_multi_az_enabled = var.elasticache_multi_az_enabled
430444
elasticache_preferred_cache_cluster_azs = var.elasticache_preferred_cache_cluster_azs
431445

432-
enable_vpn_redis_access = var.enable_vpn_redis_access
433-
vpn_client_cidr = var.vpn_client_cidr
446+
vpn_client_cidr = var.vpn_client_cidr
434447
}
435448

436449
module "comet_rds" {
@@ -447,6 +460,8 @@ module "comet_rds" {
447460
rds_allow_from_sg = var.enable_ec2 ? module.comet_ec2[0].comet_ec2_sg_id : (
448461
var.enable_eks ? module.comet_eks[0].nodegroup_sg_id : (
449462
var.rds_allow_from_sg))
463+
vpn_client_cidr = var.vpn_client_cidr
464+
450465
# EKS Auto Mode nodes attach the cluster primary SG (distinct from the managed node SG
451466
# above), so grant them MySQL access too when Auto Mode is enabled.
452467
rds_auto_mode_allow_from_sg = var.enable_eks && var.eks_enable_auto_mode ? module.comet_eks[0].cluster_primary_security_group_id : null
@@ -515,7 +530,7 @@ module "comet_rds_proxy" {
515530

516531
# Same precedence as comet_rds's rds_allow_from_sg — EC2 first, then EKS, then the
517532
# explicit list. The EC2 branch was missing, so an enable_ec2 env got a proxy the
518-
# application could not reach: rds_proxy_allowed_cidrs defaults to the VPN pool, so
533+
# application could not reach: rds_proxy_allowed_cidrs follows the VPN pool, so
519534
# the "at least one ingress source" check passed on VPN access alone.
520535
# EKS: managed node SG + (with Auto Mode) the cluster primary SG that Auto Mode nodes
521536
# attach, so pods on either node type can reach the proxy.
@@ -525,7 +540,9 @@ module "comet_rds_proxy" {
525540
var.eks_enable_auto_mode ? [module.comet_eks[0].cluster_primary_security_group_id] : [],
526541
) : var.rds_proxy_allowed_sg_ids
527542
)
528-
allowed_cidrs = var.rds_proxy_allowed_cidrs
543+
544+
# Resolved once in locals, and shared with the rds_proxy_validation precondition.
545+
allowed_cidrs = local.rds_proxy_effective_cidrs
529546

530547
mysql_cluster_id = module.comet_rds[0].mysql_cluster_id
531548
mysql_sg_id = module.comet_rds[0].mysql_sg_id

‎modules/comet_eks/main.tf‎

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,36 +1484,70 @@ module "karpenter_irsa" {
14841484
#########################################
14851485
#### EKS API ingress — fleet-wide CIDRs ####
14861486
#########################################
1487-
# These open the EKS cluster_security_group_id (the cluster's primary SG) on
1488-
# port 443 to fleet-wide management surfaces. Each ingress source is gated by
1489-
# its own toggle; the local map merges all enabled rules into a single
1490-
# for_each so the common attributes live in one place.
1487+
# These open the EKS cluster_primary_security_group_id on port 443 to fleet-wide
1488+
# management surfaces (ArgoCD management, VPN clients, CI runners). Connectivity
1489+
# is never a per-environment decision, so every source is opened unconditionally
1490+
# (DND-1522); the CIDRs remain inputs. The local map merges all rules into a
1491+
# single for_each so the common attributes live in one place.
14911492

14921493
locals {
1493-
eks_api_ingress_rules = merge(
1494-
var.enable_argocd_management_eks_access ? {
1495-
for cidr in distinct(var.argocd_management_cidrs) :
1496-
"argocd-management-${replace(cidr, "/", "_")}" => {
1494+
# Ordered candidates — on a CIDR collision the earlier entry wins.
1495+
eks_api_rule_candidates = concat(
1496+
[
1497+
for cidr in distinct(var.argocd_management_cidrs) : {
1498+
key = "argocd-management-${replace(cidr, "/", "_")}"
14971499
cidr = cidr
14981500
description = "Allow ArgoCD management to reach EKS API from ${cidr}"
14991501
name = "argocd-management-access-${replace(cidr, "/", "_")}"
15001502
}
1501-
} : {},
1502-
var.enable_vpn_eks_api_access ? {
1503-
"vpn" = {
1503+
],
1504+
[
1505+
{
1506+
key = "vpn"
15041507
cidr = var.vpn_client_cidr
15051508
description = "Allow VPN clients to reach EKS API"
15061509
name = "vpn-eks-api-access"
15071510
}
1508-
} : {},
1509-
var.enable_ci_runners_eks_api_access ? {
1510-
"ci-runners" = {
1511+
],
1512+
[
1513+
{
1514+
key = "ci-runners"
15111515
cidr = var.ci_runners_cidr
15121516
description = "Allow CI cluster runners to reach EKS API"
15131517
name = "ci-runners-eks-api-access"
15141518
}
1515-
} : {},
1519+
],
15161520
)
1521+
1522+
# AWS dedupes ingress on (protocol, port range, source), so two candidates
1523+
# sharing a CIDR would collide as InvalidPermission.Duplicate on apply. Keep
1524+
# only the first candidate for each CIDR. distinct() alone can't do this — it
1525+
# covered argocd_management_cidrs but not a cross-source collision (e.g.
1526+
# vpn_client_cidr equal to an argocd CIDR), and the enable_* toggles that used
1527+
# to make such a collision avoidable are gone (DND-1522). The default CIDRs
1528+
# don't collide, so every for_each key is unchanged and this is a no-op
1529+
# against existing state.
1530+
#
1531+
# WARNING — the winner keeps its own key, so introducing a collision moves the
1532+
# rule's Terraform address. Set vpn_client_cidr equal to an argocd CIDR and the
1533+
# "vpn" key disappears in favour of "argocd-management-<cidr>"; for an env
1534+
# already holding that rule under "vpn" the plan is a destroy plus a create at
1535+
# the new address, i.e. a window with no VPN reach to the EKS API. (Equal to
1536+
# ci_runners_cidr drops "ci-runners" instead — the order above decides which.)
1537+
# Same failure mode the redis_vpn moved block exists to prevent, reached
1538+
# through a config change rather than a refactor. If such a collision is ever
1539+
# deliberate, add a moved block for the retired key in the same change.
1540+
eks_api_ingress_rules = {
1541+
for candidate in local.eks_api_rule_candidates :
1542+
candidate.key => {
1543+
cidr = candidate.cidr
1544+
description = candidate.description
1545+
name = candidate.name
1546+
}
1547+
if candidate.key == [
1548+
for other in local.eks_api_rule_candidates : other.key if other.cidr == candidate.cidr
1549+
][0]
1550+
}
15171551
}
15181552

15191553
resource "aws_vpc_security_group_ingress_rule" "eks_api" {

‎modules/comet_eks/variables.tf‎

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -846,40 +846,19 @@ variable "eks_clickhouse_subnet_ids" {
846846
#### EKS API ingress — standardized fleet-wide access patterns
847847
#####################
848848

849-
variable "enable_argocd_management_eks_access" {
850-
description = "Open EKS API (port 443) to the ArgoCD management cluster CIDRs in argocd_management_cidrs. Required for ArgoCD to deploy into this cluster from the central mgmt cluster."
851-
type = bool
852-
default = false
853-
}
854-
855849
variable "argocd_management_cidrs" {
856-
description = "CIDRs allowed to reach the EKS API for ArgoCD management. Defaults cover the ArgoCD mgmt VPC + cluster CIDRs."
850+
description = "CIDRs allowed to reach the EKS API for ArgoCD management. Opened unconditionally (DND-1522). Required — the root module always supplies it; the default lives only there so the value can't drift between submodules."
857851
type = list(string)
858-
default = ["10.162.0.0/16", "10.100.0.0/16"]
859-
}
860-
861-
variable "enable_vpn_eks_api_access" {
862-
description = "Open EKS API (port 443) to the VPN client pool CIDR. Required after flipping endpoint_public_access=false (DND-915)."
863-
type = bool
864-
default = false
865852
}
866853

867854
variable "vpn_client_cidr" {
868-
description = "CIDR of the VPN client pool. Used by enable_vpn_eks_api_access and enable_vpn_redis_access."
855+
description = "CIDR of the VPN client pool. Opened on the EKS API (DND-915) and passed through to Redis/MySQL SG rules (DND-1522). Required — the root module always supplies it; the default lives only there so the value can't drift between submodules."
869856
type = string
870-
default = "10.126.0.0/15"
871-
}
872-
873-
variable "enable_ci_runners_eks_api_access" {
874-
description = "Open EKS API (port 443) to the CI runners cluster CIDR. Required for CI workflows that exec against the cluster (DND-1153)."
875-
type = bool
876-
default = false
877857
}
878858

879859
variable "ci_runners_cidr" {
880-
description = "CIDR of the CI runners cluster."
860+
description = "CIDR of the CI runners cluster. Opened unconditionally on the EKS API (DND-1522). Required — the root module always supplies it; the default lives only there so the value can't drift between submodules."
881861
type = string
882-
default = "10.4.0.0/16"
883862
}
884863

885864
#####################

‎modules/comet_elasticache/main.tf‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,11 @@ resource "aws_vpc_security_group_ingress_rule" "redis_port_inbound_auto_mode" {
7979
referenced_security_group_id = var.elasticache_auto_mode_allow_from_sg
8080
description = "Redis from EKS Auto Mode nodes (cluster primary SG)"
8181
}
82-
# VPN ingress to Redis (DND-752) — gated by enable_vpn_redis_access. Allows
83-
# operators on the VPN to connect to Redis via kubectl port-forward through
84-
# the cluster's Redis SG.
85-
resource "aws_vpc_security_group_ingress_rule" "redis_vpn" {
86-
count = var.enable_vpn_redis_access ? 1 : 0
8782

83+
# VPN ingress to Redis (DND-752). Allows operators on the VPN to connect to
84+
# Redis via kubectl port-forward through the cluster's Redis SG. Opened
85+
# unconditionally — connectivity is never per-environment (DND-1522).
86+
resource "aws_vpc_security_group_ingress_rule" "redis_vpn" {
8887
security_group_id = aws_security_group.redis_inbound_sg.id
8988
description = "VPN client access (DND-752)"
9089
from_port = local.redis_port
@@ -94,3 +93,14 @@ resource "aws_vpc_security_group_ingress_rule" "redis_vpn" {
9493

9594
tags = merge(var.common_tags, { Name = "redis-vpn-access" })
9695
}
96+
97+
# The rule was previously gated on enable_vpn_redis_access, so envs that had it
98+
# on hold it in state as redis_vpn[0]. Dropping count changes the address to
99+
# redis_vpn; without this, Terraform plans an unordered destroy + create of two
100+
# unrelated addresses, which can race into InvalidPermission.Duplicate and, on
101+
# the happy path, still leaves a window with no VPN ingress to Redis. No-op for
102+
# any consumer that had the toggle off (DND-1522).
103+
moved {
104+
from = aws_vpc_security_group_ingress_rule.redis_vpn[0]
105+
to = aws_vpc_security_group_ingress_rule.redis_vpn
106+
}

‎modules/comet_elasticache/outputs.tf‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ output "redis_port" {
88
value = local.redis_port
99
}
1010

11+
output "redis_sg_id" {
12+
description = "Security group ID of the Redis replication group"
13+
value = aws_security_group.redis_inbound_sg.id
14+
}
15+
1116
output "transit_encryption_enabled" {
1217
description = "Whether transit encryption is enabled"
1318
value = aws_elasticache_replication_group.comet-ml-ec-redis.transit_encryption_enabled

‎modules/comet_elasticache/variables.tf‎

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,7 @@ variable "common_tags" {
9595
default = {}
9696
}
9797

98-
variable "enable_vpn_redis_access" {
99-
description = "Add a VPN client CIDR ingress rule on the Redis SG (port 6379). Required for operator port-forward access via the VPN (DND-752)."
100-
type = bool
101-
default = false
102-
}
103-
10498
variable "vpn_client_cidr" {
105-
description = "CIDR of the VPN client pool. Used when enable_vpn_redis_access = true."
99+
description = "CIDR of the VPN client pool. Opened unconditionally on the Redis SG (port 6379) for operator port-forward access via the VPN (DND-752, DND-1522). Required — the root module always supplies it; the default lives only there so the value can't drift between submodules."
106100
type = string
107-
default = "10.126.0.0/15"
108101
}

‎modules/comet_rds/main.tf‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,3 +352,18 @@ resource "aws_vpc_security_group_ingress_rule" "mysql_port_inbound_auto_mode" {
352352
referenced_security_group_id = var.rds_auto_mode_allow_from_sg
353353
description = "MySQL from EKS Auto Mode nodes (cluster primary SG)"
354354
}
355+
356+
# VPN ingress to MySQL. Allows operators on the VPN to connect to Aurora via
357+
# kubectl port-forward through the cluster's MySQL SG. Opened unconditionally,
358+
# mirroring redis_vpn in modules/comet_elasticache — connectivity is never
359+
# per-environment (DND-1522).
360+
resource "aws_vpc_security_group_ingress_rule" "mysql_vpn" {
361+
security_group_id = aws_security_group.mysql_sg.id
362+
description = "VPN client access (DND-1522)"
363+
from_port = local.mysql_port
364+
to_port = local.mysql_port
365+
ip_protocol = "tcp"
366+
cidr_ipv4 = var.vpn_client_cidr
367+
368+
tags = merge(var.common_tags, { Name = "mysql-vpn-access" })
369+
}

‎modules/comet_rds/variables.tf‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,11 @@ variable "common_tags" {
185185
default = {}
186186
}
187187

188+
variable "vpn_client_cidr" {
189+
description = "CIDR of the VPN client pool. Opened unconditionally on the MySQL SG (port 3306) for operator port-forward access via the VPN (DND-1522). Required — the root module always supplies it; the default lives only there so the value can't drift between submodules."
190+
type = string
191+
}
192+
188193
variable "rds_snapshot_identifier" {
189194
description = "Snapshot identifier to restore the RDS cluster from. If provided, the cluster will be restored from this snapshot instead of being created fresh."
190195
type = string

‎modules/comet_rds_proxy/variables.tf‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,8 @@ variable "allowed_sg_ids" {
2626
}
2727

2828
variable "allowed_cidrs" {
29-
description = "CIDR blocks allowed to connect to the proxy on the MySQL port (in addition to allowed_sg_ids). Defaults to the agentro VPN client pool so ops queries via VPN reach the proxy directly. Set to [] to allow only SG-based ingress."
29+
description = "CIDR blocks allowed to connect to the proxy on the MySQL port (in addition to allowed_sg_ids). [] allows only SG-based ingress. Required — the root module resolves this from rds_proxy_allowed_cidrs, falling back to vpn_client_cidr; the default lives only there so the two MySQL paths can't diverge."
3030
type = list(string)
31-
default = ["10.126.0.0/15"]
3231
}
3332

3433
variable "mysql_cluster_id" {

‎outputs.tf‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ output "mysql_database_name" {
7272
value = var.enable_rds ? module.comet_rds[0].mysql_database_name : null
7373
}
7474

75+
output "mysql_sg_id" {
76+
description = "Security group ID of the MySQL (Aurora) cluster — wrappers add ingress rules to this instead of looking it up by name (DND-1522)"
77+
value = var.enable_rds ? module.comet_rds[0].mysql_sg_id : null
78+
}
79+
7580
output "mysql_log_group_names" {
7681
description = "CloudWatch log group names for the RDS log groups this module MANAGES, keyed by log type. Superset of what is actively exported — cross-reference mysql_exported_log_types. Needed to build the terraform import address when adopting a cluster whose export was enabled out-of-band (DND-1537)."
7782
value = var.enable_rds ? module.comet_rds[0].mysql_log_group_names : null
@@ -92,6 +97,11 @@ output "rds_password_auto_generated" {
9297
value = var.enable_rds ? nonsensitive(var.rds_master_password == null) : null
9398
}
9499

100+
output "redis_sg_id" {
101+
description = "Security group ID of the Redis (ElastiCache) replication group — wrappers add ingress rules to this instead of looking it up by name (DND-1522)"
102+
value = var.enable_elasticache ? module.comet_elasticache[0].redis_sg_id : null
103+
}
104+
95105
output "configure_kubectl" {
96106
description = "Configure kubectl: run the following command to update your kubeconfig with the newly provisioned cluster."
97107
value = var.enable_eks ? "aws eks update-kubeconfig --region ${var.region} --name ${module.comet_eks[0].cluster_name}" : null

0 commit comments

Comments
 (0)