12
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

team-lab-engineeringAdvent Calendar 2019

Day 12

Cloud IAP を使って、GKEのアプリケーションにGoogle認証をかます

Last updated at Posted at 2019-12-12

Cloud IAPとは、Cloud Identity-Aware Proxyの略で、GCPのサービス(?)の1つ。
Cloud IAP を使用すると、HTTPS によってアクセスされるアプリケーションの一元的な承認レイヤを確立できるため、ネットワークレベルのファイアウォールに頼らずに、アプリケーションレベルのアクセス制御モデルを使用できるらしい。
ここにかいてあった。→ Cloud Identity-Aware Proxy の概要

イメージとしては、Google認証によるアクセス制御を受け持ってもらえる感じかなと。
Googleアカウントだけでなく、Googleグループの単位でアクセス制限がかけられるのが便利そう。

iap-load-balancer.png

実際に動かす

実際に簡単なアプリケーションをGKEにデプロイして、IAPによるアクセス制限をかけてみます。
https://github.com/grandcolline/iap-test

  • GCPプロジェクトがあること
  • ローカルPCでgcloudでアクセスできること
  • ローカルPCにkubectlがインストールされていること
  • Cloud DNSでドメイン管理をおこなっていること

が前提条件です><

準備1) ソースコード取得

コードをここに準備しました。これをクローンしちゃってください。
https://github.com/grandcolline/iap-test

$ git clone git@github.com:grandcolline/iap-test.git
$ cd iap-test

準備2) GKEクラスタの作成とappのデプロイ

GCPの管理画面から、GKEのクラスタを作成します。
自分は、iap-test-clusterというクラスターを作成しました。
クラスター名・ノード数などは適当で大丈夫です。
https://console.cloud.google.com/kubernetes/list
スクリーンショット 2019-12-10 21.02.41.png

コマンドを叩いて、credentialの取得とappのデプロイを行います。
GKEクラスターにappという簡単なAPIサーバのDeploymentとServiceを反映します。

# クラスタ一覧取得
$ gcloud container clusters list
NAME              LOCATION           MASTER_VERSION  MASTER_IP       MACHINE_TYPE  NODE_VERSION    NUM_NODES  STATUS
iap-test-cluster  asia-northeast1-a  1.13.11-gke.14  35.187.203.144  g1-small      1.13.11-gke.14  3          RUNNING

# クラスタのcredentialを取得
$ gcloud container clusters get-credentials \
    iap-test-cluster \
    --region=asia-northeast1-a

# credential確認
$ kubectl config get-contexts
CURRENT   NAME                                                      CLUSTER                                                   AUTHINFO                                                  NAMESPACE
          docker-desktop                                            docker-desktop                                            docker-desktop
          docker-for-desktop                                        docker-desktop                                            docker-desktop
*         gke_grandcolline-dev_asia-northeast1-a_iap-test-cluster   gke_grandcolline-dev_asia-northeast1-a_iap-test-cluster   gke_grandcolline-dev_asia-northeast1-a_iap-test-cluster

# 反映
$ kubectl apply -f k8s/app.yml

Workloadタブで確認できるはずです。
https://console.cloud.google.com/kubernetes/workload
スクリーンショット 2019-12-10 20.59.59.png

準備3) Ingressの作成

ここでは、CloudDNSに使えるドメインがあることが前提です。
まずは、VPC Network > External IP addressから、静的IPを取得します。
typeをglobalで作らないとIngressで使えないので注意です。
https://console.cloud.google.com/networking/addresses/list
自分はiap-test-ingressという名前で取得しました。
スクリーンショット 2019-12-10 22.26.20.png

NetWork Service > CloudDNSで、先ほど取得した静的IPにドメインを割り振ります。
自分は、iap-test.dev.grandcolline.com というドメインを割り振りました。
https://console.cloud.google.com/net-services/dns/zones
スクリーンショット 2019-12-10 22.32.45.png

コマンドからIngressを反映します。

# 設定ファイルの書き換え
#  1. ingress.global-static-ip-nameを自分の取得した静的IP名に書き換える
#  2. domainを自分の取得したドメインに書き換える
$ vim k8s/ingress.yml

# 反映
$ kubectl apply -f k8s/ingress.yml

少し待つと、Load balancerが作成され、https://<ドメイン>/helloでアクセスできるようになります。
スクリーンショット 2019-12-10 22.47.20.png

以上で、準備は終わりです。
ここから、Cloud IAP for GKE の有効化を参考にCloudIAPの設定をしていきます。

OAuth同意画面の構成

OAuth同意画面にアクセスして、

  • アプリケーション名
  • サポートメール

を入力します。

スクリーンショット 2019-12-12 4.33.37.png

IAPの設定

セキュリティ>Identity-Aware Proxy にアクセスして、
https://console.cloud.google.com/security/iap
default/appのIAPをオンにします。
スクリーンショット 2019-12-12 4.43.48.png

Googleの公式の手順では、OAuth認証情報の作成なども必要そうですが、
上記の対応をすることで、勝手にOAuth認証情報も作成されます(自分はされました)
https://console.cloud.google.com/apis/credentials

