はじめに
API Gateway + Terraform 記事第6弾。
Private の API Gateway は、以下のクラスメソッドの記事の通り、ちゃんと理解して使わないとセキュアではなくなってしまう。
【Developers.IO】Amazon API Gateway プライベート API の「プライベート」を誤解してると、とても危ないという話。
このあたりを理解しながら、Terraform で API Gateway を作っていこう。
API Gateway の REST API は既に構築できている前提とする。
構築方法については、以下の記事を参考にしていただければ。
- 【Qiita】TerraformでAmazon API Gatewayを構築する(基本編)
- 【Qiita】TerraformでAmazon API Gatewayを構築する(ゲートウェイのレスポンス&ステージ詳細編)
AWS
やること
API Gateway を Private 化するのに必要な手順は以下だ。
- VPC エンドポイントとセキュリティグループを作成する
- REST API のエンドポイント設定を
PRIVATE
にして VPC エンドポイントと紐付ける - REST API のリソースポリシーを設定する
VPC エンドポイントとセキュリティグループを作成する
以下のようにリソースを作っておこう。
################################################################################
# VPC Endpoint #
################################################################################
resource "aws_vpc_endpoint" "api_gateway" {
vpc_id = data.aws_vpc.my.id
service_name = data.aws_vpc_endpoint_service.execute_api.service_name
vpc_endpoint_type = "Interface"
subnet_ids = data.aws_subnet_ids.my_vpc.ids
security_group_ids = [aws_security_group.vpc_endpoint.id]
private_dns_enabled = true
}
data "aws_vpc_endpoint_service" "execute_api" {
service = "execute-api"
}
################################################################################
# Security Group #
################################################################################
resource "aws_security_group" "vpc_endpoint" {
name = local.vpcendpoint_sg_name
description = "VPC Endpoint Security Group"
vpc_id = data.aws_vpc.my.id
ingress {
description = "HTTPS"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
セキュリティグループは、API Gateway 向け(HTTPS)なので、443番ポートのインバウンド設定をする。
REST API のエンドポイント設定を PRIVATE
にして VPC エンドポイントと紐付ける
これは簡単、aws_api_gateway_rest_api
を以下のように設定する。
################################################################################
# API Gateway #
################################################################################
resource "aws_api_gateway_rest_api" "private" {
name = local.api_gateway_name
description = "プライベートAPI検証用API Gateway"
endpoint_configuration {
types = ["PRIVATE"]
vpc_endpoint_ids = [aws_vpc_endpoint.api_gateway.id]
}
policy = data.aws_iam_policy_document.private_api.json
}
vpc_endpoint_ids
には先ほど作った VPC エンドポイントのIDを設定すれば良い。
REST API のリソースポリシーを設定する
リソースポリシーは↑の policy
に設定しているものである。
ここでは、作った VPC エンドポイント経由のものを Allow すれば良い。デフォルトを Allow にして不要なものを拒否するというセオリーに従うと、以下のようになる。
data "aws_iam_policy_document" "private_api" {
statement {
effect = "Allow"
principals {
type = "*"
identifiers = [
"*",
]
}
actions = [
"execute-api:Invoke",
]
resources = [
"*",
]
}
statement {
effect = "Deny"
principals {
type = "*"
identifiers = [
"*",
]
}
actions = [
"execute-api:Invoke",
]
resources = [
"*",
]
condition {
test = "StringNotEquals"
variable = "aws:SourceVpce"
values = [
aws_vpc_endpoint.api_gateway.id,
]
}
}
}
これを JSON にエンコードすると以下のようになる。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "*"
},
{
"Sid": "",
"Effect": "Deny",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:SourceVpce": "vpce-xxxxxxxxxxxxxxxxx"
}
}
}
]
}
これで、設定したVPCからのアクセスは、
- https://[APIのID].execute-api.[リージョン].amazonaws.com/[ステージ名]
- https://[APIのID]-vpce-[VPCエンドポイントのID].execute-api.[リージョン].amazonaws.com/[ステージ名]
で通るようになる。当然ながら、CloudShell のような非 VPC リソースやインターネット経由でのアクセスはエラーになる。