🏯

GCP API Gatewayでpushサブスクリプションを認証するためのOpenAPI v2設定ガイド

2024/11/03に公開

はじめに

IoTチームの高原です。

GCP上で今回やりたいことは下図です。

概要図

Pub/SubのpushサブスクリプションでAPI GatewayにメッセージをHTTPで送信します。
このときpush認証を有効にして、API GatewayでJWT認証をさせます。
API GatewayはOpenAPI v2のドキュメントで設定するため、その手順をご紹介します。

ハマりポイント

最初はAPI Gatewayのサービス間認証を読みながら設定しました。
するとOpenAPI v2には下記を記述するよう書かれています。

securityDefinitions:
  DEFINITION_NAME:
    authorizationUrl: ""
    flow: "implicit"
    type: "oauth2"
    x-google-issuer: "SA_EMAIL_ADDRESS"
    x-google-jwks_uri: "https://www.googleapis.com/robot/v1/metadata/x509/SA_EMAIL_ADDRESS"

なのでpushサブスクリプションからサービスアカウントをissuerとしたJWTを受信すると思い込んでいました。

{
  "iss": "push-example@project.iam.gserviceaccount.com" // pushサブスクリプションに付与したサービスアカウント
  // 他は省略
}

が、実際に上記内容を設定すると、API Gatewayは401を返すばかりで認証はしてくれません。
実際にpushサブスクリプションから受信したJWTをログ出力してデコードしてみると下記のような内容でした。

{
  "iss": "https://accounts.google.com",
  "email": "push-example@project.iam.gserviceaccount.com" // pushサブスクリプションに付与したサービスアカウント
  // 他は省略
}

よく見たらpushサブスクリプションが作成するJWTの内容はここに記載があります。
API GatewayでpushサブスクリプションのJWTを認証させるための試行錯誤がここから始まりました。

設定

下記 2 つのリソースを設定します。

  • API Gateway
  • Pub/Sub pushサブスクリプション

API Gateway

OpenAPI v2のドキュメントに下記のセキュリティ定義を記述します。

securityDefinitions:
  jwt_auth:
    type: oauth2
    flow: implicit
    authorizationUrl: ""
    x-google-issuer: https://accounts.google.com
    x-google-jwks_uri: https://www.googleapis.com/oauth2/v3/certs
    x-google-audiences: https://example-xxxxxxxx.an.gateway.dev/api/v1/example # API GatewayのURL+APIパス

そして、認証対象APIにsecurityスキーマを追記します。

paths:
  /api/v1/example:
    post:
      summary: サンプルAPI
      security:
        - jwt_auth: []

このドキュメントをAPI Gatewayの構成に適用してデプロイすればAPI Gatewayは設定完了です!

上記設定の理由

認証を有効にしたpushサブスクリプションからは下記のJWT(デコード済)を受信します。

{
  "aud": "https://example-xxxxxxxx.an.gateway.dev/api/v1/example", // 指定したpushエンドポイント
  "azp": "xxxxxxxxxxxxxxxxxxxxx",
  "email": "push-example@project.iam.gserviceaccount.com", // pushサブスクリプションに付与したサービスアカウント
  "email_verified": true,
  "exp": 1728548339,
  "iat": 1728544739,
  "iss": "https://accounts.google.com",
  "sub": "xxxxxxxxxxxxxxxxxxxxx"
}

"iss"にissuer(JWTの発行者)が記載されています。
それをOpenAPIドキュメントの"x-google-issuer"に設定します。
そしてこのJWTの署名検証に使う公開鍵URIを"x-google-jwks_uri"に設定します。
"aud"には指定したpushエンドポイントが記載されているので、その値をOpenAPIドキュメントの"x-google-audiences"に設定します。

Pub/Sub pushサブスクリプション

Pub/Sub用サービスエージェント

iam.serviceAccountTokenCreator ロールを service-{PROJECT_NUMBER}@gcp-sa-pubsub.iam.gserviceaccount.com に付与します。
この付与によってPub/SubがJWTを作成するために必要なiam.serviceAccounts.getOpenIdToken権限を得ることができます。

pushサブスクリプションに付与するサービスアカウント

そしてpushサブスクリプション用にサービスアカウントを作成します。
iam.serviceAccounts.actAs権限が必要なため、ロールroles/iam.serviceAccountUserを付与してください。

pushサブスクリプション作成

そして今まで設定した項目を元にpushサブスクリプションを作成します。

  • pushエンドポイント
    • API Gateway+APIパス
  • push認証
    • 有効に設定
  • サービスアカウント
    • 上記で作成したもの

上記設定で、無事にpushサブスクリプションからAPI Gatewayの認証が通ります!

今回の学び

公式ドキュメントに書いてあるから、きっとこうなんだ!と決めつけて設定だけいじって試行錯誤する時間が長かったです。
もっと早く実際のJWTをダンプして内容を確認したり、JWTの仕様を理解してAPI Gateway用OpenAPI の拡張を読み込んでいれば問題解決に至るのがもっと早かったと思います。
クラウドの使い方ばかりに習熟するのではなく、もっと基礎的な技術をベースに取り組んだ方がエンジニアとして強くなれると思いました。

参考記事

Luup Developers Blog

Discussion