Skip to content

Commit

Permalink
Add secret manager vpc endpoint (#469)
Browse files Browse the repository at this point in the history
## Ticket

No ticket, hotfix

## Changes

Taken from platform-test pr
Add commented out option for using secret manager
Includes json handling fix

## Testing

Testing in [platform
test](navapbc/platform-test#63)
  • Loading branch information
charles-nava authored Nov 9, 2023
1 parent c3aa454 commit 6b3588c
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 17 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@
# Ignore local environment variables which can contain environment secrets
.env
.envrc

# Python testing stuff
*__pycache__*
4 changes: 2 additions & 2 deletions infra/modules/database/backups.tf
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ data "aws_kms_key" "backup_vault_key" {
# See https://docs.aws.amazon.com/aws-backup/latest/devguide/assigning-resources.html
# and https://docs.aws.amazon.com/aws-backup/latest/devguide/API_BackupSelection.html
resource "aws_backup_selection" "db_backup" {
name = "${var.name}-db-backup"
name = "${local.name}-db-backup"
plan_id = aws_backup_plan.backup_plan.id
iam_role_arn = aws_iam_role.db_backup_role.arn

Expand All @@ -41,7 +41,7 @@ resource "aws_backup_selection" "db_backup" {

# Role that AWS Backup uses to authenticate when backing up the target resource
resource "aws_iam_role" "db_backup_role" {
name_prefix = "${var.name}-db-backup-role-"
name_prefix = "${local.name}-db-backup-role-"
assume_role_policy = data.aws_iam_policy_document.db_backup_policy.json
}

Expand Down
2 changes: 1 addition & 1 deletion infra/modules/database/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ locals {
primary_instance_name = "${var.name}-primary"
role_manager_name = "${var.name}-role-manager"
role_manager_package = "${path.root}/role_manager.zip"

name = substr(var.name, 0, 12)
# The ARN that represents the users accessing the database are of the format: "arn:aws:rds-db:<region>:<account-id>:dbuser:<resource-id>/<database-user-name>""
# See https://aws.amazon.com/blogs/database/using-iam-authentication-to-connect-with-pgadmin-amazon-aurora-postgresql-or-amazon-rds-for-postgresql/
db_user_arn_prefix = "arn:aws:rds-db:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:dbuser:${aws_rds_cluster.db.cluster_resource_id}"
Expand Down
2 changes: 1 addition & 1 deletion infra/modules/database/monitoring.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#----------------------------------#

resource "aws_iam_role" "rds_enhanced_monitoring" {
name_prefix = "${var.name}-enhanced-monitoring-"
name_prefix = "${local.name}-enhanced-monitoring-"
assume_role_policy = data.aws_iam_policy_document.rds_enhanced_monitoring.json
}

Expand Down
45 changes: 36 additions & 9 deletions infra/modules/database/role-manager.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
# This includes creating and granting permissions to roles
# as well as viewing existing roles

locals {
ssm_password_name = length(aws_rds_cluster.db.master_user_secret) == 1 ? "/aws/reference/secretsmanager/${data.aws_secretsmanager_secret.db_pass[0].name}" : ""
}

resource "aws_lambda_function" "role_manager" {
function_name = local.role_manager_name

Expand All @@ -29,7 +33,8 @@ resource "aws_lambda_function" "role_manager" {
DB_PORT = aws_rds_cluster.db.port
DB_USER = local.master_username
DB_NAME = aws_rds_cluster.db.database_name
DB_PASSWORD_PARAM_NAME = "/aws/reference/secretsmanager/${data.aws_secretsmanager_secret.db_pass.name}"
DB_PASSWORD_PARAM_NAME = local.ssm_password_name
DB_PASSWORD_SECRET_ARN = aws_rds_cluster.db.master_user_secret[0].secret_arn
DB_SCHEMA = var.schema_name
APP_USER = var.app_username
MIGRATOR_USER = var.migrator_username
Expand All @@ -42,7 +47,7 @@ resource "aws_lambda_function" "role_manager" {
tracing_config {
mode = "Active"
}

timeout = 30
# checkov:skip=CKV_AWS_272:TODO(https://github.com/navapbc/template-infra/issues/283)

# checkov:skip=CKV_AWS_116:Dead letter queue (DLQ) configuration is only relevant for asynchronous invocations
Expand Down Expand Up @@ -79,6 +84,11 @@ resource "aws_kms_key" "role_manager" {
enable_key_rotation = true
}

data "aws_secretsmanager_secret" "db_pass" {
count = length(aws_rds_cluster.db.master_user_secret)
arn = aws_rds_cluster.db.master_user_secret[0].secret_arn
}

# IAM for Role Manager lambda function
resource "aws_iam_role" "role_manager" {
name = "${var.name}-manager"
Expand All @@ -94,26 +104,43 @@ resource "aws_iam_role" "role_manager" {
]
}

data "aws_secretsmanager_secret" "db_pass" {
arn = aws_rds_cluster.db.master_user_secret[0].secret_arn
}


resource "aws_iam_role_policy" "ssm_access" {
name = "${var.name}-role-manager-ssm-access"
role = aws_iam_role.role_manager.id

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["kms:Decrypt"]
Resource = [data.aws_kms_key.default_ssm_key.arn]
}
]
})
}

resource "aws_iam_role_policy" "database_credential_tool" {
count = length(aws_rds_cluster.db.master_user_secret)
name = "${var.name}-role-manager-rds-ssm-access"
role = aws_iam_role.role_manager.id

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["secretsmanager:GetSecretValue"]
Resource = [data.aws_secretsmanager_secret.db_pass.arn]
Resource = [data.aws_secretsmanager_secret.db_pass[0].arn]
},
{
Effect = "Allow"
Action = ["kms:Decrypt"]
Resource = [data.aws_kms_key.default_ssm_key.arn]
Effect = "Allow"
Action = ["ssm:GetParameter"]
Resource = [
"arn:aws:ssm:${data.aws_region.current.name}:${data.aws_caller_identity.current.id}:parameter${local.ssm_password_name}"
]
}
]
})
Expand Down
2 changes: 1 addition & 1 deletion infra/modules/database/role_manager/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pg8000
pg8000
9 changes: 7 additions & 2 deletions infra/modules/database/role_manager/role_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@
import itertools
from operator import itemgetter
import os
import json
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)

Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion infra/networks/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ locals {
#
# 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"] : []
module.app_config.has_database ? ["ssm", "kms", "secretsmanager"] : []
)
}

Expand Down

0 comments on commit 6b3588c

Please sign in to comment.