API GatewayとVPC Endpointの正しい使い方 3月 13, 2020 9月 15, 2020 APIGateway , AWS , システム設計
API Gateway(プライベート)にVPC Endpointを使った構成を作る時のポイントまとめ
概要
今回、API Gatewayを使ったシステムを設計するにあたり、API Gatewayをプライベートのエンドポイントタイプで構成するか、パブリックのエンドポイントタイプ(リージョン or エッジ最適化)で構成するかを検討した際に改めて調査した結果をアウトプットします。
API Gatewayをプライベートタイプで使用する時の落とし穴
実は昨年担当した案件でも同じ悩みがありました。当初API Gateway をプライベートで構成しようと試みましたが、VPC Endpoint を設定したらなぜか繋がらない。その後構成見直しが入り、課題と思われたAPI Gateway の機能は廃止されたため、プライベートは要注意だという印象を持ったまま腹落ちしない状況でした。今日は、改めて当時の課題を再現しつつ、解決策を整理させていただきます。
API Gateway のプライベートタイプを使う時にハマる落とし穴があります。システム構成にAPI Gatewayのプライベートを配置するとVPC Endpointを設定しなければVPC内のサーバー(EC2、Lambdaなど)からアクセスできない。但し、システム構成にパブリックとプライベートが混在する環境では、VPC Endpointを設定した途端パブリックにアクセスできなくなるというもの。(システム構成のイメージは下記となります)
結論は
僕が考える結論としては、システムがアクセスするAPI Gatewayをすべてパブリックの構成に統一できるなら、すべてパブリックの構成とするのが安全です。(パターン1)
システムにAPI Gatewayのパブリック、プライベートが混在する構成を必要とするならば、VPC内のリソースからパブリック、プライベート両方のAPI にアクセスできるようにVPC Endpointを作成し、Private DNSの設定変更を行います。但し、API にアクセスする際のヘッダにパラメータを付加するといった注意も必要です。(パターン4) →そのため、混在する環境では少し難易度が上がります。
詳細は、以降のAPI GatewayとVPC Endpointの正しい使い方をお読みください。
API GatewayとVPC Endpointの正しい使い方
API Gatewayのパブリックとプライベートの構成を作りながら、設定方法と、動作を順を追って説明します。
パターン1:API Gateway(Public) + VPC Endpointなし
API Gatewayの作成
先ず、API Gatewayのパブリックのエンドポイントタイプを使用した構成を作ります。
API Gatewayを作成時に、非プライベートを選択します。
API name、Descriptionを入力し、Endpoint typeにエッジ最適化を選択します。以降の操作は、こちら の記事を参考にモックで構わないのでAPIのデプロイまで行います。
API疎通確認
デプロイされたAPI のエンドポイントに対して、外部のクライアント、VPC内に配置されたサーバーから疎通確認を行います。どちらも疎通はOK となりました。
← 外部のクライアント
niikawa@niikawa1:~$ curl https://xxxxxx8f43.execute-api.ap-northeast-1.amazonaws.com/dev
{"statusCode": 200, "body": "\"Hello World!\""}
← VPC内のプライベートサブネットに配置されたEC2
[ec2-user@niikawa-test-ec2 ~]$ curl https://xxxxxx8f43.execute-api.ap-northeast-1.amazonaws.com/dev
{"statusCode": 200, "body": "\"Hello World!\""}
パターン1のまとめ
API Gatewayはパブリックアクセスとなるため、外部のクライアントからのアクセスも、VPC内に配置されたサーバーからのアクセスも可能です。※下記構成ではEC2をプライベートサブネットに配置しているため、EC2はNAT Gatewayからインターネットを経由して、API Gatewayにアクセスします。
パターン2:API Gateway(Public) + VPC Endpointあり
VPC Endpointの作成
次に、パターン3 の準備を兼ね、VPC Endpointを作成します。
VPCのコンソールを開き、Endpointsを選択します。
Service categoryにAWS services、Service Nameにcom.amazonaws.ap-northeast-1.execute-api(東京リージョンの場合)を選択します。
VPC、Subnetsを選択します。VPC Endpointを利用してプライベートアクセスすることが目的のため、プライベートサブネット(Internet Gatewayなし)を選択とします。
ここが重要なポイントとなりますが、ここではEnable DNS nameを有効とします。
Security Groupを選択します。
Policyは、Full Accessのままとします。
VPC Endpointが作成できました。Enable DNS nameは、ちゃんと有効になっていますね。
API疎通確認
以前の手順でデプロイしたパブリックのAPI のエンドポイントに対して、VPC Endpoint作成後に動作がどの様に変わったかを確認します。外部のクライアント、VPC内に配置されたサーバーからそれぞれ疎通確認を行います。外部のクライアントからの疎通はOK ですが(こちらは構成が変わっていないため、当たり前)、VPC内に配置されたサーバーからの疎通はNG に変わりました。
← 外部のクライアントからパブリックAPI
niikawa@niikawa1:~$ curl https://xxxxxx8f43.execute-api.ap-northeast-1.amazonaws.com/dev
{"statusCode": 200, "body": "\"Hello World!\""}
← VPC内のプライベートサブネットに配置されたEC2からパブリックAPI
[ec2-user@niikawa-test-ec2 ~]$ curl https://xxxxxx8f43.execute-api.ap-northeast-1.amazonaws.com/dev
{"message":"Forbidden"}
パターン2のまとめ
VPCにVPC Endpointを作成後は、VPC内のリソースからAPI GatewayへのアクセスはVPC Endpoint経由となるため、VPC内に配置されたサーバーからアクセスができなくなりました。
パターン3:API Gateway(Private) + VPC Endpointあり + Private DNS有効化設定
API Gatewayの作成
次に、API Gatewayのプライベートのエンドポイントタイプを使用した構成を作ります。
API Gatewayを作成時に、プライベートを選択します。
API name、Descriptionを入力し、Endpoint typeにプライベートを選択します。以降の操作は、こちら の記事を参考にモックで構わないのでAPIのデプロイまで行います。(Resource Policyの設定は後述します)
API作成後、左側のメニューからResource Policyを選択します。Exapmlesの「Source VPC Whitelist」を例に、ポリシーを記述します。
以下、VPCホワイトリストのリソースポリシーサンプルです。ソースVPCを条件とすることで、他のVPCからのアクセス(VPC Endpoint以外のアクセス)を拒否します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:ap-northeast-1:111111111111:xxxxxxrnwh/*"
},
{
"Effect": "Deny",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:ap-northeast-1:111111111111:xxxxxxrnwh/*",
"Condition": {
"StringNotEquals": {
"aws:sourceVpc": "vpc-01234567890123456"
}
}
}
]
}
API疎通確認
新たにプライベートにデプロイされたAPI のエンドポイントに対して、外部のクライアント、VPC内に配置されたサーバーから疎通確認を行います。外部のクライアントからプライベートAPI への疎通はNGであり(こちらは当たり前)、VPC内に配置されたサーバーからプライベートAPI への疎通はOKとなりました。
次のパターン4は、プライベートAPI への疎通と共に、現在エラーとなっているVPC内に配置されたサーバーからパブリックAPI への疎通を通す方法です。
← 外部のクライアントからパブリックAPI
niikawa@niikawa1:~$ curl https://xxxxxx8f43.execute-api.ap-northeast-1.amazonaws.com/dev
{"statusCode": 200, "body": "\"Hello World!\""}
← 外部のクライアントからプライベートAPI
niikawa@niikawa1:~$ curl https://xxxxxxrnwh.execute-api.ap-northeast-1.amazonaws.com/dev
curl: (6) Could not resolve host: xopezgrnwh.execute-api.ap-northeast-1.amazonaws.com
← VPC内のプライベートサブネットに配置されたEC2からパブリックAPI
[ec2-user@niikawa-test-ec2 ~]$ curl https://xxxxxx8f43.execute-api.ap-northeast-1.amazonaws.com/dev
{"message":"Forbidden"}
← VPC内のプライベートサブネットに配置されたEC2からプライベートAPI
[ec2-user@niikawa-test-ec2 ~]$ curl https://xxxxxxrnwh.execute-api.ap-northeast-1.amazonaws.com/dev
{"statusCode": 200, "body": "\"Hello World!\""}
パターン3のまとめ
外部のクライアントからはパブリックのAPI Gatewayにはアクセスできますが、プライベートのAPI Gatewayにはアクセスできません。VPC内のリソースからAPI GatewayへのアクセスはVPC Endpoint経由となるため、VPC内に配置されたサーバーからパブリックのAPI Gatewayにはアクセスできませんが、プライベートのAPI Gatewayにはアクセスできます。
パターン4:API Gateway(Private) + VPC Endpointあり + Private DNS無効化設定
VPC EndpointのPrivate DNS設定を無効化
VPC EndpointのPrivate DNS設定は、後からでも変更できます。VPCのコンソールを開き、Endpointsを選択します。
以前の手順で作成したAPI GatewayのVPC Endpoint を選択します(東京リージョンの場合、com.amazonaws.ap-northeast-1.execute-apiですね)。
[Actions]から[Modify Private DNS names]を選択し、"Enable Private DNS Name" を無効にします。
API疎通確認
以前の手順でデプロイしたパブリックのAPI のエンドポイントに対して、VPC EndpointのPrivate DNS設定変更後に動作がどの様に変わったかを確認します。外部のクライアント、VPC内に配置されたサーバーからそれぞれ疎通確認を行います。
パターン2でエラーとなったVPC内に配置されたサーバーからパブリックAPI への疎通はOKに変わり、プライベートの疎通もOKとなっています(但し、ヘッダの追加が必要です↓ )。
実はPrivate DNS設定を無効にすることで、パブリックのAPI にアクセスできる一方、プライベートのAPI の名前解決ができません。APIのエンドポイントにプライベート用のFQDN を使用します(先頭にVPC EndpointのIDが付加されており、コンソールのVPC Endpointの情報からも確認可)。また、curlにはx-apigw-api-idヘッダ を追加で指定する必要があります。
次の形式です。x-apigw-api-id ヘッダー を使用して、プライベート API にアクセスできます。
curl -v https://{public-dns-hostname}.execute-api.{region}.vpce.amazonaws.com/test -H’x-apigw-api-id:{api-id}’
← 外部のクライアントからパブリックAPI
niikawa@niikawa1:~$ curl https://XXXXXX8f43.execute-api.ap-northeast-1.amazonaws.com/dev
{"statusCode": 200, "body": "\"Hello World!\""}
← 外部のクライアントからプライベートAPI
niikawa@niikawa1:~$ curl https://XXXXXXrnwh.execute-api.ap-northeast-1.amazonaws.com/dev
curl: (6) Could not resolve host: xopezgrnwh.execute-api.ap-northeast-1.amazonaws.com
← VPC内のプライベートサブネットに配置されたEC2からパブリックAPI
[ec2-user@niikawa-test-ec2 ~]$ curl https://XXXXXX8f43.execute-api.ap-northeast-1.amazonaws.com/dev
{"statusCode": 200, "body": "\"Hello World!\""}
← VPC内のプライベートサブネットに配置されたEC2からプライベートAPI
[ec2-user@niikawa-test-ec2 ~]$ curl https://vpce-XXXXXXXXXXXXXb3a0-XXXXXXXX.execute-api.ap-northeast-1.vpce.amazonaws.com/dev -H'x-apigw-api-id:XXXXXXrnwh'
{"statusCode": 200, "body": "\"Hello World!\""}
パターン4のまとめ
パターン4 の方法を利用することで、パブリックのAPI、プライベートのAPI が混在する環境のシステムにおいて、VPC内に配置されたサーバーからパブリック、プライベートのどちらのAPI にもアクセスすることができました。
各システムの環境や要件に合わせて、構成やVPC Endpointの使用可否を選択してください。
参考資料
本件に関する情報が記載されているドキュメントをご紹介します。