🔐

Amazon RDSとAWS Secrets Manager の統合に伴うCloudFormationの新プロパティの設定を解説

2023/03/10に公開

DBパスワードの管理が直観的に

こんにちわ!

DevelopersIO BASECAMP参加者の加藤です!

突然ですが、DB認証情報は現在どのように管理されているでしょうか?

ハードコードされたセキュリティ情報が閲覧可能な状態はセキュリティ上のバッドプラクティスであると考えられています。

AWSにはパスワードをハッシュ化・さらに定期的なローテーションを実現させるAWS Secrets Managerというサービスが存在します!

2022年12月にこのDBの認証情報の管理に関わる新しいアップデートが発表され、遂にAmazon RDSと前述のAWS Secrets Managerの統合が実現しました!

https://aws.amazon.com/jp/about-aws/whats-new/2022/12/amazon-rds-integration-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 等可能 

ちなみに従来の方法

アップデート前に主流だった方法はこうでした。

https://dev.classmethod.jp/articles/try-cloudformation-template-for-rds-password-rotation-with-aws-secrets-manager/

現在もこの方法で設定は可能ですが、

メリット:スタック作成時にローテーション日数を指定出来る。
デメリット:時間当たりのVPCエンドポイント費用、ローテーション用の関数の実行費用が発生

となります。


統合は構成図にはどう落とし込むか

3層のプライベートサブネットにあるAuroraクラスターを例に図にしました。

先に紹介したリンク記事の方法では、このようなイメージで動いていました。

今後はこうなります。

実際にはAWS管理化では、図のように

・VPC内且つ、プライベートなサブネットにあるRDS
・VPC外にあるSecretsManager

の間で通信は発生しているものの、VPCのネットワーク構成には含まれないため、構成図に矢印を引きようがない。結果上記のようになると考えます。


検証

ここからは、エラーメッセージから記事に辿り着く方を想定し、
CloudFormationで設定に失敗した組み合わせ含め載せたいと思います。

ManageMasterUserPasswordは単独でシークレットを生成するか。

失敗パターン/単独で設定

まずはManageMasterUserPasswordというプロパティだけ設定してDBシークレットが作成されるか確認します。

失敗パターン①/ManageMasterUserPasswordを単独で設定
  [リソース論理ID名]: 
    Type: AWS::RDS::DBCluster
    Properties: 
      ManageMasterUserPassword: true

エラーメッセージ/訳:パラメータ MasterUsername は必ず指定する必要があり空白であってはなりません。
"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側から紐づけたシークレットの暗号化キーを指定出来るようになったものか。

として試したのがこちら。

失敗パターン①/※従来の方法+自ら作成したKMSを指定。
      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のみセットで設定(値は自前で作成したSecretsManagerのシークレットARN)
      MasterUsername: fugafuga #作成済みのシークレットの"admin"と区別
      MasterUserSecret:
        SecretArn: arn:aws:secretsmanager:ap-northeast-1:0123456789012:secret:SampleSecret1234-xxxxxx #

エラーメッセージ/訳:MasterUserPasswordが無い・或いはblankは許容されません
The parameter MasterUserPassword must be provided and must not be blank.

MasterUserPasswordも必要との事です。このエラーメッセージはなんだか怪しいですが一応試します。


失敗パターン②/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:」というローテーション間隔を指定する項目だけを残し、
リージョンや関数を取っ払った設定が可能なのでは?

失敗パターン①/自身作成のシークレットを指定+HostedRotationLambdaをコメントアウト→ローテーション間隔の指定に挑戦
  [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