GCP スキルバッジをもらおう 〜７.ユーザー認証: Identity-Aware Proxy

実際にラボを実施した際の記録を載せていきます。
学習の助けになったリンクや、後に見るための学習メモ、ちょっと迷った手順を書いていきます。
あと、コマンドも忘れたくないのでログしていきます。

* 学習の助けになったリンクは、「学習に役立つリンク」にまとめて書いてあります。
* 学習メモは:writing_hand:アイコンをつけてます。
* 迷った手順は箇条書きでつらつら書いてます。
実際の手順については、[Google Cloud Skills Boost](https://www.cloudskillsboost.google/)のラボの手順を参照してください。

クエスト「[Security & Identity Fundamentals](https://www.cloudskillsboost.google/quests/40)」の５つめのラボです。
SkillBoostの始め方は記事「[GCP スキルバッジをもらおう　1Google Cloud Skill Boostをはじめよう](https://qiita.com/orange-tora/items/57d5fe3486f4c7e1a58a)」をどうぞ。

:::note info
この記事は「<a href="https://qiita.com/advent-calendar/2021/gcp-skill-boost-sif">GCP スキルバッジをもらおう！　〜Security & Identity Fundamentals Advent Calendar 2021</a>」の一部として公開しています。
２５日にスキルバッジが獲得できるペースで公開していきます。
:::

##ラボの情報
ラボの名前：[ユーザー認証: Identity-Aware Proxy](https://www.cloudskillsboost.google/focuses/5562?parent=catalog)
所要時間：1時間　レベル：Fundamental　必要なクレジット:5
概要：
AppEngineにアプリを構築して、IAP（Identity-Aware Proxy）を使用してアクセスを制限したり、ユーザー情報を提供したりする。

:writing_hand:Identity-Aware Proxy（IAP)とは？

- BeyondCorpセキュリティモデル（ゼロトラストっぽいやつ）に基づいた設計
- Google Cloud の外部にある HTTP ベースアプリにアクセスできます。
- アプリケーションに送信されたウェブ リクエストをインターセプトし、リクエストを送信したユーザーを Google の ID サービスを使用して認証し、認証されたユーザーからのリクエストのみを通過させます。
- さらに、リクエスト ヘッダーを変更して認証されたユーザーに関する情報を含めることができます。

:writing_hand:IAPによって提供される情報
リクエストヘッダにアクセスしたユーザの情報が提供される
`X-Goog-Authenticated-User-Email`：アクセスしているユーザのメールアドレス
`X-Goog-Authenticated-User-ID`：アクセスしているユーザのID
`X-Goog-IAP-JWT-Assertion`：アクセスしているユーザ検証用の署名データ

##学習に役立つリンク
参考URL：
[Identity-Aware Proxy](https://cloud.google.com/iap/)
[IAP で保護されたリソースへのアクセスの管理 ](https://cloud.google.com/iap/docs/managing-access)
[ユーザIDの取得](https://cloud.google.com/iap/docs/identity-howto)
関連SDK：
[jwt decode](https://cloud.google.com/apigee/docs/api-platform/reference/policies/decode-jwt-policy)

##ラボの実施記録
###コードをダウンロードする
パブリックのバケットからサンプルコードをダウンロードして解凍する。

```
$gsutil cp gs://spls/gsp499/user-authentication-with-iap.zip .
```
```
$unzip user-authentication-with-iap.zip
```
```
$cd user-authentication-with-iap
```

###アプリケーションをデプロイして IAP で保護する
####アプリケーション コードを確認する。
ラボのステップごとに、フォルダにわけてコードが保存されている。
Editorを開いてちょっと確認してみる。
こんな感じ。
![スクリーンショット 2021-12-21 14.08.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365170/23963e88-372e-7547-cd6d-1b516d61cea7.png)

* main.py
アプリケーションコード。Flask ウェブ フレームワークを使用し、テンプレートの内容を利用してウェブリクエストに応答する。
* templates/index.html テンプレートファイル。 
* templates/privacy.html 2 番目のテンプレート ファイルには、簡単なプライバシー ポリシーのサンプルが含まれています。
* requirements.txt アプリケーションが使用する Python ライブラリ（デフォルト以外）がすべてリストされています。
* app.yaml このアプリケーションが Python App Engine アプリケーションであることを Google Cloud に通知するファイル。

####AppEngineへデプロイする

```
gcloud app deploy
```
regionの選択、適当にアジアを選択しました

```
Please choose the region where you want your App Engine application located:

 [1] asia-east1    (supports standard and flexible)
 [2] asia-east2    (supports standard and flexible and search_api)
...
...
Please enter your numeric choice:  1
```

Continueを求められたら`Y`

で、おそらくみなさん、必ず初回は失敗します。
同じコマンドを**もう一度実行すればOK**っす。

```
ERROR: (gcloud.app.deploy) NOT_FOUND: Unable to retrieve P4SA: [service-670719700473@gcp-gae-service.iam.gserviceaccount.com] from GAIA. Could be GAIA propagation delay or request from deleted apps.
```

deployしたアプリを確認します。
リンクをクリックすると、ブラウザタブが起動されて画面が見えます。
`Hello World`と表示さればOK。

```
$gcloud app browse
```
```
Did not detect your browser. Go to this link to view your app:
https://<Project_id>.de.r.appspot.com
```
####IAPを使用してアクセスを制限する
クラウドコンソールで操作します。
ハンバーガー＞Security>Identity-Aware Proxyを開いて、ラボの指示通りに設定、実行する。

CloudShellで、次のコマンドを実行して、Flex API を無効にする。

```
$gcloud services disable appengineflex.googleapis.com
```

:::note info
>App Engine には、さまざまなアプリケーション アーキテクチャ向けに最適化された標準の柔軟な環境があります。 現在、App Engine で IAP を有効にする場合、Flex API が有効になっていると、Google Cloud は Flex サービスアカウントを検索します。 Qwiklabs プロジェクトには、利便性を目的としてすでに有効になっている多数の API が付属しています。 ただし、これにより、サービスアカウントを作成せずに FlexAPI が有効になるという任意の状況が発生します。
:::

CloudConsoleの[Identity-Aware Proxy]でIAPを有効にします。一覧の「IAP」列のボタンをスライドさせます。
Statusが緑になればOK。

![スクリーンショット 2021-12-21 14.40.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365170/8a5bcc1f-55a3-39a7-5e77-61c824e1d6a5.png)

IAPが有効になりました。

####IAP が有効になっていることをテストする
まだ、どのユーザーを許可するか設定をしていないので、この状態にアプリにアクセスをすると、拒否されます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365170/f00e80a7-9dbd-446b-ab59-021f190dbb76.png" width="200" height="200">

###ユーザー ID 情報へアクセスする
####AppEngineにデプロイする
こんどは、２つ目のアプリをデプロイします。

```
$cd ~/user-authentication-with-iap/2-HelloUser
$gcloud app deploy
```

####アプリケーション ファイルを調べる
構成は、１つ目のアプリと同じです。

`main.py`に、ユーザー情報を取得する処理が加えられています。次の箇所。

```
user_email = request.headers.get('X-Goog-Authenticated-User-Email')
user_id = request.headers.get('X-Goog-Authenticated-User-ID')
```

X-Goog-Authenticated-User- ヘッダはIAP によって提供されている。
テンプレートファイルで値を表示できるように渡している。

```
page = render_template('index.html', email=user_email, id=user_id)
```

テンプレートファイル```index.html```の表示箇所。

```
Hello, {{ email }}! Your persistent ID is {{ id }}.
```
####更新されたIAPをテストする

```
gcloud app browse
```

ブラウザでリンクを開いて、更新したAPIをテストする。
ユーザー情報、Emailアドレスが表示されていればOK。
![スクリーンショット 2021-12-21 15.01.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365170/05a9a8ba-076f-b13e-5f77-f27554c6444e.png)

####IAPを無効にする
今度は、IAPを無効にした状態でアプリを確認すると、アクセスはできるけどユーザー情報が表示されなくなっていることがわかる。

###暗号検証を使用する
デジタル署名を検証してIDが有効かどうかユーザーを検証する方法をためす。
IAP によって追加された `X-Goog-IAP-JWT-Assertion` リクエストヘッダーを使用する。

####App Engineをデプロイする
３番目のアプリをデプロイする。
####アプリケーションファイルを確認する
構成は１つめ、２つめとおなじ。
`auth.py`ファイルが追加されている。
暗号で署名された ID 情報を取得して検証するための`user()`メソッドを記述している。
`user()`メソッドの内容：
`assertion`に、`X-Goog-IAP-JWT-Assertion`ヘッダーで取得した署名データを格納して、検証およびデコードする。
検証では、署名されたデータを確認し、Google 提供の公開鍵を使用。
ヘルパー関数の keys() と audience() がそれらの値を収集して返す。

検証済みのメールアドレスと一意の ID 値（サブスクライバーの場合は、sub 標準フィールドに指定）の 2 つのデータ、これらの値を戻す。

```
def user():
    assertion = request.headers.get('X-Goog-IAP-JWT-Assertion')
    if assertion is None:
        return None, None
    info = jwt.decode(
        assertion,
        keys(),
        algorithms=['ES256'],
        audience=audience()
    )
    return info['email'], info['sub']
```

`main.py`と`index.html`は`user()`関数の結果を表示するように変更している。

####暗号検証をテストする
３つ目のアプリをデプロイしてブラウザで確認する。
IAP無効な状態のままで、一度表示を確認して、
IAPを有効にして、再度表示を確認する。
「Hello　Verified　User」画面でアカウントとemailアドレスが表示されていればOK。

##お疲れさまでした
これでラボの手順は終了。
右上のスコア表示が「100/100」になっていることを確認して、「ラボを終了」を押します。