はじめに
こんにちは。1年ぶりの記事です。
サボっていたわけではありません。ただ、生きている喜びを実感していただけです。
素敵やん。
今回担当案件でどうしても実現したい事象がありました。
それはSecrets Managerの値を別アカウントのSecretsManagerに同期したいというものでした。
説明下手でわかりにくい箇所等あるかと思います。ご了承ください。
備忘録程度なので雑ですみません!権限はもっと絞れるはずです!
もっと簡単な方法あるぞ!という意見お待ちしています。
前提
AWSアカウントAとAWSアカウントBがあり、それぞれ異なるECサイトを運用しています。
アカウントAにAuroraがあり、そのDB情報等をjsonファイルとしてSecretsManagerに格納しています。
アカウントBはアカウントAのAuroraをクロスアカウントで利用しています。
この度、お客様よりアカウントBにもアカウントAと同期されたSecretsManagerを作成してほしいとの要望がありました。
課題
いやいやわざわざSecrets作成しなくても下記の方法でクロスアカウントで取得できるぜ!
SecretsManagerのキーをKMSキーに変更し、スイッチロールで値を取得することが可能です。
しかし、今回の要望は同期されたSecretsManagerを作成してほしいというものでした。(要件は複雑なので割愛します)
そこでアカウントAのSecretsManagerの値に変更が加えられたら、アカウントBのSecretsManagerにも反映されるように実装をしていきました。
解決策
EventBridgeとLambdaを使って実装することにしました。
流れとしては
- アカウントAのSecretsManagerの値変更
- EventBridgeが変更を検知し、アカウントA内でターゲットのLambdaを実行
- LambdaがアカウントBのSecretsManagerを書き換える
※いずれにせよLambdaでスイッチロールは必須
解決
アカウントB準備
SecretsManager作成
「super-top-secretB」という名前のSecretsManagerを作成します。
ロール作成
アカウントAからスイッチロールしてきて、アカウントBのSecretsManagerを操作するIAMRoleを作成します。
ロール名「secretsmanager-change-role」
信頼されたエンティティ
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::アカウントAのID:role/service-role/AcountB-SecretsManager-cahnge-Lambda-role(後ほど作成します)"
},
"Action": "sts:AssumeRole"
}
]
}
今回ポリシーは以下をアタッチします
- SecretsManagerReadWrite(AWS管理:SecretsManagerへのwrite)
アカウントA準備
Lambda作成
Lambdaを作成し、ロールをアタッチします。(クロスアカウントのためのポリシーも付与)
ロール名「AcountB-SecretsManager-cahnge-Lambda-role」
今回ポリシーは以下をアタッチします。
- AWSLambdaBasicExecutionRole-***(Lambdaのデフォルトポリシー)
- SecretsManagerReadWrite(AWS管理:SecretsManagerのread)
- AcountB-assumerole-policy(アカウントBへのアシュームロール用のポリシー)
AcountB-assumerole-policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::アカウントBのID:role/secretsmanager-change-role"
}
]
}
ソースコードは次のようにしました。
import boto3
import json
def lambda_handler(event, context):
source_secret_name = "super-top-secretA" # ソースアカウントのシークレット名
destination_secret_name = "super-top-secretB" # 宛先アカウントのシークレット名
# ソースアカウントのSecrets Managerクライアントを作成
client_source = boto3.client('secretsmanager', region_name='ap-northeast-1')
# 宛先アカウントのSecrets Managerクライアントを作成
# AssumeRoleを使用して一時的な認証情報を取得
sts_client = boto3.client('sts')
assumed_role = sts_client.assume_role(
RoleArn="arn:aws:iam::アカウントBのID:role/secretsmanager-change-role",
RoleSessionName="ReplicateSecretSession"
)
credentials = assumed_role['Credentials']
client_destination = boto3.client(
'secretsmanager',
region_name='ap-northeast-1',
aws_access_key_id=credentials['AccessKeyId'],
aws_secret_access_key=credentials['SecretAccessKey'],
aws_session_token=credentials['SessionToken'],
)
# ソースアカウントからシークレットを取得
secret_value = client_source.get_secret_value(SecretId=source_secret_name)['SecretString']
# 宛先アカウントにシークレットを書き込み
response = client_destination.put_secret_value(
SecretId=destination_secret_name,
SecretString=secret_value
)
return {
'statusCode': 200,
'body': json.dumps('Secret replicated successfully.')
}
EventBrige
EventBridge作成し、ターゲットを上記Lambdaに設定します。
イベントパターン
{
"source": ["aws.secretsmanager"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventSource": ["secretsmanager.amazonaws.com"],
"eventName": ["PutSecretValue", その他お好みで検知したい変更を],
"responseElements": {
"arn": ["arn:aws:secretsmanager:ap-northeast-1:アカウントAのID:secret:super-top-secretA"]
}
}
}
これでsuper-top-secretAの値を変更するとsuper-top-secretBに同期されるようになります。
最後に
AWSのベストプラクティスは存在するものの、運用しているサービスによっては多種多様な実装方法があることを最近思い知らされます。
設計の時点で最善を構築するのはもちろんですが、運用途中でも最善を探すことが一番大事だなーと思いました!
ご意見は遠慮なくください!