異なる AWS アカウントの S3 バケットに Lambda からエンドポイント経由でアクセスする

記事タイトルとURLをコピーする

技術 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 を作成します。それぞれの役割は以下のとおりです。


Public サブネット Private サブネットの Lambda が AWS エンドポイントにインターネット経由でアクセスするための NAT サーバを配置
Private サブネット Lambda ファンクションをデプロイするサブネット

※ STS の API をコールするために Lambda はインターネットに接続できる必要があります。

エンドポイントの作成

Lambda をデプロイする VPC に S3 エンドポイントを作成してください。


参考:VPC Endpointを使ってS3にアクセスしてみる

NAT サーバの作成

NAT インスタンス作成手順 に従い、 Public サブネットに NAT インスタンスを作成します。 (もちろん NAT Gateway を利用しても大丈夫です。)

ルーティングの設定

以下を参考に Private サブネットのルートテーブルを修正します。

送信先 ターゲット
VPC のローカル local
0.0.0.0/0 NAT インスタンスのインスタンス ID
com.amazonaws.ap-northeast-1.s3 エンドポイント ID

Lambda の設定

新しい Lambda を作成します。 以下の設定値を参考に作成してください。

項目
関数コード ※以下を参考に作成
ランタイム Python 3.6
実行ロール Lambda 用に作成した IAM ロールを選択
ネットワーク Lambda 用に作成した Private サブネットを選択
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 を実行する方法を書きたいと思います!