-
Notifications
You must be signed in to change notification settings - Fork 0
security architecture
Network isolation, encryption, security groups, and TLS across all modules.
Public/private subnet pattern:
flowchart TD
Internet --> IGW[Internet Gateway]
IGW --> Public[Public Subnets]
Public --- ALB[ALBs\nBRMS + Agent]
ALB --> Private[Private Subnets]
Public --> NAT[NAT Gateway] --> Internet2[Internet]
Private --- ECS[ECS Tasks\nBRMS + Agent]
Private --- Aurora[Aurora Cluster]
Private --- Lambda[Lambda\nIAM auth]
-
Public subnets: host the internet-facing ALBs and the NAT gateway. Optional. They are not created when every ALB is internal (
alb_internal = true) andnat_gateway_mode = none. - Private subnets: all compute (ECS tasks, Lambda) and data (Aurora). Internal ALBs sit here too.
-
NAT Gateway: internet egress for private resources. Skipped when
nat_gateway_mode = none. -
VPC Endpoints: reach AWS without NAT. Optional. Created automatically when
nat_gateway_mode = none. See VPC Module
The diagram above shows the default topology. With internal ALBs and nat_gateway_mode = none, the public subnets, internet gateway and NAT gateway are not created, and ingress comes through the private subnets instead.
Four layers of security groups, each allowing only necessary traffic:
| Service | Ingress | Egress |
|---|---|---|
| BRMS ALB | HTTP/HTTPS from allowed_cidr_blocks
|
Container port to BRMS Task SG |
| Agent ALB | HTTP/HTTPS from allowed_cidr_blocks
|
Container port to Agent Task SG |
| Service | Ingress | Egress |
|---|---|---|
| BRMS Tasks | Container port from BRMS ALB SG | 0.0.0.0/0 (all) |
| Agent Tasks | Container port from Agent ALB SG | 0.0.0.0/0 (all) |
| Ingress | Source |
|---|---|
| Port 5432 | BRMS Task SG (via Root Module cross-module rule) |
| Port 5432 | Lambda SG (if IAM auth enabled) |
| Ingress | Source |
|---|---|
| Port 443 (HTTPS) | VPC CIDR block |
The Root Module creates the critical SG rule connecting BRMS to the database:
resource "aws_security_group_rule" "database_from_brms" {
count = local.create_brms && local.create_database ? 1 : 0
type = "ingress"
from_port = 5432
to_port = 5432
protocol = "tcp"
source_security_group_id = module.ecs[0].brms_tasks_security_group_id
security_group_id = module.database[0].security_group_id
}This pattern uses source_security_group_id (not CIDRs) for internal traffic.
| Connection | Encryption | Mechanism |
|---|---|---|
| Client → ALB | TLS 1.3 | ALB HTTPS listener (ELBSecurityPolicy-TLS13-1-2-2021-06) |
| ALB → ECS Tasks | HTTP | Within VPC, SG-restricted |
| BRMS → Aurora | SSL |
rds.force_ssl = 1 parameter, RDS CA bundle |
| ECS → S3 | HTTPS | AWS SDK default. A bucket policy denying insecure transport (aws:SecureTransport=false) is added only when cross_account_write_principals is set |
| ECS → Secrets Manager | HTTPS | AWS API default |
BRMS requires HTTPS because it uses Web Crypto and Service Workers that only work in secure contexts. Enforced at variable validation level: you must provide route53_zone_id or certificate_arn, unless you set alb_http_only = true. In that mode the ALB serves plain HTTP and a trusted edge such as CloudFront terminates TLS upstream. alb_http_only also requires alb_internal = true. See Certificates and DNS.
The ECS Module fetches the RDS CA bundle and passes it as DB_SSL_CA (base64 encoded) to the BRMS container. If the user sets ssl_verify = false, it passes DB_REJECT_UNAUTHORIZED=false instead.
| Resource | Encryption |
|---|---|
| Aurora cluster | Storage encrypted (AWS-managed key) |
| S3 bucket | AES256 server-side encryption + bucket key |
| Secrets Manager | AWS-managed or customer KMS key |
| CloudWatch Logs | AWS-managed encryption |
| KMS key (optional) | For BRMS secrets encryption provider |
All secrets managed via AWS Secrets Manager, never in task definitions or Terraform state:
- Database master password
- S3 access keys (secrets auth mode)
- BRMS license key
- Cookie secret (auto-generated)
- Secrets master key (auto-generated)
- AI API keys
Separate task roles per service with minimal permissions:
- BRMS: S3 read-write + database access + optional KMS/Bedrock
- Agent: S3 read-only only
- ECS task egress is 0.0.0.0/0, wide open outbound. Can be tightened with VPC endpoints
-
prevent_destroy = falseon KMS keys and secrets master keys despite "DO NOT DELETE" comments - RDS CA bundle HTTP fetch is unconditional (potential supply chain consideration)
-
Provider version constraints are floor-only (
>= 6.0), with no upper-bound protection