プライベートAPIGatewayを異なるアカウントから呼び出す
サーバーレスを勉強中に、classmethodさんの記事を見て、え?そうなの?と思って試してみた。
イメージは以下の感じで、プライベートAPIGatewayを作成したアカウントAからは勿論、異なるアカウントBからもアクセスさせたい。
ついでにLambdaからのプライベートAPIGateway呼び出しも試してみた
アカウントBのRoute53にアカウントAで作成したAPIGatewayのエンドポイントを、ホストゾーンとレコード追加して
アカウントBで作成したVPCエンドポイントのDNS名と紐づけることで、アカウントBからアカウントAで作成したプライベートAPIGatewayの呼び出しが可能となりました。
VPCエンドポイントで「*.execute-api.ap-northeast-1.amazonaws.com」がワイルドカードで登録されるので
Route53を登録しなくてもプライベートAPIGatewayのURL形式であればエンドポイントに接続されました。
アカウントAでの環境準備
各リソースの作成手順は割愛します、重要な設定値のみです。
APIGatewayの作成
以下の設定値でAPIGatewayを作成。
・エンドポイントタイプ:プライベート
・ID:xxxx
・メソッド:GET
・タイプ:Lambda関数
・ドメイン:https://xxxx.execute-api.ap-northeast-1.amazonaws.com
・ステージ:test
・リソースポリシー:下記の通りvpcidを指定してアクセス許可を設定(参考:https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-vpc-endpoint-policies.html)
事前にアカウントBのVPCIDも登録しています。
※プライベートAPI Gatewayの送信先IPアドレス制限は意味がない(0.0.0.0/0をDenyしてもリクエストできます。)→VPCで送信元偽れますもんね、よく考えたらそらそうか・・・。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": "*", "Action": "execute-api:Invoke", "Resource": "arn:aws:execute-api:ap-northeast-1:1234567890:xxxx/*" }, { "Effect": "Deny", "Principal": "*", "Action": "execute-api:Invoke", "Resource": "arn:aws:execute-api:ap-northeast-1:1234567890:xxxx/*", "Condition": { "StringNotEquals": { "aws:SourceVpc": [ "vpc-xxxxa", "vpc-xxxxb" ] } } } ] }
APIGatewayから呼び出されるLambda関数
def lambda_handler(event, context): return { 'isBase64Encoded': False, 'statusCode': 200, 'headers': {}, 'body': {"message": "Hello from AWS Lambda"} }
APIGatewayを呼び出すLambda関数
エンドポイント経由でアクセスするため、VPC、サブネット、セキュリティグループを指定して作成します。
指定したリソースは下記として説明します。
・VPC:vpc-xxxxa
・サブネット:Lambda-subnet
・セキュリティグループ:Lambda-sg
import urllib.request import json def lambda_handler(event, context): url = "https://xxxx.execute-api.ap-northeast-1.amazonaws.com/test/" response = urllib.request.urlopen(url) content = json.loads(response.read().decode('utf8')) return content
VPCエンドポイントの作成
VPCに設置したLambdaがプライベートAPIGatewayに到達できるようにVPCエンドポイントを作成します。
VPCエンドポイントは、Lambdaからhttpsのインバウンドを許可する必要があります。
指定したリソースは下記になります。
・サービス名:com.amazonaws.ap-northeast-1.execute-api
・VPC:vpc-xxxxa
・サブネット:Lambda-subnet
・セキュリティグループ:Vpcendpoint-sg(Lambda-sgをソースとしたhttpsのインバウンドを許可する)
アカウントAでの実行結果
アカウントBでの環境準備
各リソースの作成手順は割愛します、重要な設定値のみです。
APIGatewayを呼び出すLambda関数
これかアカウントAとソースコードは変わりません。
指定したリソースは下記として説明します。
・VPC:vpc-xxxxb
・サブネット:Lambda-subnet
・セキュリティグループ:Lambda-sg
import urllib.request import json def lambda_handler(event, context): url = "https://xxxx.execute-api.ap-northeast-1.amazonaws.com/test/" response = urllib.request.urlopen(url) content = json.loads(response.read().decode('utf8')) return content
VPCエンドポイントの作成
VPCに設置したLambdaがプライベートAPIGatewayに到達できるようにVPCエンドポイントを作成します。
VPCエンドポイントは、Lambdaからhttpsのインバウンドを許可する必要があります。
指定したリソースは下記になります。
・サービス名:com.amazonaws.ap-northeast-1.execute-api
・VPC:vpc-xxxxb
・サブネット:Lambda-subnet
・セキュリティグループ:Vpcendpoint-sg(Lambda-sgをソースとしたhttpsのインバウンドを許可する)
アカウントBでの実行結果
以上でアカウントBの環境は整ったので、「APIGatewayを呼び出すLambda関数」を実行します。
実行結果は以下の通りで、アカウントAと同様にLambdaからAPIGatewayが呼び出せました。
まとめ
classmethodさんの記事は正しかった。
AWS のプライベートネットワーク内においては特に制限をしない限り、誰でもアクセスできる API Gateway です。←まさにこれでした。
その他
LambdaからAPIGatewayの呼出し方は下記のスクリプトでも良いかもメモ
import urllib3 import json def lambda_handler(event, context): url = "https://xxxx.execute-api.ap-northeast-1.amazonaws.com/test" http = urllib3.PoolManager() response = http.request("GET",url) content = json.loads(response.data.decode("utf-8")) return content