GKEでgatewayリソースを使ってIAPを有効化する

# はじめに
GoogleCloudにはWebに公開されているサービスへのアクセスを、RBACを元に制限するIdentity-Aware Proxyという機能があります。この機能は当然GKE上で動作するサービスにも対応しているのですが、設定の方法がingressを使って公開しているか、gatewayを使って公開しているかで微妙に異なります。
GoogleCloudの公式ドキュメント上に「GatewayをIAPで保護する」みたいなドンピシャの項目があるわけでなく、Gatewayの設定に関する汎用的な記事の中にまとめられている上に、IAPの設定に関するKubernetes上のリソース名が非常にわかりにくく(というかingressで設定するときのリソースとごっちゃにしやすい)、やや混乱したためまとめておきます。
結論から言えばドキュメント通りにやっただけなんですが、ドキュメントの書き方も相当にわかりにくく、一読して全て理解できるものでもないため、後日の自分のためにもまとめておきました。また、特定のパスへのアクセスのみIAPを有効にする方法もまとめます。

# 0. OAuth同意画面を作成する
IAPによるWebサービスへの保護を有効化する前に、OAuth同意画面を作成する必要があります。以下の画面にアクセスし、諸々の設定を行います。
* https://console.cloud.google.com/apis/credentials/consent

また、各項目の詳細は以下のページから確認できます。
* https://support.google.com/cloud/answer/10311615

後でいくらでも変更可能なので、ひとまず必須の項目(アプリ名や連絡先)だけ埋めて作成します。このとき、ユーザの種類を「内部」にするか「外部」にするかだけは考えておいた方がいいです。「内部」にすると自分の組織内のユーザのみがアクセスできるようになります。「外部」にするとGoogleアカウントを有する全てのユーザがアクセスできるようになります。
より正確には、「アクセスできるようにすることができる」と言ったところです。明示的に許可されていないユーザは内部だろうが外部だろうがアクセスできません。

# 1. GKE上に必要なリソースをデプロイする
GKE上にアプリケーションのdeploymentリソース、Gatewayリソース、およびHTTPRouteリソースをデプロイし、インターネットからアクセス可能にします。このとき、HTTPRouteリソースの`.spec.rules`に登録されたbackendごとにIAPの有効・無効を切り替えることができるようになります。なので、特定のパスだけIAPを有効にしたい、あるいは無効にしたい場合は、そのパスに対して個別のruleを作成する必要があります。
例えば`/api`以下へのアクセスのみIAPを有効・無効にしたい場合は以下のようなHTTPRouteリソースを作成する必要があります。
```yaml
kind: HTTPRoute
apiVersion: gateway.networking.k8s.io/v1beta1
metadata:
  name: gateway-sample-route
spec:
  parentRefs:
  - kind: Gateway
    name: gateway-sample
  hostnames:
  - "example.net"
  rules:
  - matches:
    - path:
        value: /
    backendRefs:
    - name: frontend
      port: 80
  - matches: #/apiへのアクセスはこちらに流れる
    - path:
        value: /api
    backendRefs:
    - name: backend
      port: 80
```

# 2. GoogleCloud上でIAPを有効化する
次にGoogleCloud側でIAPの設定を行います。基本的には、以下のページの指示に従いながら作業を進めることになります。
* https://cloud.google.com/iap/docs/enabling-kubernetes-howto?hl=ja#iap-access

ここで注意するべき点として、「IAPアクセス権の設定」という項目の次に「BackendConfigの構成」という項目があるのですが、この項目を**実施しない**でください。GKE上でIAPを有効にする場合の必須項目かのようにも見えますが、この項目はingressでサービスを公開する場合の手順です。この項目を実施してもGatewayを使用する場合は無益なほか、不要なsecretリソースが作成されてしまいます(Gatewayを使用する場合もsecretリソースが必要なのですが、必要な項目が異なります)。

スクリーンショット付きで具体的な手順を見ていきます。

