【AWS】AWS Secrets Managerを使って、RDSのパスワードをローテーションさせてみました


はじめに

AWS Secrets Managerは皆さん使っていますでしょうか。
AWS上のRDSをより安全でセキュアに使っていくにあたって、このサービスが大いに役に立ちます。
簡単によくある使い道を説明すると、RDSへの接続は、直接パスワードやRDSエンドポイント等の情報をそのままコードに埋め込むではなく、Secrets Managerを経由して必要な情報を取得し、接続していくパターンになります。
さらに、AWS Secrets Managerは、Lambda経由してパスワードをローテーションさせることができるので、パスワード管理などは大変便利になります。
早速、個人が使い方をまとめましたので、当手順を公開していきます。

構成図

全体構成図は下記になります。ポイントをざっくり解説します。
・EC2にSecrets Managerをアクセスできるように、適切なロールを付与します。
・Secrets Managerにシークレット情報を暗号化するために、KMSで暗号化をかけています。
・VPC内にLmabdaがあり、Secrets Managerと通信できるように、VPCエンドポイントを作成しています。
・Secrets ManagerでRDS等の接続情報を管理しており、Lambda経由でRDS接続用のパスワードを定期的にローテーションさせる役割を果たしています。

1.EC2の設定

今回私が使っているEC2は Amazon Linux 2になります。
あらかじめ下記のミドルウェア等をインストールしておきましょう。
①EPEL リポジトリ

$ sudo amazon-linux-extras install epel -y

参考URL:

②AWS CLI

$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
$ unzip awscliv2.zip
$ sudo ./aws/install

参考URL:

③jq

$ sudo yum install jq -y

④postgresql

$ sudo yum install postgresql -y

⑤EC2にIAMロールを付与
・SecretsManagerReadWrite
・AmazonEC2RoleforSSM
なお、SSMロールは個人的にSSM Agentを利用するため付与したロールです。
実際Secrets Managerだけを使うのであれば、「SecretsManagerReadWrite」だけで十分です。

2.RDSの作成

今回私が作成したRDSはPostgreSQLです。
RDS作成手順はここで割愛させていただきます。

3.Secrets Managerの作成

上記「2.RDSの作成」で作成されたRDSの「ユーザー名」と「パスワード」を入力します。
暗号化キーは今回「DefaultEncryptionKey」を選択します。
※なお、暗号化キーにKMSカスタマーキーも設定できるが、今回はデフォルを選択します。

上記「2.RDSの作成」で作成されたRDSを選択します。

シートレットの名前を入力します。

「自動ローテーションを有効にする」を選択します。
私は今回ローテーション間隔は30日間と設定します。

「ローテーションを実行するための新しいLambda関数を作成します」を選択します。
「新しいAWS Lambda関数名」に関数名を入れておきます。
「ローテーションの実行に使用するシークレットを選択してください」に「このシークレットを使う」を選択します。

最後はレビューでもう一回チェックをし、問題がなければ作成ボタンを押していきます。
作成後に、合わせてAWSがLambda関数を作成してくれるので、Lambdaで確認できます。

4.セキュリティグループの設定

EC2用のセキュリティグループとほかに、以下三つのセキュリティグループを作成または編集する必要があります。
・Secret Manager VPCエンドポイント用セキュリティグループ
・RDS用セキュリティグループ
・Lmabda用セキュリティグループ

①Secret Manager VPCエンドポイント用セキュリティグループ、VPC CIDR でTCP 443ポートを許可します。今回私が使っている VPC CIDR は 10.0.0.0/16 なので、10.0.0.0/16を開放します。

②Lmabda用セキュリティグループに、Secret Manager エンドポイント用セキュリティグループを追加します。ポートは TCP 443 です。

③RDS用セキュリティグループに、Lmabda用セキュリティグループを追加します。ポートは TCP 5432です。

④上記「3.Secrets Managerの作成」で作成されたLambda関数のセキュリティグループを編集します。
自動作成されたLambda関数では、デフォルセキュリティグループを使用します。
デフォルセキュリティグループを、Lambda用のセキュリティグループに置き換えていきます。

