この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは、AWS事業本部コンサルティング部のたかくに(@takakuni_)です。
今回はタイトルの通り、AWS Secrets ManagerでRDSのパスワードローテーションをしてみようと思います。
弊社ブログ内でいくつが既に紹介されていますが、Secrets ManagerがVPCエンドポイントをサポートしていたためアップデート記事となります。
年初めに、タイトルに「in 2022」をつけて大丈夫か内心ソワソワしています...
2022年12月23日追記
年の終わりにまさかのアップデートがありました。
Amazon RDS と Secrets Manager が直接統合され、より簡単にパスワードローテーションできるようになりました。詳しくは以下をご覧ください!
公式ドキュメント
チュートリアル: AWS データベースのシークレットをローテーションする
弊社ブログ
機密管理サービス 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インスタンスからシークレットを取得する際は、443ポートでEC2インスタンスのセキュリティグループを別途許可する必要があります。
加えて、EC2の環境変数にSecrets Managerの情報(DBのユーザ・パスワード)を登録する方法は、こちらをご参照ください。
アウトバウンドルール:無し
事前準備 その2:VPCエンドポイントの設定
VPCエンドポイントを4つ(ssm, ssmmessages, ec2messages, secretsmanager)作成します。
ステップ1: テスト用 MySQL データベースの設定
検証用のRDSインスタンスを作成します。
DBエンジンのバージョンもDBインスタンスのクラスも任意で構いません。
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_)でした!