Amazon RDSとAWS Secrets Manager の統合に伴うCloudFormationの新プロパティの設定を解説
DBパスワードの管理が直観的に
こんにちわ!
DevelopersIO BASECAMP参加者の加藤です!
突然ですが、DB認証情報は現在どのように管理されているでしょうか?
ハードコードされたセキュリティ情報が閲覧可能な状態はセキュリティ上のバッドプラクティスであると考えられています。
AWSにはパスワードをハッシュ化・さらに定期的なローテーションを実現させるAWS Secrets Managerというサービスが存在します!
2022年12月にこのDBの認証情報の管理に関わる新しいアップデートが発表され、遂にAmazon RDSと前述のAWS Secrets Managerの統合が実現しました!
それに伴い、CloudFormationにも新しいプロパティが追加されていますのでその特徴や設定方法について触れたいと思います!
まずは結論から
嬉しい点
・VPCエンドポイント(com.amazonaws.[リージョン].secretsmanager)の設置が不要になりコストメリットが出る
惜しい・今後に期待したい点
・スタック作成時にシークレットのローテーション間隔を指定出来ずデフォルトの7日は避けられなかった。(2022年3月8日時点)
マネコン(AWS Management Console)での設定する場合
先にマネコン画面での設定を見ていただいた方がイメージしやすいかと思いました。
RDSの作成時に
①名前の入力(adminから変更したい場合のみ)
②すぐ下のチェックを入れる。
③KMSの暗号化キーを指定する。(別のものを指定したい場合のみ)
これでadminというマスターユーザー名で、
デフォルト7日毎にローテーションするハッシュパスワードが生成されます。
CloudFormationで設定する場合
CloudFormationでは統合後に2つのプロパティが追加されました。
・ManageMasterUserPassword
・MasterUserSecret
これを以下のように設定する事で同じ状態になります。
ManageMasterUserPassword: true
MasterUsername: [任意の文字列] #admin 等
(自前の暗号化キーを指定する場合は)
ManageMasterUserPassword: true
MasterUsername: [任意の文字列] #admin 等
MasterUserSecret:
KmsKeyId: [作成済みのKmsのID] # !GetAtt [同スタック内で作成のKMSの論理ID].KeyId 等可能
ちなみに従来の方法
アップデート前に主流だった方法はこうでした。
現在もこの方法で設定は可能ですが、
メリット:スタック作成時にローテーション日数を指定出来る。
デメリット:時間当たりのVPCエンドポイント費用、ローテーション用の関数の実行費用が発生
となります。
統合は構成図にはどう落とし込むか
3層のプライベートサブネットにあるAuroraクラスターを例に図にしました。
先に紹介したリンク記事の方法では、このようなイメージで動いていました。
今後はこうなります。
実際にはAWS管理化では、図のように
・VPC内且つ、プライベートなサブネットにあるRDS
・VPC外にあるSecretsManager
の間で通信は発生しているものの、VPCのネットワーク構成には含まれないため、構成図に矢印を引きようがない。結果上記のようになると考えます。
検証
ここからは、エラーメッセージから記事に辿り着く方を想定し、
CloudFormationで設定に失敗した組み合わせ含め載せたいと思います。
ManageMasterUserPasswordは単独でシークレットを生成するか。
失敗パターン/単独で設定
まずはManageMasterUserPasswordというプロパティだけ設定してDBシークレットが作成されるか確認します。
[リソース論理ID名]:
Type: AWS::RDS::DBCluster
Properties:
ManageMasterUserPassword: true
↓
"The parameter MasterUsername must be provided and must not be blank.
MasterUsernameをセットで設定しないと機能しない事がわかりました。
↓
成功パターン/MasterUsernameをセットで設定
(※これ以降コードブロックから論理ID名とTypeの列は省略)
先ほどの失敗を踏まえ、ユーザーネームをセットにしました。
MasterUsername: [任意の文字列] #admin 等
ManageMasterUserPassword: true
↓
同リージョンのSecretsManagerのコンソールでシークレットの生成を確認してみると
・MasterUsernameプロパティに設定した値→usernameキーのvalueに転用**
・ハッシュ化されたパスワード
・ローテーションスケジュールはデフォルトの7日
となっている事が無事確認できました。
【結果】
成功
MasterUserSecret:KmsKeyId: で自前の暗号化キーを指定する。
失敗パターン/※従来の方法+自ら作成したKMSを指定。
従来はこうして別で作成したSecretの値を召喚していました。
単純にDB側から紐づけたシークレットの暗号化キーを指定出来るようになったものか。
として試したのがこちら。
MasterUserPassword: !Sub "{{resolve:secretsmanager:${AuroraDBSecret}:SecretString:password::}}"
MasterUsername: !Sub "{{resolve:secretsmanager:${AuroraDBSecret}:SecretString:username}}"
MasterUserSecret:
KmsKeyId: !GetAtt [作成したKMSKeyの論理ID].KeyId
↓
A ManageMasterUserPassword value is required when MasterUserSecretKmsKeyId is specified.
MasterUserSecret: KmsKeyId:は ManageMasterUserPasswordとセットで利用が必要との事。
追加して再挑戦します。
↓
成功パターン/ManageMasterUserPassword+MasterUsernameとセットで設定
MasterUsername: [任意の文字列] #admin 等
ManageMasterUserPassword: true
MasterUserSecret:
KmsKeyId: !GetAtt [作成したKMSKeyの論理ID].KeyId
↓
生成されたシークレットのコンソールで自身の暗号化キーの反映を確認。
暗号化キーが自前のものに変わっています。
(※"KMS-SecretsManager"は自身設定のAWS::KMS::AliasのAliasName)
【結果】
成功
MasterUserSecret:SecretArn: はそもそも何が出来るのか確認
ユーザーガイドからプロパティの説明を読むと
シークレットの Amazon リソースネーム (ARN)。
と簡素な説明が添えてありましたが、役割が全くわかりません。
自前で作成したSecretsManagerのシークレットをRDS管理化においてくれるのでは?
(前述のコストメリット享受が出来るので意味はある)
割愛しますが先に単独で設定した所、やはりMasterUsernameは必須でしたので以下のようにしました。
失敗パターン①/MasterUsernameとセットで設定
MasterUsername: fugafuga #作成済みのシークレットの"admin"と区別
MasterUserSecret:
SecretArn: arn:aws:secretsmanager:ap-northeast-1:0123456789012:secret:SampleSecret1234-xxxxxx #
↓
The parameter MasterUserPassword must be provided and must not be blank.
MasterUserPasswordも必要との事です。このエラーメッセージはなんだか怪しいですが一応試します。
失敗パターン②/MasterUserPasswordも追加
MasterUserPasswordを追加します。
MasterUsername: fugafuga
MasterUserPassword: hogehoge
MasterUserSecret:
SecretArn: arn:aws:secretsmanager:ap-northeast-1:0123456789012:secret:SampleSecret1234-xxxxxx
↓
・3プロパティがある事でスタック作成はCompleteしました。
一方で、
・事前に手動作成済したSecretの値に変化は無し。
・DBの設定タブ→MasterUsername,MasterUserPasswordの文字列が反映
(※ただMasterUsernameとMasterUserPasswordをハードコードしたのと変わらない。)
思っていたのとは違うようです。
失敗パターン③/RotationSchedule
従来から可能だった設定方法のメリットでもあった「AutomaticallyAfterDays:」というローテーション間隔を指定する項目だけを残し、
リージョンや関数を取っ払った設定が可能なのでは?
[DBクラスターの論理ID]:
Type: AWS::RDS::DBCluster
Properties:
MasterUsername: [任意の文字列] #admin 等
ManageMasterUserPassword: true
MasterUserSecret:
SecretArn: !Ref [自前のSecretの論理ID]
[ローテーションスケジュールの論理ID]:
DependsOn: [SecretTargetAttachmentの論理ID]
Type: AWS::SecretsManager::RotationSchedule
Properties:
RotationRules:
AutomaticallyAfterDays: 30
SecretId: !Ref [自前のSecretの論理ID]
#HostedRotationLambda:
#RotationLambdaName: !Sub sample-rotationschedule-function
#RotationType: MySQLSingleUser
#VpcSecurityGroupIds: !Ref [ローテーション関数セキュリティグループの論理ID]
#VpcSubnetIds:
#Fn::Join:
#- ","
#- - Ref: PrivateSubnet1a
#- Ref: PrivateSubnet1c
↓
No Lambda rotation function ARN is associated with this secret.
これも違うようです。
【結果】
失敗
検証まとめ
ManageMasterUserPasswordは
・MasterUserNameと一緒に設定しなければならない。
・MasterUserPasswordと一緒には利用しない。
・正常に動作している場合"!GetAtt [論理ID名].MasterUserSecret.SecretArn"の出力が可能。
MasterUserSecretは
・KmsKeyId:を指定する際はManageMasterUserPasswordもセットで。
・SecretArnの正体はドキュメントだけ読んで判明させるのは難しい
(このあたりが関連するかも)
その他
・Transform宣言は不要。ネストスタックも作成されない。
・ローテーション用の関数は作成されない(或いはユーザーからは見えない)
・SecurityGroup・ルールの追加もされない。
・Secrets Managerの統合に必要なアクセス許可についてはこちらを参照。
終わりに
統合で省かれたリソースについてはサービス単位で利用をしていない事で請求は起こりませんが、
"通信はしていない訳ではない"=AWSが実質なんらかの仕組みで負担・やりくりをしてくれているのではないか という理解です!
新しくわかった事があれば追記させていただきます!
最後までお付き合いいただきありがとうございました!!
Discussion