Skip to content
Merged
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 docs/platform-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ ALB, ACM wildcard certificate, Route53 DNS.
| HTTPS listener | Port 443, default action = fixed 404 |
| Route53 | Validation records for ACM |

The ALB drops request headers whose names contain characters other than letters, digits and `-` (`drop_invalid_header_fields`, Security Hub ELB.4). Headers with underscores, such as `X_Api_Key`, never reach the app. Clients of an app moving onto the platform ALB must send hyphenated header names.

**Inputs:** `public_subnet_ids`, `alb_sg_id` from networking; `extra_domains` (domain → zone ID) from dns
**Outputs:** `alb_arn`, `https_listener_arn`, `alb_dns_name`, `alb_zone_id`

Expand Down
38 changes: 38 additions & 0 deletions terraform/platform/cost-analytics/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,20 @@ resource "aws_s3_bucket_policy" "cur_data" {
}
}
},
{
# S3.5
Sid = "DenyInsecureTransport"
Effect = "Deny"
Principal = "*"
Action = "s3:*"
Resource = [
aws_s3_bucket.cur_data.arn,
"${aws_s3_bucket.cur_data.arn}/*",
]
Condition = {
Bool = { "aws:SecureTransport" = "false" }
}
},
]
})
}
Expand Down Expand Up @@ -129,6 +143,30 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "athena_results" {
}
}

# S3.5
resource "aws_s3_bucket_policy" "athena_results" {
bucket = aws_s3_bucket.athena_results.id

policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Sid = "DenyInsecureTransport"
Effect = "Deny"
Principal = "*"
Action = "s3:*"
Resource = [
aws_s3_bucket.athena_results.arn,
"${aws_s3_bucket.athena_results.arn}/*",
]
Condition = {
Bool = { "aws:SecureTransport" = "false" }
}
}]
})

depends_on = [aws_s3_bucket_public_access_block.athena_results]
}

resource "aws_s3_bucket_lifecycle_configuration" "athena_results" {
bucket = aws_s3_bucket.athena_results.id

Expand Down
6 changes: 4 additions & 2 deletions terraform/platform/identity/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
################################################################################

resource "aws_cognito_user_pool" "internal" {
name = "${var.project}-internal"
name = "${var.project}-internal"
deletion_protection = "ACTIVE" # Cognito.6

lambda_config {
pre_token_generation_config {
Expand Down Expand Up @@ -139,7 +140,8 @@ removed {
################################################################################

resource "aws_cognito_user_pool" "external" {
name = "${var.project}-external"
name = "${var.project}-external"
deletion_protection = "ACTIVE" # Cognito.6

auto_verified_attributes = ["email"]
username_attributes = ["email"]
Expand Down
2 changes: 2 additions & 0 deletions terraform/platform/ingress/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ resource "aws_lb" "main" {
security_groups = [var.alb_sg_id]
subnets = var.public_subnet_ids
enable_deletion_protection = true
# ELB.4: drop headers with invalid field names (request smuggling hygiene)
drop_invalid_header_fields = true

tags = {
Name = "${var.project}-platform-alb"
Expand Down
18 changes: 16 additions & 2 deletions terraform/platform/networking/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,29 @@ resource "aws_vpc" "main" {
}
}

# EC2.2: the VPC's default security group allows nothing. Every platform and
# app resource uses its own security group; nothing is attached to this one.
resource "aws_default_security_group" "main" {
vpc_id = aws_vpc.main.id

tags = {
Name = "${var.project}-default-deny"
team = "shared"
}
}

################################################################################
# Subnets
################################################################################

# EC2.15: public subnets do not auto-assign public IPs. Nothing relies on it:
# NAT has an EIP, the ALB manages its own, and ECS tasks run in private
# subnets without public IPs.
resource "aws_subnet" "public_a" {
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidrs[0]
availability_zone = data.aws_availability_zones.available.names[0]
map_public_ip_on_launch = true
map_public_ip_on_launch = false

tags = {
Name = "${var.project}-public-a"
Expand All @@ -42,7 +56,7 @@ resource "aws_subnet" "public_b" {
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidrs[1]
availability_zone = data.aws_availability_zones.available.names[1]
map_public_ip_on_launch = true
map_public_ip_on_launch = false

tags = {
Name = "${var.project}-public-b"
Expand Down
Loading