
技術本部Data Intelligence Engineering Unitのスタッフソフトウェアエンジニア藤原です。 Sansan Data Intelligence開発Unitブログリレーのvol.12として、少し趣向を変えて、今日はGoogle Cloudのちょっとだけマニアックだけど便利な機能、IAP(Identity-Aware Proxy)の活用について紹介します。
IAP(Identity-Aware Proxy)とは
IAP を一言でいうと、Google CloudのPaaSやKaaSを使用したアプリケーションに対して、Googleエコシステムでの認証と認可を簡単に追加する機能です*1。
IAPを使うと何がうれしいのか
すぐに使える認証機能を、Cloud RunやGKEに足すことができます。 Sansan Data IntelligenceはGKE上に構築されたOrbitという基盤上で動作しており、GKEに対してIAPを有効にしています。 後述のようにいくつかの制限があるのでお客様向けのエンドポイントにそのまま採用するのは難しい部分もありますが、以下のような高可用性を求められないケースでは問題なく使用できます。
- 内部運用向けのツール(Web画面、CLIから呼び出すAPIエンドポイントなど)に対する認証/認可の追加
- Cloud SchedulerやCloud Pub/Subのpush subscriptionのようなリソースからGKE上のサービスを呼び出す際の認証機構の追加
IAPの構成例
IAPを構成するには以下の要素が必要です。ここで示すのは、Googleアカウントを使用する基本的な構成です。
- 管理APIの有効化
- リソースでのIAPの有効化
- IAPで保護されたリソースに対するアカウントのアクセス許可の設定
- IAPにアクセスするためのOAuth2クライアントの払い出し(通常は 管理クライアント を使えばOK)
詳細な手順は 公式ドキュメント を参照いただくとして、いくつかポイントを紹介します。
リソースでのIAPの有効化
多くの場合、特定のリソース、具体的には前述のPush Subscriptionの対象や、運用ツールで使う対象となるCloud RunやGKEのサービスでのみIAPを有効にしたいはずです。
なんですが、この構成方法が意外とわかりづらい。Cloud Runだと簡単にできる ようですが、GKEでGateway APIを使っている場合、ゲートウェイに紐づいているBackend Serviceに対してIAPを有効にする手順がわかりづらいです。具体的には 公式ドキュメント を参照して、 GCPBackendPolicy 経由でBackend Serviceに対してIAPを有効にする必要があります。Ingress APIを使っている場合は、BackendConfig で有効にします(公式ドキュメント)
IAPで保護されたリソースに対するアカウントのアクセス許可の設定
これは、IAPを有効にしたリソースに対するIAMのアクセス許可として指定します。特別な要件がなければ、roles/iap.httpsResourceAccessor ロールをバインドしてあげればよいです。
たとえば、Terraformの google_iap_web_backend_service_iam_* プロバイダーを使用すると以下のようになります。local は適宜適切な値に置き換えてください。
resource"google_iap_web_backend_service_iam_member" "main" { web_backend_service= "projects/${local.project_id}/iap_web/compute/services/${local.gke_backend_service_name}" role = "roles/iap.httpsResourceAccessor" member= "group:${local.google_group_email}" }
IAPにアクセスするためのOAuth2クライアントの払い出し
IAPはOAuth2のフローに則ります。そのため、Google CloudのOAuth2クライアントに登録したクライアントIDとクライアントシークレットを使用してOAuth2のフローを開始する必要があります。
GKEを使用している場合、GKE側でIAPを構成すると、自動的にWebアプリ用のOAuth2クライアント(管理クライアント )が登録されます(自分で登録して、そのクライアントIDとクライアントシークレットをGKEで構成してもかまいません)。
対話型のWebアプリではなく、CLIツールから実行したい場合、デスクトップアプリ用のOAuth2クライアントを構成すれば、ADC(Application Default Credentials) を使ったユーザー認証も可能になります。gcloud コマンドを使ってるときにあるような、認証をブラウザでやって、認証コードをコンソールに貼りつけ、その後のコマンドはブラウザでログインしたユーザーとして実行されるというあれです。
なお、OAuth2クライアントを作成するには、あらかじめGoogle Cloud Projectの(OAuthの)同意画面を構成しておく必要があります。これは管理クライアントを使う場合も同様です。
JWT Assertionの検証
また、IAPで保護したアプリケーションでは、構成ミスなどによってIAPをバイパスされた場合の対処が必要です。たとえば、JWTベースのOAuth2による認可を実装しているならば、JWTの署名検証と、 aud クレームの検証を行っていると思います(RFC7519)。同様に、IAPは x-goog-iap-jwt-assertion にJWT Assertionという形で署名付きのJWTを挿入します。このJWTは sub、aud、email といった最低限のクレームしかない簡素なものですが、認可を行うには十分です。
IAPのはまりどころ
IAPは便利ですが、少し癖があり、以下のようなはまりどころがあります。
GKEのBackend Serviceとの相性
GKEのAutopilotモードを使っている場合、Load BalancerとPodを結びつけるBackend Serviceの名前はGoogle Cloudによって以下のようなヒューリスティックなルールで決まります*2。
{gateway-name}-{k8s-namespace}-{service-name}-{port}-{hash}
そのため、実際のBackend Serviceの名前がデプロイするまでわからないという欠点があります。一度構成が固まった後に変わることはほとんどないので、許容範囲と言えると思います。
JWT Assertionは必要最低限
Googleのポリシーなのかはわかりませんが、JWT Assertionに含まれるクレームは必要最小限になっています。他のクラウドの認証/認可の仕組みだと、ユーザーの所属するグループの情報が含まれていたりしますが、IAPにはありません。なので、グループに所属しているかどうかの判定は以下のいずれかで行います。
- パスベースのアクセス許可で十分であれば、IAMで制御する。IAPに対するIAMでは、そのIAM conditionで
request.pathプロパティからリクエストのパスを取得できます。特定のパスに対してのみIAMによるアクセス許可を与えることで、簡単なRBACが実現できます。SPAやCLIツールからRESTful APIを呼び出すアークテクチャの場合、これで十分なことも多いかと思います。 - より複雑な制御が必要であれば、Google Cloud Identity APIなどを使い、指定されたユーザーがグループに所属しているかを判定します。しかし、このAPIをサービスアカウントから呼び出すには、以下のいずれかを満たす必要があり、組織のセキュリティポリシーによっては難しいこともあるでしょう。この方式はとらなかったこともありここでこれ以上踏み込みませんが、運用ツールで楽をしたいために使うには大げさでしょう。
- サービスアカウントをGoogleグループに所属させる。
- Google WorkspaceのAPIを呼び出すための権限を付与する。
- 権限制御のデータベースをアプリケーションで作りこむ(そりゃそうだ)。
私たちは開発や運用をシンプルに保つためにIAPを選んだので、パスベースのアクセス制御を行うことにしました。そもそもIAPで保護されたエンドポイントの呼び出し可否をIAMで制御しているところを拡張するだけで済みますし、メンバーシップの管理がGoogleグループのメンバーシップ管理で完結します。アプリケーション内できめ細かなアクセス制御を行いたい場合には機能不足だとか、パス設計に依存するとか、Terraformを触るのは嫌だとかいわれると厳しいですが、無料の機能ですからある程度の妥協は必要です。
OAuth2クライアントの登録はIaCではできない
CLIツールなどから使いたい場合、デフォルトで用意される管理クライアントは、対話型のWebアプリ用の構成になっています。そのため、CLIツール等から使いたい場合、カスタムのOAuthクライアントを作成する必要があります。ところが、IAP用のOAuthクライアントやその前提となる同意画面は2026/3/19以降 IaCやAPIで構成できなくなっています。残念ですね。
基になるGoogle CloudのAPIがクローズされるので、RESTだろうが gcloud コマンドだろうが使えなくなっています。あきらめてCloud Consoleから指定しましょう。普通はプロジェクトで1回適用すればいいはず。
JWT Assertionのaudが静的でない
GKEのBackend ServiceでIAPを有効にした場合、JWT Assertionに含まれる aud の形式は、/projects/{projectNumber}/global/backendServices/{backendServiceID} になっています。プロジェクトIDではなくプロジェクト番号、Backend Service名ではなくBackend Service IDを使うことで、audienceとして、IAPが受け入れた対象のBackend Serviceを一意に特定できるようになっているわけです。
ところで、先ほど、Backend Serviceの名前が変わることがあるといいました。
実は、それに加えて、何らかのネットワーク構成の変更を行った場合、Backend Serviceの名前は変更されないが、Backend ServiceのID(一意に特定するためのランダムな数字)が変わることがあります。
そのため、JWT Assertionでの aud の検証ロジックでの期待値が変わる可能性があります。ここはGoogle CloudのBackend Service APIを呼び出し、Backend Service IDの変更に追随できるようにしたりと、一工夫必要です。
まとめ
IAPは少し癖がありますが、本記事で紹介したような運用ツールに認証をつけるには便利なツールです。コアドメインではない領域については可能な限り既存のマネージド機能を活用し、ありものの機能をうまく使っていくことが重要だと考えています。AIが生成したコードも結局テストして保守しないといけないですしね。作らなくていいものは作らないようにし、本質的な価値に集中したいですよね。 とはいえ、IAPは少々マニアックなところもあり、昨今はやりの生成AIでもうまくいかないことがあります。この記事が生成AIの入力となり、IAPがより活用され、GCP内での開発優先度が上がる、こともあるかもしれないと祈っておきます。
Sansan技術本部ではカジュアル面談を実施しています
Sansan技術本部では中途の方向けにカジュアル面談を実施しています。Sansan技術本部での働き方、仕事の魅力について、現役エンジニアの視点からお話しします。「実際に働く人の話を直接聞きたい」「どんな人が働いているのかを事前に知っておきたい」とお考えの方は、ぜひエントリーをご検討ください。
さらに、採用説明会を開催します!
3月31日(火)に採用説明会を行います。チームの動き方やAI活用の実際など、現場のエンジニアやProduct Ownerから直接お話しします。興味のある方はぜひご参加ください。