この状態で、先ほどのhttps://<ドメイン>/helloにアクセスすると、Googleログインが求められ、Access Deniedのページへと飛ばされます!

スクリーンショット 2019-12-12 4.51.30.png

うまく、IAPが挟まったようです!

アクセス権限を与えるためには、
セキュリティ>Identity-Aware Proxy から、
https://console.cloud.google.com/security/iap
default/appを選択して、ADD MEMBERから、IAP-secured Web App Userの権限でアプリにアクセス可能なアカウントを追加します。
ここには、

のアカウントが使えるそうです。
スクリーンショット 2019-12-12 4.56.59.png

改めて、https://<ドメイン>/helloにいくと、アクセスができます!

ユーザ情報を取得する

ユーザのIDの取得を参考に、アプリ側でユーザ情報を取得します。
Cloud IAPはユーザーのIDを以下のHTTPヘッダーでバックエンドサービスに渡します。

ヘッダー名 説明 値の例
X-Goog-Authenticated-User-Email ユーザーのメールアドレス accounts.google.com:example@gmail.com
X-Goog-Authenticated-User-ID ユーザーの永続的で一意な ID accounts.google.com:userIDvalue

ですので、アプリ側でヘッダーを取得するようなコードを書くことで、ユーザ情報を取得できます。

func userHandler(w http.ResponseWriter, r *http.Request) {
	mail := r.Header.Get("X-Goog-Authenticated-User-Email")
	id := r.Header.Get("X-Goog-Authenticated-User-ID")
	fmt.Fprint(w, "ID: "+id+"\nmail: "+mail)
}

こちらのハンドラーをhttps://<ドメイン>/userにルーティングしてあるので、こちらのURLにアクセスすると、IDとメールアドレスが取得できるかと思います!

ただ、こちらの方法ではCloud IAPによって承認されていることを確認はできていないので、注意は必要です。

IAPの承認を検証する

署名済みヘッダーによるアプリの保護を参考に、承認を検証します。

Cloud IAPを通る際に、X-Goog-*ヘッダーを削除するそうですが、もしIAPをバイパスする場合には、攻撃者はX-Goog-Authenticated-User-{Email,ID}を偽造することができます。
そこで、X-Goog-IAP-JWT-Assertionヘッダーに入っている、JWTを使って、IAPによって承認されていることを検証します。

まず、Identity-Aware Proxyから Signed Header JWT Audience を取得します。
スクリーンショット 2019-12-12 5.34.30.png

環境変数に追加して、アプリケーションを再デプロイをします。

# 環境変数のAUDIENCEを、取得したSignedHeaderJWTAudienceに書き換え
$ vi k8s/app.yml

# 変更を反映
$ kubectl apply -f k8s/app.yml

デプロイが完了したら、https://<ドメイン>/authにアクセスをして、ユーザ情報が取得できていれば、検証がうまくできています。

アプリケーションで行っている処理は単純です。

 1. ヘッダーのX-Goog-IAP-JWT-Assertionから、JWTを取り出す。
 2. Googleが公開している公開鍵から、鍵を取得する。
 3. 鍵を使って、検証。ここで期限などもチェック。
 4. Audienceが正しいかを確認。

を行います。

詳細は、署名済みヘッダーによるアプリの保護を参考にしてください。
コードはこんな感じです。何かおかしな場所がありましたら、指摘いただければ幸いです。

func authHandler(w http.ResponseWriter, r *http.Request) {
	// get token
	tokenStr := r.Header.Get("X-Goog-IAP-JWT-Assertion")
	audience := os.Getenv("AUDIENCE")
	claim := &iapClaim{}

	_, err := jwt.ParseWithClaims(tokenStr, claim, func(token *jwt.Token) (interface{}, error) {
		// check signing method
		if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok {
			return nil, errors.New("Unexpected signing method")
		}
		// get public key
		if _, ok := token.Header["kid"].(string); !ok {
			return nil, errors.New("not found kid")
		}
		rawKey, err := fetchPublicKey(token.Header["kid"].(string))
		if err != nil {
			return nil, err
		}
		key, err := jwt.ParseECPublicKeyFromPEM(rawKey)
		if err != nil {
			return nil, err
		}
		return key, nil
	})
	if err != nil {
		fmt.Fprint(w, "Validation: NG"+"\nerror: "+err.Error())
		return
	}

	if claim.Audience != audience {
		fmt.Fprint(w, "Validation: NG"+"\nerror: invalid audience")
		return
	}

	fmt.Fprint(w, "Validation: OK"+"\nID: "+claim.Subject+"\nmail: "+claim.Email)
}

まとめ的ななにがし

自分は、今までCloud IAPを使ったことがなかったのですが、簡単にGoogle認証をかませてすごいと思いました。
一旦、動くところまではできましたが、サービスアカウントでの使い方や、結局backend-configの設定をしなくていいかなど、わかっていない部分もたくさんあります😨

ただ、Googleグループなどでの制御もできるので、ステージング環境などのアクセス制限などでも使い勝手が良さそうだなぁという印象です。もっと勉強して、今後も積極的に使っていきたいです!!

12
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?