異なる AWS アカウントの S3 バケットに Lambda からエンドポイント経由でアクセスする【エンジニアブログより】
技術 1 課の山中です。 今日、屋上で作業をしようとおもって上がったら、小雨が降っていてすぐに断念しました。
というわけで、本日は異なるアカウントの S3 バケットに対して VPC エンドポイント経由でアクセスしてみたいとおもいます。
構成について
今回は異なるアカウントとしてアカウント A とアカウント B を想定しています。 アカウント B には S3 バケットが存在し、アカウント A の Lambda からアカウント B の S3 バケットを参照します。 Lambda は Private サブネットにあり取得したデータを DirectConnect 経由で SFTP 等を利用してオンプレミスのサーバに送信することが可能です。(ここは本ブログでは割愛します。
認証
アカウント A の Lambda にはアカウント B の S3 バケットにアクセスするための権限が必要です。 Lambda から STS を利用して S3 にアクセスするための一時クレデンシャルを取得します。
事前準備
アカウント B での準備
S3 バケットの作成
データを取得するためのバケット(examplebucket)を作成します。 今回はエンドポイント経由でのアクセスだけを許可するように、以下バケットポリシーを付与します。
{
"Version": "2012-10-17",
"Id": "Policy1415115909152",
"Statement": [
{
"Sid": "Access-to-specific-VPCE-only",
"Action": "s3:*",
"Effect": "Deny",
"Resource": ["arn:aws:s3:::examplebucket",
"arn:aws:s3:::examplebucket/*"],
"Condition": {
"StringNotEquals": {
"aws:sourceVpce": "vpce-1a2b3c4d"
}
},
"Principal": "*"
}
]
}
S3 バケットにアクセスするための IAM Role 作成
IAM ロールの作成画面にて信頼されたエンティティの種類で「別の AWS アカウント」を選びアカウント A のアカウント ID を入力します。
今回は S3 へアクセスしたいので AmazonS3FullAccess のポリシーをアタッチします。
※ examplebucket のみにアクセスを許可したい場合は、別途ポリシーを作成しアタッチしてください。
アカウント A での準備
STS をコールするための IAM Role 作成
STS をコールするためのポリシーを作成します。 Resource にはアカウント B で作成したロール ARN を指定します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::111111111111:role/accountb-s3bucket-access-role"
}
]
}
IAM ロールの作成画面にて信頼されたエンティティの種類で「AWS サービス」を選択後「Lambda」を選びます。
その後作成したポリシーをアタッチしてください。
VPC 及びサブネットの作成
今回は 1 つの VPC と 2 つのサブネットを作成します。 サブネットは Public と Private を作成します。それぞれの役割は以下のとおりです。
※ STS の API をコールするために Lambda はインターネットに接続できる必要があります。
エンドポイントの作成
Lambda をデプロイする VPC に S3 エンドポイントを作成してください。
参考:VPC Endpointを使ってS3にアクセスしてみる
NAT サーバの作成
NAT インスタンス作成手順 に従い、 Public サブネットに NAT インスタンスを作成します。 (もちろん NAT Gateway を利用しても大丈夫です。)
ルーティングの設定
以下を参考に Private サブネットのルートテーブルを修正します。
Lambda の設定
新しい Lambda を作成します。 以下の設定値を参考に作成してください。
import boto3
sts = boto3.client('sts')
def lambda_handler(event, context):
assumedRoleObject = sts.assume_role(
RoleArn='arn:aws:iam::111111111111:role/accountb-s3bucket-access-role',
RoleSessionName='sample_session',
)
credentials = assumedRoleObject['Credentials']
s3 = boto3.resource(
's3',
aws_access_key_id = credentials['AccessKeyId'],
aws_secret_access_key = credentials['SecretAccessKey'],
aws_session_token = credentials['SessionToken'],
)
bucket = s3.Bucket('examplebucket')
for object in bucket.objects.all():
print(object.key)
作成後、テスト実行するとアカウント B のバケットのオブジェクト一覧が取得できるはずです。 取得したオブジェクトを DirectConnect 経由で拠点に送る等処理をすることができます。
注意事項
Lambda からインターネットに接続できる必要がある
今回 Private サブネットの Lambda ファンクションから他アカウントの S3 へプライベートアクセスを行いましたが、 アカウント B のバケットを操作する権限を取得するために AWS Security Token Service(STS) を利用しているので HTTPS リクエストを Lambda からインターネット経由で発行できる必要があります。
まとめ
今度は今回作成したアカウント B のバケットのイベントをトリガとしてアカウント A の Lambda を実行する方法を書きたいと思います!