AWS Secrets Managerで RDSのパスワードローテーションしてみる in 2022
こんにちは、AWS 事業本部コンサルティング部のたかくに(@takakuni_)です。
今回はタイトルの通り、AWS Secrets Manager で RDS のパスワードローテーションをしてみようと思います。
弊社ブログ内でいくつが既に紹介されていますが、Secrets Manager が VPC エンドポイントをサポートしていたためアップデート記事となります。
年初めに、タイトルに「in 2022」をつけて大丈夫か内心ソワソワしています...
2022 年 12 月 23 日追記
年の終わりにまさかのアップデートがありました。
Amazon RDS と Secrets Manager が直接統合され、より簡単にパスワードローテーションできるようになりました。詳しくは以下をご覧ください!
公式ドキュメント
弊社ブログ
- 機密管理サービス AWS Secrets Manager で RDS のパスワードローテーションを試す
- 新サービス「AWS Secrets Manager」をチュートリアル 2 種(基本設定、RDS ローテーション)で基礎から学ぶ
構成図
ポイントは以下の通りです。
- セッションマネージャー使用用途で ssm、ec2message、ssmmessages の VPC エンドポイントを作成します。Secrets Manager とは直接的に関係はありません。
- Lambda から Secrets の取得用途で、 secretsmanager の VPC エンドポイントを作成します。
- 接続テスト用 EC2 インスタンスには、既に MySQL クライアントがインストールされています。
- 今回はテスト用のため Lambda 関数は、RDS と同一サブネットに配置していますが、同一 VPC 内であれば別サブネットに配置して問題ありません。
- ap-northeast-1c に配置したサブネットは、サブネットグループを作成するために使用するサブネットです。
- VPC エンドポイントの設定が完了後、チュートリアル: AWS データベースのシークレットをローテーションするの手順を踏んでいきます。(必要に応じて読み替えを行います。)
事前準備 その 1:セキュリティグループの作成
使用するセキュリティグループは以下になります。
- テスト用 EC2 インスタンスのセキュリティグループ
- Lambda のセキュリティグループ
- RDS のセキュリティグループ
- VPC エンドポイント(ssm, ssmmessages, ec2messages)のセキュリティグループ
- VPC エンドポイント(secretsmanager)のセキュリティグループ
セキュリティグループのルール設定
各セキュリティグループの詳細です。
テスト用 EC2 インスタンスのセキュリティグループ
- インバウンドルール:無し(セッションマネージャーで接続)
- アウトバウンドルール:フルオープン
Lambda のセキュリティグループ
- インバウンドルール:無し
- アウトバウンドルール:フルオープン
参考:アウトバウンドのセキュアパターン
CIDR/SG | ポート | 用途 |
---|---|---|
RDS の SG | 3306 | パスワード変更で接続するため |
VPC エンドポイント(secretsmanager)の SG | 443 | パスワード取得で接続するため |
RDS のセキュリティグループ
インバウンドルールは以下の通り
CIDR/SG | ポート | 用途 |
---|---|---|
Lambda の SG | 3306 | パスワード変更で接続するため |
EC2 | 3306 | テストで接続するため |
- アウトバウンドルール:フルオープン
VPC エンドポイント(ssm, ssmmessages, ec2messages)のセキュリティグループ
インバウンドルールは以下の通り
CIDR/SG | ポート | 用途 |
---|---|---|
テスト接続用 EC2 の SG | 443 | セッションマネージャーで接続するため |
- アウトバウンドルール:無し
VPC エンドポイント(secretsmanager)のセキュリティグループ
インバウンドルールは以下の通り
CIDR/SG | ポート | 用途 |
---|---|---|
Lambda の SG | 443 | パスワード取得で接続するため |
加えて、EC2 の環境変数に Secrets Manager の情報(DB のユーザ・パスワード)を登録する方法は、こちらをご参照ください。
- アウトバウンドルール:無し
事前準備 その 2:VPC エンドポイントの設定
VPC エンドポイントを 4 つ(ssm, ssmmessages, ec2messages, secretsmanager)作成します。
ステップ 1: テスト用 MySQL データベースの設定
検証用の RDS インスタンスを作成します。
RDS の設定値は次の通り設定しました。
設定値 | 値 |
---|---|
DB インスタンス識別子 | MyTestDatabaseInstance |
マスターユーザー名 | adminuser |
マスターパスワード | KeyRotatePassword |
チュートリアルとの相違点(テスト用 MySQL データベースの設定)
ドキュメントではパブリックアクセスを「Yes」に変更していますが、今回は VPC Endpoint 経由でアクセスするため No のまま設定 しました。
セキュリティグループ設定欄で事前準備した RDS 用セキュリティグループ を設定しました。
ステップ 2: シークレットを作成する
Secrets Manager コンソール画面からシークレットを作成します。
ステップ 1 で指定したユーザー名、パスワード、ローテーション対象の RDS インスタンスを設定します。
「次へ」をクリックすると、シークレット名と説明の設定画面へ遷移します。
シークレットの名前は、チュートリアルの通り「MyTestDatabaseMasterSecret」と設定しました。
「次へ」を選択すると、ローテーション設定に遷移します。
今回は、事前に RDS で設定したパスワードの接続確認を行うため、チュートリアルに従い「自動ローテーション」は無効に設定します。
作成するシークレットの確認画面へ遷移します。
問題なければ、「保存」でシークレットを作成します。
シークレットが作成されました。
ステップ 3: 最初のシークレットを検証する
接続確認用 EC2 インスタンスから RDS へ接続を行います。シークレットとして登録した後でも問題なく接続できました。
ステップ 4: シークレットのローテーションを設定する
ここからが本題です。
ステップ 2 にて作成したシークレットにローテーション機能を追加します。
Secrets Manager コンソールからステップ 2 にて作成したシークレットを選択し、「ローテーション構成」の変更を行いました。
ローテーション間隔は、1 から 365 日までで選択可能でした。
ローテーション設定を保存すると画面上部のポップアップに「ローテーション用の AWS CloudFormation リソースを作成します。」と表示されました。
内部的に、スタックを作成する仕組みのようです。
ステップ 4 +α: Lambda 関数の VPC 設定を行う
VPC Endpoint へ Lambda 関数が接続できるように「VPC 設定」を変更します。
セキュリティグループを事前準備で作成したLambda 用のセキュリティグループに変更します。
ステップ 5: 成功したローテーションの確認
それでは、パスワードを変更していきましょう。
再び Secrets Manager コンソールへ戻り、シークレットを選択。
「ローテーション構成」から「シークレットをローテーションさせる」を選択します。
シークレットの値が更新されていることが確認できました!
ステップ 5 +α: 接続確認
新しくローテーションを行ったパスワードで MySQL へ接続を行います。
パスワードの文字列の関係で対話式でログインを行いましたが、問題なくログインできました!
もっと知りたい人向け
CloudFormation で設定されるリソースについて
スタックの中身としては、「AWS::Serverless::Function」、「AWS::IAM::Role」、「AWS::Lambda::Permission」を作成しているみたいです。
ソースコードは以下になります。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Parameters:
endpoint:
Type: String
Description: The Secrets Manager endpoint to use.
functionName:
Type: String
Description: The name of the Lambda function.
invokingServicePrincipal:
Type: String
Description: The service principal for the invoking service.
Default: "secretsmanager.amazonaws.com"
vpcSubnetIds:
Type: CommaDelimitedList
Description: A comma-separated list of VPC subnet IDs applied to the database network.
Default: ""
vpcSecurityGroupIds:
Type: CommaDelimitedList
Description: A comma-separated list of security group IDs applied to the database.
Default: ""
kmsKeyArn:
Type: String
Description: The ARN of the KMS key that Secrets Manager uses to encrypt the secret.
Default: ""
excludeCharacters:
Type: String
Description: A string of the characters that you don't want in the password.
Default: "/@\"'\\" # MySQL DB設定時はデフォルト値が設定されていました。
Conditions:
AddVpcConfig:
!And
- !Not [!Equals ["", !Join ["", !Ref vpcSubnetIds]]]
- !Not [!Equals ["", !Join ["", !Ref vpcSecurityGroupIds]]]
KmsKeyArnExists:
!Not [!Equals ["", !Ref kmsKeyArn]]
Resources:
SecretsManagerRDSMySQLRotationSingleUser:
Type: AWS::Serverless::Function
Properties:
FunctionName:
Ref: functionName
Description: Rotates a Secrets Manager secret for Amazon RDS MySQL credentials using the single user rotation strategy.
Handler: lambda_function.lambda_handler
Runtime: python3.7
CodeUri: s3://secrets-manager-rotation-apps-7c4fea7a5d7a5497325114af491e6b58/SecretsManagerRDSMySQLRotationSingleUser/SecretsManagerRDSMySQLRotationSingleUser.zip # AWSさんのURL 既存EC2からはアクセスできませんでした。
Timeout: 30
Policies:
- VPCAccessPolicy: {}
- AWSSecretsManagerRotationPolicy:
FunctionName:
Ref: functionName
- !If
- KmsKeyArnExists
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- kms:Decrypt
- kms:DescribeKey
- kms:GenerateDataKey
Resource: !Ref kmsKeyArn
- !Ref "AWS::NoValue"
Environment:
Variables:
SECRETS_MANAGER_ENDPOINT:
Ref: endpoint
EXCLUDE_CHARACTERS:
Ref: excludeCharacters
VpcConfig:
!If
- AddVpcConfig
-
SubnetIds: !Ref vpcSubnetIds
SecurityGroupIds: !Ref vpcSecurityGroupIds
- !Ref "AWS::NoValue"
Tags:
SecretsManagerLambda: Rotation
LambdaPermission:
Type: 'AWS::Lambda::Permission'
Properties:
Action: 'lambda:InvokeFunction'
FunctionName: !GetAtt SecretsManagerRDSMySQLRotationSingleUser.Arn
Principal: !Ref invokingServicePrincipal
Outputs:
RotationLambdaARN:
Description: The ARN of the rotation lambda
Value: !GetAtt SecretsManagerRDSMySQLRotationSingleUser.Arn
自動作成される Lambda に関して
IAM ロール
Lambda に付与される IAM ロールは、AWS 管理ポリシー「AWSLambdaBasicExecutionRole」、「AWSLambdaVPCAccessExecutionRole」に加えて以下のポリシーがインラインポリシーで定義されていました。
また、Lambda のリソースベースポリシーではSecrets Manager からのみ実行できるように制限されていました。
Secrets Manager で IAM ロールを設定していない(できない)ため、Lambda 側から許可する用途で使用されているのではないかと思います。
VPC 設定
ステップ 4 で自動生成された Lambda の「VPC 設定」の初期状態は以下になります。
ほとんど、マスキングでわかりませんが VPC 設定をまとめると以下になります。
- マルチ AZ でデプロイされる(RDS 本体のマルチ AZ 設定の有無は関係ない)
- サブネットは RDS のサブネットグループで選択したサブネットで作成される
- セキュリティグループは、RDS にアタッチされたセキュリティグループと同じセキュリティグループがアタッチされている
まとめ
個人的に、簡単にローテーションを組むことができて画期的な機能だと思いました。
今回、記事としてはネットワーク経路をよりセキュアに変更した記事でしたが、「運用面も考慮して実装したい!」という方は、新サービス「AWS Secrets Manager」をチュートリアル 2 種(基本設定、RDS ローテーション)で基礎から学ぶ 【運用上を気をつけておくべきポイント 3 点】も合わせてご覧いただけますと幸いです。
以上、AWS 事業本部コンサルティング部のたかくに(@takakuni_)でした!