Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compute/ecs_cluster/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ module "api_service" {
| public_alb_ssl_policy | SSL policy for HTTPS | `string` | `"ELBSecurityPolicy-TLS13-1-2-2021-06"` | no |
| public_alb_idle_timeout | Idle timeout in seconds | `number` | `60` | no |
| public_alb_ingress_cidr_blocks | Allowed IPv4 CIDR blocks | `list(string)` | `["0.0.0.0/0"]` | no |
| public_alb_ingress_security_group_ids | Security group IDs allowed to access the public ALB | `list(string)` | `[]` | no |
| public_alb_access_logs_enabled | Enable access logs | `bool` | `false` | no |
| public_alb_access_logs_bucket_arn | S3 bucket ARN for access logs | `string` | `null` | no |
| public_alb_web_acl_arn | WAFv2 Web ACL ARN | `string` | `null` | no |
Expand All @@ -277,6 +278,7 @@ module "api_service" {
| private_alb_ssl_policy | SSL policy for HTTPS | `string` | `"ELBSecurityPolicy-TLS13-1-2-2021-06"` | no |
| private_alb_idle_timeout | Idle timeout in seconds | `number` | `60` | no |
| private_alb_ingress_cidr_blocks | Allowed IPv4 CIDR blocks | `list(string)` | `["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]` | no |
| private_alb_ingress_security_group_ids | Security group IDs allowed to access the private ALB (e.g., CloudFront VPC origins) | `list(string)` | `[]` | no |
| private_alb_access_logs_enabled | Enable access logs | `bool` | `false` | no |
| private_alb_access_logs_bucket_arn | S3 bucket ARN for access logs | `string` | `null` | no |

Expand Down
10 changes: 6 additions & 4 deletions compute/ecs_cluster/load_balancers.tf
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ module "public_alb" {
deletion_protection_enabled = var.load_balancer_deletion_protection_enabled

# Security
ingress_cidr_blocks = var.public_alb_ingress_cidr_blocks
ingress_ipv6_cidr_blocks = var.public_alb_ingress_ipv6_cidr_blocks
ingress_cidr_blocks = var.public_alb_ingress_cidr_blocks
ingress_ipv6_cidr_blocks = var.public_alb_ingress_ipv6_cidr_blocks
ingress_security_group_ids = var.public_alb_ingress_security_group_ids

# Access logs
access_logs_enabled = var.public_alb_access_logs_enabled
Expand Down Expand Up @@ -69,8 +70,9 @@ module "private_alb" {
deletion_protection_enabled = var.load_balancer_deletion_protection_enabled

# Security
ingress_cidr_blocks = var.private_alb_ingress_cidr_blocks
ingress_ipv6_cidr_blocks = var.private_alb_ingress_ipv6_cidr_blocks
ingress_cidr_blocks = var.private_alb_ingress_cidr_blocks
ingress_ipv6_cidr_blocks = var.private_alb_ingress_ipv6_cidr_blocks
ingress_security_group_ids = var.private_alb_ingress_security_group_ids

# Access logs
access_logs_enabled = var.private_alb_access_logs_enabled
Expand Down
14 changes: 12 additions & 2 deletions compute/ecs_cluster/rvn-ecs-cluster-definition.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ definition:
name: ECS Cluster
description: Production-ready AWS ECS cluster with Fargate, Fargate Spot, optional EC2 capacity, and shared load balancers.
release:
version: 0.5.0
description: "BREAKING: Rename ec2_managed_termination_protection to ec2_managed_termination_protection_enabled and ec2_managed_scaling_status to ec2_managed_scaling_enabled; both inputs now accept booleans instead of ENABLED or DISABLED strings."
version: 0.5.2
description: "Add private_alb_ingress_security_group_ids input (and public_alb_ingress_security_group_ids Terraform variable) to allow ALB ingress rules that reference source security groups, such as the CloudFront VPC origins security group."
module:
inputs:
- id: network
Expand Down Expand Up @@ -342,6 +342,15 @@ module:
show_when:
private_alb_enabled: true
type: string_array
- add_button_label: Add security group
collapsible: true
description: Security groups whose members can access the private ALB. Useful for sources without static CIDRs, such as CloudFront VPC origins.
id: private_alb_ingress_security_group_ids
label: Ingress security groups
placeholder: sg-...
show_when:
private_alb_enabled: true
type: string_array
- collapsible: true
description: Idle timeout for the private ALB.
id: private_alb_idle_timeout
Expand Down Expand Up @@ -671,6 +680,7 @@ module:
private_alb_idle_timeout: << module.input.private_alb_idle_timeout >>
private_alb_ingress_cidr_blocks: << module.input.private_alb_ingress_cidr_blocks >>
private_alb_ingress_ipv6_cidr_blocks: << module.input.private_alb_ingress_ipv6_cidr_blocks >>
private_alb_ingress_security_group_ids: << module.input.private_alb_ingress_security_group_ids >>
private_alb_ssl_policy: << module.input.private_alb_ssl_policy >>
private_nlb_access_logs_bucket_arn: << module.input.private_nlb_access_logs_bucket_arn >>
private_nlb_elastic_ip_allocation_ids: << module.input.private_nlb_elastic_ip_allocation_ids >>
Expand Down
41 changes: 41 additions & 0 deletions compute/ecs_cluster/tests/basic.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,47 @@ run "private_alb_custom_settings" {
}
}

# Test 28b: Private ALB ingress rules referencing source security groups
run "private_alb_ingress_security_groups" {
command = plan

variables {
private_alb_enabled = true
private_alb_https_enabled = true
private_alb_certificate_arns = ["arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"]
private_alb_ingress_security_group_ids = ["sg-0123456789abcdef0"]
}

assert {
condition = length([
for rule in module.private_alb[0].module.security_group.aws_vpc_security_group_ingress_rule.this : rule
if rule.referenced_security_group_id == "sg-0123456789abcdef0"
]) == 2
error_message = "Private ALB should have HTTP and HTTPS ingress rules referencing the source security group"
}
}

# Test 28c: Public ALB ingress rules referencing source security groups
run "public_alb_ingress_security_groups" {
command = plan

variables {
public_alb_enabled = true
public_alb_https_enabled = true
public_alb_certificate_arns = ["arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"]
public_subnet_ids = ["subnet-public1", "subnet-public2"]
public_alb_ingress_security_group_ids = ["sg-0123456789abcdef0"]
}

assert {
condition = length([
for rule in module.public_alb[0].module.security_group.aws_vpc_security_group_ingress_rule.this : rule
if rule.referenced_security_group_id == "sg-0123456789abcdef0"
]) == 2
error_message = "Public ALB should have HTTP and HTTPS ingress rules referencing the source security group"
}
}

################################################################################
# Combined Configuration Tests
################################################################################
Expand Down
22 changes: 22 additions & 0 deletions compute/ecs_cluster/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,17 @@ variable "public_alb_ingress_ipv6_cidr_blocks" {
default = ["::/0"]
}

variable "public_alb_ingress_security_group_ids" {
type = list(string)
description = "Security group IDs whose members are allowed to access the public ALB."
default = []

validation {
condition = alltrue([for sg in var.public_alb_ingress_security_group_ids : can(regex("^sg-", sg))])
error_message = "All public_alb_ingress_security_group_ids must be valid security group IDs starting with 'sg-'."
}
}

variable "public_alb_access_logs_enabled" {
type = bool
description = "Enable access logging for the public ALB."
Expand Down Expand Up @@ -542,6 +553,17 @@ variable "private_alb_ingress_ipv6_cidr_blocks" {
default = []
}

variable "private_alb_ingress_security_group_ids" {
type = list(string)
description = "Security group IDs whose members are allowed to access the private ALB. Useful for sources without static CIDRs, such as CloudFront VPC origins."
default = []

validation {
condition = alltrue([for sg in var.private_alb_ingress_security_group_ids : can(regex("^sg-", sg))])
error_message = "All private_alb_ingress_security_group_ids must be valid security group IDs starting with 'sg-'."
}
}

variable "private_alb_access_logs_enabled" {
type = bool
description = "Enable access logging for the private ALB."
Expand Down
1 change: 1 addition & 0 deletions networking/alb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ spec:
|------|-------------|------|---------|----------|
| ingress_cidr_blocks | IPv4 CIDR blocks allowed to access the ALB | `list(string)` | `["0.0.0.0/0"]` | no |
| ingress_ipv6_cidr_blocks | IPv6 CIDR blocks allowed to access the ALB | `list(string)` | `["::/0"]` | no |
| ingress_security_group_ids | Security group IDs whose members are allowed to access the ALB | `list(string)` | `[]` | no |

### Access Logs

Expand Down
20 changes: 20 additions & 0 deletions networking/alb/security_group.tf
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,26 @@ module "security_group" {
ip_protocol = "tcp"
cidr_ipv6 = cidr
}
] : [],
# HTTP ingress (security groups)
local.create_http_listener ? [
for sg in var.ingress_security_group_ids : {
description = "Allow HTTP traffic from security group ${sg}"
from_port = var.http_listener_port
to_port = var.http_listener_port
ip_protocol = "tcp"
referenced_security_group_id = sg
}
] : [],
# HTTPS ingress (security groups)
local.create_https_listener ? [
for sg in var.ingress_security_group_ids : {
description = "Allow HTTPS traffic from security group ${sg}"
from_port = var.https_listener_port
to_port = var.https_listener_port
ip_protocol = "tcp"
referenced_security_group_id = sg
}
] : []
)
}
40 changes: 40 additions & 0 deletions networking/alb/tests/basic.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -561,3 +561,43 @@ run "waf_fail_open" {
error_message = "ALB should have WAF fail open enabled"
}
}

# Test 29: Ingress rules referencing source security groups
run "ingress_security_groups" {
command = plan

variables {
https_listener_enabled = true
certificate_arns = ["arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"]
ingress_security_group_ids = ["sg-0123456789abcdef0"]
}

assert {
condition = length([
for rule in module.security_group.aws_vpc_security_group_ingress_rule.this : rule
if rule.referenced_security_group_id == "sg-0123456789abcdef0"
]) == 2
error_message = "Should create HTTP and HTTPS ingress rules referencing the source security group"
}

assert {
condition = length([
for rule in module.security_group.aws_vpc_security_group_ingress_rule.this : rule
if rule.referenced_security_group_id == "sg-0123456789abcdef0" && rule.from_port == 443
]) == 1
error_message = "Should create an HTTPS ingress rule referencing the source security group"
}
}

# Test 30: No security group referenced ingress rules by default
run "no_ingress_security_groups_by_default" {
command = plan

assert {
condition = length([
for rule in module.security_group.aws_vpc_security_group_ingress_rule.this : rule
if rule.referenced_security_group_id != null
]) == 0
error_message = "Should not create security group referenced ingress rules by default"
}
}
26 changes: 26 additions & 0 deletions networking/alb/tests/validation.tftest.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# ALB variable validation tests

mock_provider "aws" {
override_resource {
target = aws_lb.this
values = {
arn = "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/test-alb/1234567890123456"
}
}
}

variables {
name = "test-alb"
vpc_id = "vpc-12345678"
subnet_ids = ["subnet-12345678", "subnet-87654321"]
}

run "duplicate_ingress_security_groups" {
command = plan

variables {
ingress_security_group_ids = ["sg-0123456789abcdef0", "sg-0123456789abcdef0"]
}

expect_failures = [var.ingress_security_group_ids]
}
11 changes: 11 additions & 0 deletions networking/alb/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,17 @@ variable "ingress_ipv6_cidr_blocks" {
default = null
}

variable "ingress_security_group_ids" {
type = list(string)
description = "A list of security group IDs whose members are allowed to access the ALB. Ingress rules are created on each enabled listener port."
default = []

validation {
condition = length(distinct(var.ingress_security_group_ids)) == length(var.ingress_security_group_ids) && alltrue([for sg in var.ingress_security_group_ids : can(regex("^sg-", sg))])
error_message = "All ingress_security_group_ids must be unique, valid security group IDs starting with 'sg-'."
}
}

################################################################################
# Access Logs
################################################################################
Expand Down
Loading