5.Secrets Manager 用 VPC エンドポイント作成

VPC→エンドポイントで、Secrets Manager 用 VPC エンドポイント作成していきます。
エンドポイントサービス名:com.amazonaws.ap-northeast-1.secretsmanager

VPCとサブネットを選択します。

セキュリティグループに、上記設定した Secret Manager VPCエンドポイント用セキュリティグループを選択します。

ポリシー欄は一旦このままでよいので、作成後に編集していきます。
エンドポイントの作成を押してください。

エンドポイント作成後に、必ずエンドポイントのポリシーを変更してください。
下記のようなポリシーを使用するには、AWS アカウント ID と VPC エンドポイント ID のプレースホルダーを、ご自身の有効な値で置き換えます。
このエンドポイントのポリシーの意味は下記となります:

ユーザーがSecrets Manager にリクエストを送信すると、Secrets Manager はリクエストの VPC エンドポイント ID をaws:sourceVpce条件キーの値を指定します。これらが一致しない場合、Secrets Manager はリクエストを拒否します。

参考URL:

{
    "Id": "example-policy-1",
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "EnableSecretsManagerpermissions",
            "Effect": "Allow",
            "Principal": {"AWS":["XXXXXXXXXXX"]},
            "Action": ["secretsmanager:*"],
            "Resource": "*"
        },
        {
            "Sid": "RestrictGetSecretValueoperation",
            "Effect": "Deny",
            "Principal": "*",
            "Action": ["secretsmanager:GetSecretValue"],
            "Resource": "*",
            "Condition": {
                "StringNotEquals": {
                    "aws:sourceVpce": "vpce-XXXXXXXXXXXXXX"
                }
            }
        }

    ]
}

5.パスワードをローテーションさせる

パスワード変更前:

sh-4.2$ aws secretsmanager get-secret-value --secret-id a-test-postgresql-key --region ap-northeast-1 | jq .SecretString | jq fromjson
{
  "username": "admindadada",
  "password": "tX9Vbx5A",
  "engine": "postgres",
  "host": "secret-manager-test.XXXXXXXXXXXXX.ap-northeast-1.rds.amazonaws.com",
  "port": 5432,
  "dbname": "dadada",
  "dbInstanceIdentifier": "secret-manager-test"
}

Secrets Managerで、「すぐにシークレットをローテーションさせる」押してください。

すると、パスワードが変更されたことを確認できました。

sh-4.2$ aws secretsmanager get-secret-value --secret-id a-test-postgresql-key --region ap-northeast-1 | jq .SecretString | jq fromjson
{
  "username": "admindadada",
  "password": "_DDFpe$EFcby>ZS(3Q-]jGhbb!x]zbkz",
  "engine": "postgres",
  "host": "secret-manager-test.XXXXXXXXXXXXX.ap-northeast-1.rds.amazonaws.com",
  "port": 5432,
  "dbname": "dadada",
  "dbInstanceIdentifier": "secret-manager-test"
}

6.まとめ

このように、AWS Secrets Managerを使うことによって、コード内に直接RDSのエンドポイントやパスワード等の情報を埋め込まずに済みました。
さらにLambdaでローテーションさせることで、安全性を一層高めることが出来ます。
ちなみに、jqを使って、RDSの諸情報をフィルターできます。

下記フィルターの例になります。
RDSのユーザー:

aws secretsmanager get-secret-value --secret-id XXXXXXXXXXX --region ap-northeast-1 | jq .SecretString | jq fromjson | jq -r .username

RDSのパスワード:

aws secretsmanager get-secret-value --secret-id XXXXXXXXXXX --region ap-northeast-1 | jq .SecretString | jq fromjson | jq -r .password

RDSのエンドポイント:

aws secretsmanager get-secret-value --secret-id XXXXXXXXXXX --region ap-northeast-1 | jq .SecretString | jq fromjson | jq -r .host

RDSのポート:

aws secretsmanager get-secret-value --secret-id XXXXXXXXXXX --region ap-northeast-1 | jq .SecretString | jq fromjson | jq -r .port

今回クラスメソッドさんの記事が大変参考になりました。

参考資料: