OLTAの柴田です。GCPとGithub Actions間の認証をキーレスで行う方法を紹介します。この記事を見れば利用している技術の概要が把握でき、GCP、Github Actionsの設定まで行えるようになると思います。
概要
Github Actions からGCPのリソースにアクセスするとき、GithubのOpen ID Connect(OIDC)と、GCPのWorkloadIdentity Federationを利用すると、秘密鍵を作成することなくOIDCトークンを利用してGithub ActionsからGCPにアクセスすることができます。
OIDCトークンで認証を行い、都度有効期限が短いアクセストークンを利用してGithubからGCPにアクセスすることになるので、サービスカウントキーのメンテナンスとセキュリティの負担がなくなります。
この仕組みができる前は、GCPのサービスアカウントキーを発行して、発行したキーを外部システムに保存して利用する必要がありました。このサービスアカウントキーを持っているだけでサービスアカウントに付与しているIAM権限でGCPリソースにできてしまうため、漏洩したときの影響が大きく、ローテーションなど管理コストが発生することが課題としてありました。
OLTAでは2022年春頃からCIにGithub Actionsを利用し始めており、そのタイミングでWorkloadIdentity Federationを利用してGCPとの認証を行っています。GithubのOIDCサポート、Workload Identity Federation自体は 2021年冬ごろから使える状態だったようです。
前提
GithubのOpenID Connectサポート
GithubのOpenID Connect(以下OIDC)サポートというのは、GithubがOIDC Providerを用意し、 OIDCトークンを払い出せるようになった、ということです。( OpenID Connectについての説明はしません) OIDCに対応している任意のクラウドプロバイダーは、Githubが払い出すOIDCトークンを検証することで、認証することができるようになります。これによって、クラウドプロバイダー側で発行した機密情報を保存する必要がなくなり、漏洩のリスクを減らせます。
GCPの WorkloadIdentity Federation
GCPの WorkloadIdentity Federationは、外部IDに対して、IAMロールを付与することができる機能です。 ちなみに WorkloadIdentity は GKE用の機能で、KubernetesのService AccountとGCPのService Accountを連携させて、GCPのService AccountにIAM権限を付与することで、Kubernetes内のコンテナにIAM権限を付与することができるようになります。Kubernetesのコンテナも上述と同じ、サービスアカウントキーを払い出して保存させる必要があり、冒頭とほぼ同じ問題がありました。
外部IDとの連携は、OAuth2.0トークン交換の仕様に従って行われます。 そのため、Githubだけではなく、AWS, Okta, Kubernetesクラスタなど様々な外部IDプロバイダと連携することができます。
連携方法
GCP(terraform)
まずWorkloadIdentityプールを作ります。これは外部IDを管理するエンティティです。Githubのような外部IDプロバイダが増えるたびに追加します。
resource "google_iam_workload_identity_pool" "github_oidc" { workload_identity_pool_id = "github-oidc" display_name = "github-oidc" description = "github-oidc" }
次に、WorkloadIdentityプールのプロパイダーを設定します。これは、プールと外部IDプロバイダとの関係を表すエンティティです。attribute_mappingで、GithubのOIDCトークンに含まれるclaimをプロバイダのattributeにマッピングしています。google.subjectは必須です。attribute.* はカスタム属性で最大50個まで定義することができ、マッピングした属性を認証の条件として利用することができます。特定のレポジトリからのアクセス時のみ権限を渡したいので、attribute.repositoryを定義しています。
resource "google_iam_workload_identity_pool_provider" "github_actions" { workload_identity_pool_id = google_iam_workload_identity_pool.github_oidc.workload_identity_pool_id workload_identity_pool_provider_id = "github-actions" display_name = "github" attribute_mapping = { "google.subject" = "assertion.sub" "attribute.actor" = "assertion.actor" "attribute.repository" = "assertion.repository" } oidc { issuer_uri = "https://token.actions.githubusercontent.com" } }
最後にサービスアカウントのWorkloadIdentityUserロールを外部IDに付与します。memberと指定しているprincipalSet で、外部IDを認証するときの条件を設定できます。下記の例では、attribute.repository(GithubのOIDCトークンのassertion.repository)を利用して、特定のレポジトリからのアクセスの場合のみ権限付与を行う設定になっています。
resource "google_service_account_iam_binding" "github_actions_invoy_ops" { service_account_id = google_service_account.xxxxx.name role = "roles/iam.workloadIdentityUser" members = [ "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github_oidc.name}/attribute.repository/#{Githubのレポジトリ名}" ] }
principalの指定方法は、属性を利用する以外にも、単一のID、プール内の全てのIDなど複数の記述方法があります。 cloud.google.com
Github Actions
Github Actionsではworkflow定義内のstepsで、google-github-actions/authを利用するだけです。
- name: Authenticate to Google Cloud id: auth uses: google-github-actions/auth@v0 with: workload_identity_provider: projects/#{GCPのプロジェクトナンバー}/locations/global/workloadIdentityPools/github-oidc/providers/github-actions service_account: #{サービスアカウントのメールアドレス}
GCPのプロジェクト構成
GCPのWorkloadIdentityプールは、プロジェクトに紐づくリソースのため、プロジェクトが開発環境、ステージング環境、本番環境で分かれている場合、素直にプロジェクトごとにWorkload Identityプールを作成すると、管理すべきリソースが増えてしまいます。 そこで、OLTAでは以下のようなプロジェクト構成にしています。Buildプロジェクトのサービスアカウントに対して必要に応じてdev,stage,prodプロジェクトのIAM権限を付与して、CI/CDを行っています。
まとめ
最初見た時は把握すべき概念が多く取っ付きづらいイメージだったのですが、一度把握してしまえば、少量のterraformの定義を追加するだけで Github Actionsから安全にGCPリソースにアクセスさせることができる環境が作れます。CI/CD以外にもGCPリソース関連のちょっとした自動化をGithub Actionsを利用できます。 Github ActionsとGCPを利用していて、もしサービスアカウントキーでの認証を続けている場合は、WorkloadIdentity Federationへの移行がおすすめです。
最後に
OLTAでは、ユーザーに価値を提供し、事業を成長させるサービスを一緒に作る仲間を募集しています。 もし、この投稿にご興味を持っていただけたら、是非カジュアルにお話しさせてください。