GCPのAPIGatewayをAPIキー認証を付けて独自ドメインで公開する設定をTerraformで行う
GCPのAPIGatewayって癖があるうえ、ドキュメントが若干抜けてるところとか、そもそもTerraformでどうすればいいのかみたいなのがすごいわかりにくいのでまとめた
かなり端折っているのでここをとっかかりにTerraform,GCPの各ドキュメントを精読すればわかると思う
前提
- バックエンドはCloud Functions
- APIキー認証を入れる
- 自前のドメイン(カスタムドメイン)でホストする
バックエンドを作る
Cloud Functionsを作って設定します
resource "google_cloudfunctions2_function" "func" {
name = "test-func"
build_config {
...
}
service_config {
ingress_settings = "ALLOW_ALL"
...
}
}
いろいろ省略したけど大事なのは ingress_settings の部分
INTERNAL_ONLY
やINTERNAL_ONLY_AND_GCLB
を設定したい気持ちになるがAPI Gateway経由のリクエストの場合対応していないらしい(これはバックエンドがCloud RunなどでもAPI Gateway経由だとそうらしい)。らしいというあやふやな答えで申し訳ないが調べた感じ駄目なようだし、実際にALLOW_ALL以外だと弾かれるのは確認した
かわりにservice_iam_memberで設定する
resource "google_cloud_run_service_iam_member" "func_auth" {
role = "roles/run.invoker"
member = "allAuthenticatedUsers"
...
}
2世代目のCloud Functionsでこれらの設定を行う google_cloudfunctions2_function_iam_member もあるがこれはバージョンによっては動かないらしい。これも実際にハマったのでこうしている。もしかしたら最新版では解決してるかもしれない
API Gatewayの設定
API Gatewayの設定で必要になるのは大雑把に以下の3つ
- API
- API設定
- APIのゲートウェイ
APIに設定を結びつけ、その結び付けられた設定を参照するゲートウェイを置く、という感じ
APIファイルの作成
APIに読み込ませるAPI構造のファイル。今回はOpenAPIを用いる
swagger: '2.0'
info:
...
paths:
/api/v1/hello:
get:
x-google-backend:
address: ${func_url}
security:
- api_key: []
...
securityDefinitions
api_key:
type: apiKey
name: x-api-key
in: header
公式ドキュメントにもサンプルはあるのでざっくりだが注意事項は security と securityDefinitions の項、あとバックエンドのアドレスはTerraformで入れるため変数を用意する
API定義の作成
続いてAPI定義のTerraform
resource "google_api_gateway_api" "api" {
api_id = "func-api"
...
}
resource "google_api_gateway_api_config" "api_config" {
api = google_api_gateway_api.api.api_id
openapi_documents {
path = "spec.yaml"
contents = base64encode(templatefile("path/to/api.yaml", {
func_url = google_cloudfunctions2_function.func.url
}))
}
...
}
resource "google_api_gateway_gateway" "api_gateway" {
api_config = google_api_gateway_api_config.api_config.id
gateway_id = "gateway-id"
...
}
templatefileを使って ${func_url} をURLのアドレスに置き換えさせる。
ちなみにこの時点でAPIキーを必要とするAPI Gatewayの設定は完了する。ここでデプロイすれば認証が必要なAPIが動いているはず(APIキーの設定をしていないので呼び出せるクライアントが無いが
APIキーの設定
サービスのAPIキー設定への登録
APIキーを設定するが、このAPIキーは当然API Gateway以外で使えていると問題がある。そして別のAPI GatewayのAPIが呼び出せたりしても困る
しかし、公式ドキュメントを見るとどうも設定がgcloudコマンドでしかできない疑惑がある
というわけでこんなふうにして無理やり設定する
resource "null_resource" "enable_api_gateway_service" {
provisioner "local-exec" {
command = <<-EOT
gcloud services enable \
${google_api_gateway_api.api.managed_service}
EOT
}
}
ただ、この google_api_gateway_api.managed_service はドキュメントに書かれておらず、調べている中であとあと見つけたので動くのか若干怪しい。マネージドサービス名を取るため私はこんな事をした
data "external" "api_describe" {
program = [
"gcloud",
"api-gateway",
"apis",
"describe",
"${google_api_gateway_api.api.api_id}",
"--format=json"
]
}
resource "null_resource" "enable_api_gateway_service" {
provisioner "local-exec" {
command = <<-EOT
gcloud services enable \
${data.external.api_describe.result[managedService]}
EOT
}
}
data.externalは結果がJSONでないといけないが、 --formatオプションでそこはとてもいい感じにごまかせる。
APIキーの生成
上記の2つのどちらかで有効になるはずなので、これをそのまま用いてAPIキーを設定する
resource "google_apikeys_key" "api_key" {
restrictions {
api_targets {
service = google_api_gateway_api.api.managed_service
# 上記は google_api_gateway_api.managed_service を用いた場合
}
...
}
nameとかdisplay_nameとかは各々入れてください
ネットワークグループ、URLマップ、ロードバランサなどの設定
ここから先はカスタムドメインを使用するケースの話。
まず前提として、現在(2023-11-06時点)プレビュー版であること、(やってることは同じなので)負荷分散のスタートガイドを理解しておくことが必要。ここの設定を全部Terraform上で記述するイメージ
大雑把には通常のCloud RunやCloud Functionsをカスタムドメインで公開する場合とほぼ同じ。バックエンドサービスの指定にAPI Gatewayを指定するため、ネットワークエンドグループにAPI Gatewayの設定を入れるのが一番の違い
resource "google_compute_region_network_endpoint_group" "api_neg" {
...
network_endpoint_type = "SERVERLESS"
serverless_deployment { # ベータ版
platform = "apigateway.googleapis.com"
resource = google_api_gateway_gateway.api_gateway.gateway_id
}
}
resource "google_compute_backend_service" "api_backend_service" {
...
backend {
group = google_compute_region_network_endpoint_group.api_neg.id
}
}
resource "google_compute_url_map" "api_url_map" {
...
default_service = google_compute_backend_service.api_backend_service.id
}
resource "google_compute_target_https_proxy" "api_https_proxy" {
...
url_map = google_compute_url_map.api_url_map.id
ssl_certifiates = [
... # SSL設定は別途やってここに設定する
]
}
resource "google_compute_global_forwarding_rule" "api_forwarding_rule" {
...
target = "google_compute_target_https_proxy.api_https_proxy.id
ip_address = ... # IPアドレスも別途設定してここに入れる
}
書いてない部分として
- SSL設定 (google_compute_ssl_policy, google_compute_managed_ssl_certificate)
- IPアドレスの確保(google_compute_global_address)
- IPアドレスとドメインの結びつけ(google_dns_record_setなど)
このあたりが設定できればあとは(SSL認証が通れば)テストするのみである。APIキーは下手に持ってくるよりコンソールで確認したほうが安全である
curl -X POST -H "x-api-key: [your api key]" "https://[your domain]/api/v1/hello"
まとめ
GCPのAPI Gatewayって癖があるので大変ですが、まあなんとかなると思います
わかんないこと・間違ったことがあったら教えてください。あとプレビュー版・ベータ版の機能が多めなので、半年もしたらいろいろ変わってるかもしれません。適宜公式ドキュメントを参照しましょう
Discussion