はじめに
以前参加記を書いたCARTA HOLDINGSの夏インターン「Treasure」の期間が終わった後もチームで開発を続けていました。その中でGithub ActionsからCloud Runへコンテナを自動デプロイしていたところ運良く新しい技術を使う機会に恵まれたので今回はその話を書こうと思います。記事の内容とは関係ありませんがTreasureまじで最高です。学生さんは1度は応募してみることをお勧めします✨
何使ったの?
Workload Identity FederationというGCPが提供する認証方法を使ってみました。詳しくはGCP公式が出してるこのプレスとかGitHub公式が出してるこのブログ記事なんかが参考になると思いますがざっくり概要だけまとめて見ると...
GitHub Actions ワークフローで Google Cloud 上でのリソースの読み取りや変更が必要な場合(Artifact Registry へのコンテナの公開や Cloud Run での新しいサービスのデプロイなど)、最初に認証をしなくてはなりません。
これはそのままです。GitHub Actions上でCloud Runにコンテナデプロイしたりする時(要するにクラウド上のリソースを操作したい時)には予め認証が必要です。でその認証方法がこれまではあんまりイケてませんでした。WIFはそれに代わる新たな認証方法というわけです。ではそのイケてない認証方法というのは...
GitHub Actions から Google Cloud への従来の認証では、有効期間が長い JSON サービス アカウント キーのエクスポートと保存が必要でした。
サービスアカウントです。AWSでいうところのIAM Roleに当たるもので人間以外(今回はGithub Actions)がプログラムの中からクラウドのリソースにアクセスしたい時に使われるものですね。詳しい解説が欲しい方にはGCP公式の解説があります。
そしてサービスアカウントのどの辺がイケてないかというとGitHub上でサービスアカウント認証のためのキーを保存しておかなければならないので
- キーのローテーションなどの管理コストを払わなければならない
- キーは寿命が長いので(サービスアカウントに紐づいているRoleが強いと特に)漏洩した時のリスクがとても高い
というあたりですね。GitHub内でsecretsとして管理していても漏洩のことを心配するあたりさすがセキュリティだなと私は思いました。
そしてWIFは認証の都度ごく短時間(デフォルトでは1時間)だけ有効なキーをGCPに発行してもらうことにより、サービスアカウントキーをGitHubに保存せずにサービスアカウントへのアクセスを可能とすることでこれを克服する認証方法です。
仕組みをシーケンス図にするとこんな感じ。GCP側で予めサービスアカウントの作成とRoleの付与が完了しているとします。
これにより寿命の長いサービスアカウントのキーをGitHubに保存しなくて良くなるのでセキュリティ的に嬉しいだけでなく、キーをGCP側とGitHub側で二重管理する手間からも解放されることになります。
発展
GitHubが発行してるトークンの内容が気になったのでもう少し調べてみるとOpenID Connectという仕様を実装して署名付きIDトークンを発行しているようです。OpenID Connectに関してはこちらの神記事がとても参考になります。
これによるとOpenID Connectでは署名付きIDトークンを発行する側をIDプロバイダ、署名付きIDトークンを利用する側をクライアントアプリというそうです。つまり今回はGitHubがIDプロバイダになっていてGCPがクライアントアプリというわけですね。
これによりクライアントアプリは一度署名付きIDトークンをゲットしてしまえばトークンの署名を検証するだけでトークンの正当性を確認しつつトークン(実態はJSON)に含まれるユーザの属性を引き回すことができる、という仕組みのようです。考えた人は天才ですね。
整理してみると
終わりに
OpenID Connectなどのセキュリティ周りの知識は弱いなーと思っていたので今回はセキュリティ強化の良い勉強になりました。ユーザが自身の認証情報を認証サーバに渡してトークンを発行させる方式が同じだったのでOAuthとOpenID Connectが何となく似ているなーと思っていましたが、OpenID ConnectがOAuthの拡張仕様であると知って納得しました。
また、一般的なプログラミングのプラクティスとして「変数の寿命は短く」というのがありますが、今回のGithubのOIDCサポートにも「認証情報の寿命を短く」という思想が感じられて良いですね。
そして導入のためのGithub ActionsのREADMEも充実していて、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'