目次
とある日
RDSのMySQLユーザパスワードをLambdaやプログラムに直接書かないで、セキュリティを向上させたいため、Secrets Managerを利用した話。
LamabdaからRDSへの接続する際のパスワードをSecrets Managerで取得する際の方法でVPCエンドポイントを使った記事があまりなかったので、自分の構築を備忘録として残します。
参考にした記事など各所にあるので参考にしてください。
はじめに
この記事は、2021年6月8日を基準として構築を行っています。
想定読者
- VPCエンドポイントを使用してLambdaからSecrets Managerへのアクセス接続を目的としている方
記事の構成
解説しないこと
AWS構成図
赤点線で囲んだところの話のみします。
構築手順
1. Secrets Managerを作成
https://blog.serverworks.co.jp/secrets-manager-rotation
ステップ2までは参考にしても問題ない。
2. サンプルコードを追加&修正
作成したシークレットを選択して、一番下まで行くと、
サンプルコードがあるのでコピーします。
下記のようなソースコード。
Lambdaに、適当なファイルを作成してペーストします。
import boto3 import base64 from botocore.exceptions import ClientError def get_secret(): secret_name = "MY/SECRET/NAME" region_name = "us-west-2" # Create a Secrets Manager client session = boto3.session.Session() client = session.client( service_name='secretsmanager', region_name=region_name ) # In this sample we only handle the specific exceptions for the 'GetSecretValue' API. # See https://docs.aws.Amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html # We rethrow the exception by default. try: get_secret_value_response = client.get_secret_value( SecretId=secret_name ) except ClientError as e: if e.response['Error']['Code'] == 'DecryptionFailureException': # Secrets Manager can't decrypt the protected secret text using the provided KMS key. # Deal with the exception here, and/or rethrow at your discretion. raise e Elif e.response['Error']['Code'] == 'InternalServiceErrorException': # An error occurred on the server side. # Deal with the exception here, and/or rethrow at your discretion. raise e Elif e.response['Error']['Code'] == 'InvalidParameterException': # You provided an invalid value for a parameter. # Deal with the exception here, and/or rethrow at your discretion. raise e Elif e.response['Error']['Code'] == 'InvalidRequestException': # You provided a parameter value that is not valid for the current state of the resource. # Deal with the exception here, and/or rethrow at your discretion. raise e Elif e.response['Error']['Code'] == 'ResourceNotFoundException': # We can't find the resource that you asked for. # Deal with the exception here, and/or rethrow at your discretion. raise e else: # Decrypts secret using the associated KMS CMK. # Depending on whether the secret is a string or binary, one of these fields will be populated. if 'SecretString' in get_secret_value_response: secret = get_secret_value_response['SecretString'] else: decoded_binary_secret = base64.b64decode(get_secret_value_response['SecretBinary']) # Your code goes here.
自分は、LambdaにSecretsManager.pyで作成しました。
ハンドラーに指定してあるファイルに、SecretsManager.pyをimportしました。
importしたら、get_secret()
を呼び出せれば初期配置完了です。
次は、プログラム一部を変更します。
下記の記事で上記のプログラムを拡張してデータベースに接続するプログラムまであるのですが、
自分はそこは別ファイルに切り出していたでの、必要な部分はpasswordを取り出すところだけなので、
下記のようなコードを追加。
追加場所は、上記のコードの50番あたり。
if 'SecretString' in get_secret_value_response: secret = get_secret_value_response['SecretString'] j = json.loads(secret) password = j['password']
あとは、return password
で返り値でパスワードが受け取れます。
これでLambdaのプログラム修正は完成しました。
3. VPCエンドポイント作成
Lambdaを配置してあるVPCを選択。
[Security groups] で、セキュリティグループを選択または作成します。
セキュリティグループを使用すると、エンドポイントへのアクセスをコントロールできます。これは、ファイアウォールを使用するのに似ています。
セキュリティグループは、Lambdaに付与してあるセキュリティグループを許可すればいいと思います。
4. Lambda IAMポリシーアタッチ
LambdaをVPC配置したときにセキュリティグループなどは追加済みです。
SecretsManagerへのアクセスを許可するためポリシーを付与。
最低限のポリシーsecretsmanager:GetSecretValue
だけ許可すれば良いと思います。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": "secretsmanager:GetSecretValue", "Resource": "*" } ] }
5. Secrets Managerリソースポリシー追加
リソースポリシーで、VPCエンドポイントのアクセスを許可する。
{ "Id": "example-policy-1", "Version": "2012-10-17", "Statement": [ { "Sid": "RestrictGetSecretValueoperation", "Effect": "Deny", "Principal": "*", "Action": ["secretsmanager:GetSecretValue"], "Resource": "*", "Condition": { "StringNotEquals": { "aws:sourceVpce": "vpce-1234a5678b9012c" } } } ] }
VPC エンドポイントで Secrets Manager を使用する - AWS Secrets Manager
6. Lambdaから接続テスト
Lambdaからのテストを実行する。
無事に、データベースにアクセスができたら成功となる。
Timeoutになる場合は、セキュリティ関連でアクセスができていないと思われます。
ハマったポイント
LambdaからSecrets Managerのアクセスの方法がわからないかった。
リソースポリシーとかを設定すればアクセスできると思っていたが。
VPC内に配置してあるLambdaは、VPCの外にあるSecrets Managerにアクセスできないので、VPCで通信をできるよに設定する必要があった。
その場合は、下記の記事で解説されてます。
元記事は、英語なのでGoogle翻訳を使用しています。
であなたの関数を置き、プライベートサブネットと使用NATゲートウェイ/インスタンスをインターネットへのアクセスを提供するために、正しく設定ルートテーブルで、その結果、へSecrets Manager。 プライベート サブネットで Secrets Manager の VPC インターフェース エンドポイントを設定します。このようにして、ラムダ関数はSecrets Manager、インターネットを必要とせずに、エンドポイントを使用して に接続できます。
1つ目の方法は、下記の記事でやってる内容だと思います。
AWS Secrets Managerに保存されたRDSのログインパスワードをローテーションしてみた件 - サーバーワークスエンジニアブログ
自分は2つ目の方法で行いました。
〆
VPC内のLambdaからSecrets Managerのアクセス経路がわからなかったため苦戦した。
AWSは、セキュリティにつまずく事が多いですね。
それも、アクセスできないからエラーが返ってくる場合と、返ってこない場合があるため前者ならいいが、後者だとトラブル箇所の見極めが難しい。