1.Identity-Aware Proxyのページで、対象のサービスの「IAP」トグルスイッチをONにする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144563/f490a0dc-bb3b-b87f-6b1f-25f78727c1ca.png)
2. 同ページで、対象のサービスをクリックすると右側にメニューが開くので、「プリンシパルを追加」をクリックし、対象のユーザやグループに「IAP-secured Web App User」を付与する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144563/7534532e-f358-40be-5f4f-bae450234cf3.png)
3. 同ページで、対象サービスの右側にある三点メニューボタンから「OAuth構成に移動」をクリック(右方向にスクロールする必要がある場合があります)
4. 移動先ページの右側の「クライアントID」と「クライアントシークレット」をコピーして手元に保存する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144563/d7ec8355-5e2d-f553-3dc4-4c129a6d611f.png)

# 3. Gatewayを設定しIAPを有効化する
次にGatewayを設定し、アプリケーションへのアクセスをIAPで保護していきます。基本的にはこのページに従って操作します。
* https://cloud.google.com/kubernetes-engine/docs/how-to/configure-gateway-resources#configure_iap

まず先ほど作成したクライアントシークレットをもとに、sercretリソースを作成します。
```bash
echo -n CLIENT_SECRET > iap-secret.txt
kubectl create secret generic SECRET_NAME --from-file=key=iap-secret.txt
```
このコマンドを実行すると`SECRET_NAME`という名前のsecretリソースが作成されます。secretリソース自体の名前は任意に変更してください。
このsecretは`iap-secret.txt`というkeyでクライアントシークレットを保存しています。キー名がどうあるべきか、公式ドキュメント上に指定がない(というか公式に従うとこのキー名で作成される)ので正直よくわからないのですが、少なくともこのキー名で動作することが確認できています。

次に、GCPBackendPolicyリソースを作成してIAPを有効にします。GCPBackendPolicyリソースのサンプルは以下のとおりです。
```yaml
apiVersion: networking.gke.io/v1
kind: GCPBackendPolicy
metadata:
  name: gateway-sample-httpbin-policy
spec:
  default:
    iap:
      enabled: true
      oauth2ClientSecret:
        name: SECRET_NAME
      clientID: CLIENT_ID
  targetRef:
    group: ""
    kind: Service
    name: backend
```
上記のサンプルのうち、CLIENT_IDは先ほど保存したクライアントIDに置き換えて使用します。また、SECRET_NAMEも先ほど作成したクライアントシークレットを含むsecretリソース名に置き換えます。
上記のリソースをデプロイすると、`targetRef`に指定したリソースにトラフィックがルーティングされた場合、IAPを使用したアクセス制限が有効になります。本記事の例だと、先のHTTPRouteリソースで`/api`へのアクセスは`backend`serviceへルーティングするよう設定しましたので、`/api`へのアクセスがあった場合に認証が要求されます。

実際に`/api`にアクセスすると以下のような画面が表示され、googleへのログインが要求されます。もしログイン済みでアクセス権が付与されている場合、アクセスを継続するか質問されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144563/26be1f17-e969-6d54-4ce5-a7fb0374cdea.png)
(「Spinnaker」に移動と表示されているのは、一番最初のOAuth同意画面の作成時にSpinnakerという名前で構成したためです)
もしアクセス権がないユーザーがアクセスした場合は、403エラーが表示されます。

# まとめ
GoogleCloudには、インターネットに公開したアプリケーションへのアクセスを制限するIdentity-Aware Proxyという機能があり、GKEにも適用することができます。
しかし、適用に必要な手順がIngressを使用するかGatewayを使用するかで異なっているほか、設定手順の資料が散在しており、一貫して説明した資料が存在しません。
このため、本記事ではGatewayリソースを使用してIAPを有効化する手順についてまとめました。GoogleCloud上でIAPを有効化したのち、GCPBackendPolicyリソースでGatewayの動作を設定することで、IAPを有効にすることができます。また、その際特定のバックエンドへのアクセスのみIAPを有効化することができます。