はじめに
検索してもあまり良い記事を見つけることができなかったため、自分で色々試して成功したことを記載します。
VPC、サブネット、RDS
VPC、サブネット、RDSは検索すればたくさん出てくるので、既に作成済みを前提として記載します。
VPCのCIDRは**"10.0.0.0/16"としております。
今回RDSはAurora PostgreSQLを使用し、プライベートサブネットに配置します。
RDSのセキュリティグループはPostgreSQLのポート(5432)に同VPC内(10.0.0.0/16)からのアクセスのみ許可**しております。
パスワードローテーションの仕組み
AWS Secrets Managerに認証情報を保持し、Lambdaを使って定期的にパスワードを更新します。
https://docs.aws.amazon.com/ja_jp/secretsmanager/latest/userguide/enable-rotation-rds.html
コンソールだと上記ドキュメント記載通りにすれば簡単に設定可能なのですが、これをTerraformでコーディングするのはどうすれば良いかを試行錯誤しました。
ローテーションのタイプ
ローテーションのタイプは2つある。
-
シングルユーザー
パスワード変更可能なユーザーの認証情報をローテーションする場合。 -
マルチユーザー
パスワード変更ができないユーザーの認証情報をローテーションする場合。別途、パスワード変更可能なユーザーの認証情報を保持しているシークレットが必要になる。
今回はシングルユーザーを前提とします
ローテーションのプロセス
ローテーションのプロセスは簡単に書くと以下のようです。
- Secrets Managerからローテーションを行うLambda関数を呼び出す
- Lambda関数がSecrets Managerにある認証情報をコピーして新しい認証情報を生成し、RDSのパスワード更新を行う
- Lambda関数がSecrets Managerの認証情報を新しい認証情報に差し替え
詳しくは以下に記載されています。
https://docs.aws.amazon.com/ja_jp/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html
コーディング方法
それでは本題のコーディング方法を記載します。
VPCやサブネットは作成済みを前提としているため、コードに出てくるaws_vpc.main.id
やaws_subnet.rds_subnet.*.id
は既に作成済み前提のVPCやサブネットのIDとなります。
実際にコーディングする場合はCIDRの値("10.0.0.0/16")も含めてご自身の環境に合った値に変更してお試しください。
こちら記載するために実際には変数になっている部分を改変などしているため、変なところがあればご指摘していただけますと嬉しいです。
セキュリティグループ
まず、セキュリティグループを作成します。
Secrets Manager用エンドポイントのセキュリティグループとローテーション用Lambdaのセキュリティグループが必要になります。
LambdaはSecrets Manager間の相互の通信が必要なのと、RDSへのアクセスが必要になりますので、以下のように設定します。
# エンドポイント用セキュリティグループ
resource "aws_security_group" "endpoint_security_group" {
name = "endpoint_security_group"
vpc_id = aws_vpc.main.id
egress {
cidr_blocks = ["0.0.0.0/0"]
from_port = 0
to_port = 0
protocol = "-1"
self = false
}
ingress {
cidr_blocks = ["0.0.0.0/0"]
from_port = 443
to_port = 443
protocol = "tcp"
self = false
}
tags = {
Name = "endpoint_security_group"
}
}
# ローテーションLambda用セキュリティグループ
resource "aws_security_group" "lambda_security_group" {
name = "rds-rotate-lambda"
vpc_id = aws_vpc.main.id
tags = {
Name = "rds-rotate-lambda"
}
}
# Secrets Manager用アウトバウンド
resource "aws_security_group_rule" "lambda_security_group_rule1" {
type = "egress"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["10.0.0.0/16"]
security_group_id = aws_security_group.lambda_security_group.id
}
# RDS用アウトバウンド
resource "aws_security_group_rule" "lambda_security_group_rule2" {
type = "egress"
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = ["10.0.0.0/16"]
security_group_id = aws_security_group.lambda_security_group.id
}
# Secrets Manager用インバウンド
resource "aws_security_group_rule" "lambda_security_group_rule3" {
type = "ingress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["10.0.0.0/16"]
security_group_id = aws_security_group.lambda_security_group.id
}
LambdaはSecrets Manager用のインバウンドとアウトバウンドとRDS用のアウトバウンドルールを設定しています。
エンドポイント
Secrets ManagerはVPC外にあるため、エンドポイントが必要になります。
data "aws_region" "current" {}
resource "aws_vpc_endpoint" "secretsmanager" {
service_name = "com.amazonaws.${data.aws_region.current.name}.secretsmanager"
security_group_ids = [aws_security_group.endpoint_security_group.id]
policy = jsonencode(
{
Statement = [
{
Action = "*"
Effect = "Allow"
Principal = "*"
Resource = "*"
},
]
}
)
private_dns_enabled = true
route_table_ids = []
subnet_ids = aws_subnet.rds_subnet.*.id
vpc_endpoint_type = "Interface"
vpc_id = aws_vpc.main.id
tags = {
Name = "secrets-manager"
}
}
Lambda
LambdaはSAP(AWS Serverless Application Repository)で公開されているSAM(AWS Serverless Application Model)を使用します。
https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:297356227824:applications~SecretsManagerRDSPostgreSQLRotationSingleUser
data "aws_partition" "current" {}
resource "aws_serverlessapplicationrepository_cloudformation_stack" "postgres_rotator" {
name = "postgres-rotator"
application_id = "arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerRDSPostgreSQLRotationSingleUser"
capabilities = [
"CAPABILITY_IAM",
"CAPABILITY_RESOURCE_POLICY",
]
parameters = {
functionName = "postgres-rotator"
endpoint = "https://secretsmanager.${data.aws_region.current.name}.${data.aws_partition.current.dns_suffix}"
vpcSubnetIds = join(",", aws_subnet.rds_subnet.*.id)
vpcSecurityGroupIds = "${aws_security_group.lambda_security_group.id}"
}
}
parameters
でサブネットとセキュリティグループを指定しています。
RDSはプライベートサブネットにあるため、Lambdaも同プライベートサブネットに配置します。
PostgreSQL以外のSAMも公開されています。
MySQL シングルユーザー
Oracle シングルユーザー
検索欄で"rotation"と打てば他にも出てきます。
Secrets Manager
最後にSecrets Managerに認証情報のシークレットを作成します。
resource "aws_secretsmanager_secret" "postgres_rotate_secretsmanager_secret" {
name = "postgres-rotate-secretsmanager-secret"
}
resource "aws_secretsmanager_secret_rotation" "postgres_rotate_secretsmanager_secret_rotation" {
secret_id = aws_secretsmanager_secret.postgres_rotate_secretsmanager_secret.id
rotation_lambda_arn = aws_serverlessapplicationrepository_cloudformation_stack.postgres_rotator.outputs.RotationLambdaARN
rotation_rules {
automatically_after_days = 30
}
}
resource "aws_secretsmanager_secret_version" "postgres_rotate_secretsmanager_secret_version" {
secret_id = aws_secretsmanager_secret.postgres_rotate_secretsmanager_secret.id
secret_string = <<-EOF
{
"engine": "postgres",
"host": "{RDSのエンドポイント}",
"username": "{DBのユーザ名}",
"password": "{DBのパスワード}",
"dbname": "{DBのデータベース名}",
"port": "5432"
}
EOF
}
aws_secretsmanager_secret_rotation
で作成したLambdaのARNを指定しています。
automatically_after_days
は更新間隔(日)です。
secret_string
は使用するデータベースによって内容が異なります。
その内容はこちらに記載されております。
完了
これでapplyし、Secrets Managerからローテーションを試してみて成功すればOKです。