-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Issue 963] Moves ECS services into private subnets #1014
Changes from all commits
766db2d
563b26d
325b8e6
43babf2
43fde08
175950c
d8dabe7
c92157a
046898c
3d9074d
62ac8c7
d460cce
595df4f
e730721
4434228
530f1bd
f0ae706
e3998c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
module "staging_config" { | ||
source = "./env-config" | ||
app_name = local.app_name | ||
default_region = module.project_config.default_region | ||
environment = "staging" | ||
has_database = local.has_database | ||
has_incident_management_service = local.has_incident_management_service | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
bucket = "simpler-grants-gov-315341936575-us-east-1-tf" | ||
key = "infra/api/database/staging.tfstate" | ||
dynamodb_table = "simpler-grants-gov-315341936575-us-east-1-tf-state-locks" | ||
region = "us-east-1" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
bucket = "simpler-grants-gov-315341936575-us-east-1-tf" | ||
key = "infra/api/service/staging.tfstate" | ||
dynamodb_table = "simpler-grants-gov-315341936575-us-east-1-tf-state-locks" | ||
region = "us-east-1" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module "staging_config" { | ||
source = "./env-config" | ||
app_name = local.app_name | ||
default_region = module.project_config.default_region | ||
environment = "staging" | ||
has_database = local.has_database | ||
has_incident_management_service = local.has_incident_management_service | ||
domain = "beta.grants.gov" | ||
sendy_api_key = "/${local.app_name}/staging/sendy-api-key" | ||
sendy_api_url = "/${local.app_name}/staging/sendy-api-url" | ||
sendy_list_id = "/${local.app_name}/staging/sendy-list-id" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
bucket = "simpler-grants-gov-315341936575-us-east-1-tf" | ||
key = "infra/frontend/service/staging.tfstate" | ||
dynamodb_table = "simpler-grants-gov-315341936575-us-east-1-tf-state-locks" | ||
region = "us-east-1" |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,9 +2,14 @@ | |
import itertools | ||
from operator import itemgetter | ||
import os | ||
import json | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
import logging | ||
from pg8000.native import Connection, identifier | ||
|
||
logging.basicConfig() | ||
logging.getLogger('botocore').setLevel(logging.DEBUG) | ||
logging.getLogger('boto3').setLevel(logging.DEBUG) | ||
|
||
logger = logging.getLogger() | ||
logger.setLevel(logging.INFO) | ||
|
||
|
@@ -115,9 +120,9 @@ def connect_using_iam(user: str) -> Connection: | |
return Connection(user=user, host=host, port=port, database=database, password=token, ssl_context=True) | ||
|
||
def get_password() -> str: | ||
ssm = boto3.client("ssm") | ||
ssm = boto3.client("ssm",region_name=os.environ["AWS_REGION"]) | ||
param_name = os.environ["DB_PASSWORD_PARAM_NAME"] | ||
logger.info("Fetching password from parameter store") | ||
logger.info("Fetching password from parameter store:\n%s"%param_name) | ||
result = json.loads(ssm.get_parameter( | ||
Name=param_name, | ||
WithDecryption=True, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,7 +28,7 @@ locals { | |
{ name : "DB_NAME", value : var.db_vars.connection_info.db_name }, | ||
{ name : "DB_SCHEMA", value : var.db_vars.connection_info.schema_name }, | ||
] | ||
environment_variables = concat(local.base_environment_variables, local.db_environment_variables) | ||
environment_variables = concat(local.base_environment_variables, local.db_environment_variables, var.extra_environment_variables) | ||
} | ||
|
||
#------------------- | ||
|
@@ -49,10 +49,8 @@ resource "aws_ecs_service" "app" { | |
} | ||
|
||
network_configuration { | ||
# TODO(https://github.com/navapbc/template-infra/issues/152) set assign_public_ip = false after using private subnets | ||
# checkov:skip=CKV_AWS_333:Switch to using private subnets | ||
assign_public_ip = true | ||
subnets = var.subnet_ids | ||
assign_public_ip = false | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line is the whole reason I'm here |
||
subnets = var.private_subnet_ids | ||
security_groups = [aws_security_group.app.id] | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -70,9 +70,20 @@ variable "vpc_id" { | |
description = "Uniquely identifies the VPC." | ||
} | ||
|
||
variable "subnet_ids" { | ||
variable "public_subnet_ids" { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A lot of these changes were all pulled from the Nava template. Here's the diff between our version and main: |
||
type = list(any) | ||
description = "Private subnet id from vpc module" | ||
description = "Public subnet ids in VPC" | ||
} | ||
|
||
variable "private_subnet_ids" { | ||
type = list(any) | ||
description = "Private subnet ids in VPC" | ||
} | ||
|
||
variable "extra_environment_variables" { | ||
type = list(object({ name = string, value = string })) | ||
description = "Additional environment variables to pass to the service container" | ||
default = [] | ||
} | ||
|
||
variable "db_vars" { | ||
|
@@ -92,6 +103,12 @@ variable "db_vars" { | |
default = null | ||
} | ||
|
||
variable "extra_policies" { | ||
description = "Map of extra IAM policies to attach to the service's task role. The map's keys define the resource name in terraform." | ||
type = map(string) | ||
default = {} | ||
} | ||
|
||
variable "cert_arn" { | ||
description = "The ARN for the TLS certificate passed in from the app service layer" | ||
type = string | ||
|
@@ -120,4 +137,4 @@ variable "api_auth_token" { | |
type = string | ||
default = null | ||
description = "Auth token for connecting to the API" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,9 +17,17 @@ locals { | |
# see https://docs.aws.amazon.com/vpc/latest/privatelink/aws-services-privatelink-support.html | ||
# | ||
# The database module requires VPC access from private networks to SSM, KMS, and RDS | ||
aws_service_integrations = toset( | ||
module.app_config.has_database ? ["ssm", "kms"] : [] | ||
aws_service_integrations = setunion( | ||
# AWS services used by ECS Fargate: ECR to fetch images, S3 for image layers, and CloudWatch for logs | ||
["ecr.api", "ecr.dkr", "s3", "logs"], | ||
|
||
# AWS services used by the database's role manager | ||
var.has_database ? ["ssm", "kms", "secretsmanager"] : [], | ||
) | ||
|
||
# S3 and DynamoDB use Gateway VPC endpoints. All other services use Interface VPC endpoints | ||
interface_vpc_endpoints = toset([for aws_service in local.aws_service_integrations : aws_service if !contains(["s3", "dynamodb"], aws_service)]) | ||
gateway_vpc_endpoints = toset([for aws_service in local.aws_service_integrations : aws_service if contains(["s3", "dynamodb"], aws_service)]) | ||
Comment on lines
+28
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This pattern was pulled from the Nava template: https://github.com/navapbc/template-infra/ |
||
} | ||
|
||
terraform { | ||
|
@@ -75,6 +83,7 @@ data "aws_subnets" "default" { | |
# See https://repost.aws/knowledge-center/lambda-vpc-parameter-store | ||
# See https://docs.aws.amazon.com/vpc/latest/privatelink/create-interface-endpoint.html#create-interface-endpoint | ||
|
||
# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group | ||
resource "aws_security_group" "aws_services" { | ||
count = length(local.aws_service_integrations) > 0 ? 1 : 0 | ||
|
||
|
@@ -83,17 +92,40 @@ resource "aws_security_group" "aws_services" { | |
vpc_id = data.aws_vpc.default.id | ||
} | ||
|
||
# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule | ||
resource "aws_vpc_security_group_ingress_rule" "aws_services" { | ||
count = length(local.aws_service_integrations) > 0 ? 1 : 0 | ||
|
||
security_group_id = aws_security_group.aws_services[0].id | ||
description = "Allow all traffic from the VPCs CIDR block to the VPC endpoint security group" | ||
from_port = 443 | ||
to_port = 443 | ||
ip_protocol = "tcp" | ||
cidr_ipv4 = data.aws_vpc.default.cidr_block | ||
} | ||
|
||
# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_endpoint | ||
resource "aws_vpc_endpoint" "aws_service" { | ||
for_each = local.aws_service_integrations | ||
|
||
vpc_id = data.aws_vpc.default.id | ||
service_name = "com.amazonaws.${data.aws_region.current.name}.${each.key}" | ||
vpc_endpoint_type = "Interface" | ||
security_group_ids = [aws_security_group.aws_services[0].id] | ||
subnet_ids = data.aws_subnets.default.ids | ||
subnet_ids = [for subnet in aws_subnet.backfill_private : subnet.id] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See: https://betagrantsgov.slack.com/archives/C05TSL64VUH/p1705438485101009 I needed to deploy the VPC endpoints exclusively into the private subnets |
||
private_dns_enabled = true | ||
} | ||
|
||
# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_endpoint | ||
resource "aws_vpc_endpoint" "gateway" { | ||
for_each = local.gateway_vpc_endpoints | ||
|
||
vpc_id = data.aws_vpc.default.id | ||
service_name = "com.amazonaws.${data.aws_region.current.name}.${each.key}" | ||
vpc_endpoint_type = "Gateway" | ||
route_table_ids = [for table in aws_route_table.backfill_private : table.id] | ||
} | ||
|
||
# VPC Configuration for DMS | ||
# ---------------------------------------- | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes from deploying in every subnet, to only deploying into the private subnets