【GitHub x GCP】Workload Identity Federation使ってみた

# はじめに

以前[参加記](https://qiita.com/EbiTT/items/6775b85f6d555db21f58)を書いたCARTA HOLDINGSの夏インターン「Treasure」の期間が終わった後もチームで開発を続けていました。その中でGithub ActionsからCloud Runへコンテナを自動デプロイしていたところ運良く新しい技術を使う機会に恵まれたので今回はその話を書こうと思います。記事の内容とは関係ありませんがTreasureまじで最高です。学生さんは1度は応募してみることをお勧めします✨

# 何使ったの？

Workload Identity FederationというGCPが提供する***認証方法***を使ってみました。詳しくはGCP公式が出してる[このプレス](https://cloud.google.com/blog/ja/products/identity-security/enabling-keyless-authentication-from-github-actions)とかGitHub公式が出してる[このブログ記事](https://github.blog/changelog/2021-10-27-github-actions-secure-cloud-deployments-with-openid-connect/)なんかが参考になると思いますがざっくり概要だけまとめて見ると...

> GitHub Actions ワークフローで Google Cloud 上でのリソースの読み取りや変更が必要な場合（Artifact Registry へのコンテナの公開や Cloud Run での新しいサービスのデプロイなど）、最初に認証をしなくてはなりません。

これはそのままです。GitHub Actions上でCloud Runにコンテナデプロイしたりする時(要するにクラウド上のリソースを操作したい時)には予め認証が必要です。でその認証方法がこれまではあんまりイケてませんでした。WIFはそれに代わる***新たな認証方法***というわけです。ではそのイケてない認証方法というのは...

> GitHub Actions から Google Cloud への従来の認証では、有効期間が長い JSON サービス アカウント キーのエクスポートと保存が必要でした。

***サービスアカウント***です。AWSでいうところのIAM Roleに当たるもので人間以外(今回はGithub Actions)がプログラムの中からクラウドのリソースにアクセスしたい時に使われるものですね。詳しい解説が欲しい方には[GCP公式の解説](https://www.youtube.com/watch?v=xXk1YlkKW_k&t=123s)があります。

そしてサービスアカウントのどの辺がイケてないかというとGitHub上でサービスアカウント認証のためのキーを保存しておかなければならないので


- キーのローテーションなどの管理コストを払わなければならない
- キーは寿命が長いので(サービスアカウントに紐づいているRoleが強いと特に)漏洩した時のリスクがとても高い

というあたりですね。GitHub内でsecretsとして管理していても漏洩のことを心配するあたりさすがセキュリティだなと私は思いました。

そしてWIFは***認証の都度ごく短時間(デフォルトでは1時間)だけ有効なキーをGCPに発行***してもらうことにより、サービスアカウントキーをGitHubに保存せずにサービスアカウントへのアクセスを可能とすることでこれを克服する認証方法です。

仕組みをシーケンス図にするとこんな感じ。GCP側で予めサービスアカウントの作成とRoleの付与が完了しているとします。

```mermaid
sequenceDiagram
    GitHub Actions->>GCP: GitHubが自動生成したトークン
    GCP ->> Workload Identity Pool: トークンを検証
    Workload Identity Pool ->> GCP: 検証結果
    GCP ->> GitHub Actions: サービスアカウントにアクセスできるトークン
    GitHub Actions ->> GCP: トークンでGCPリソースにアクセス
```

これにより寿命の長いサービスアカウントのキーをGitHubに保存しなくて良くなるのでセキュリティ的に嬉しいだけでなく、キーをGCP側とGitHub側で二重管理する手間からも解放されることになります。

# 発展

GitHubが発行してるトークンの内容が気になったのでもう少し調べてみるとOpenID Connectという仕様を実装して署名付きIDトークンを発行しているようです。OpenID Connectに関しては[こちらの神記事](https://qiita.com/TakahikoKawasaki/items/498ca08bbfcc341691fe)がとても参考になります。

これによるとOpenID Connectでは署名付きIDトークンを発行する側をIDプロバイダ、署名付きIDトークンを利用する側をクライアントアプリというそうです。つまり今回はGitHubがIDプロバイダになっていてGCPがクライアントアプリというわけですね。

これによりクライアントアプリは一度署名付きIDトークンをゲットしてしまえばトークンの署名を検証するだけでトークンの正当性を確認しつつトークン(実態はJSON)に含まれるユーザの属性を引き回すことができる、という仕組みのようです。考えた人は天才ですね。

整理してみると

```mermaid
sequenceDiagram
    ユーザ(GitHub Actions) ->> GitHub認証サービス: いつも通りID,パスワードで認証しておく
    GitHub認証サービス ->> GitHub OIDCプロバイダ: 認証OKでーす
    GitHub OIDCプロバイダ ->> GCP Security Token Services: OIDCトークン発行
    GCP Security Token Services ->> Workload Identity Pool: OIDCトークン丸投げ
    Workload Identity Pool ->> GitHub OIDCプロバイダ: 署名を検証
    Workload Identity Pool ->> GCP Security Token Services: OIDCトークン正当だよー
    GCP Security Token Services ->> ユーザ(GitHub Actions): サービスアカウントにアクセスできるトークンをあげます
```

# 終わりに

OpenID Connectなどのセキュリティ周りの知識は弱いなーと思っていたので今回はセキュリティ強化の良い勉強になりました。ユーザが自身の認証情報を認証サーバに渡してトークンを発行させる方式が同じだったのでOAuthとOpenID Connectが何となく似ているなーと思っていましたが、OpenID ConnectがOAuthの拡張仕様であると知って納得しました。


また、一般的なプログラミングのプラクティスとして「変数の寿命は短く」というのがありますが、今回のGithubのOIDCサポートにも「認証情報の寿命を短く」という思想が感じられて良いですね。


そして導入のためのGithub Actionsの[README](https://github.com/google-github-actions/auth#setup)も充実していて、OIDCがどうとかWIFがどうとか書きましたが結局コード上で変更したのは下記(例です)の通り数行だけで済んだので正直拍子抜けするくらい導入は楽でした。さらにこれだけでGCPから発行されたトークンの削除というクリーンアップ作業も勝手にやってくれるので、余程急いでないのであれば今後も使っちゃいますねこれは。ではまた。

```
steps:
- id: 'auth'
  name: 'Authenticate to Google Cloud'
  uses: 'google-github-actions/auth@v0.4.0'
  with:
    workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider'
    service_account: 'my-service-account@my-project.iam.gserviceaccount.com'
```