Direct Workload Identity Federation + Github Actions + Cloud Run のCD構築
背景
私は日ごろから業務や個人開発で GCP のお世話になりがちなのですが、認証周りをサービスアカウントの認証情報 JSON ファイルを使用した認証に頼ってしまうことが多くありました。
実際、一部のプロジェクトでは Github Actions を用いて Cloud Run へ自動デプロイを行う CD パイプラインを構築しているのですが、その際にサービスアカウントの認証情報 JSON ファイルを Github Secrets に登録して、それを用いて GCP への認証を行うという方法をとっていました。
しかし、これではサービスアカウント認証情報の管理が煩雑になってしまう上、セキュリティ的にもリスクがあり、あまりよろしくありません。
そこで、サービスアカウントの認証情報 JSON ファイルを使用せずに認証を実施すべく、 Direct Workload Identity Federation (Workload Identity)を利用した認証方法を試してみることにしました。
(Direct Workload Identity Federation 自体は 2023 年 11 月から追加されている機能らしいので、かなり今更ではありますが。)
Direct Workload Identity Federation とは
Direct Workload Identity Federation は、サービスアカウントやその認証情報 JSON ファイルを使用せずに、外部の ID プロバイダ (IDP) を通じて GCP のサービスにアクセスするための認証方法です。
これにより、外部の ID に対して IAM ロールを付与し、GCP リソースへの直接アクセスを可能にします。
GCP 環境の構築
デプロイ前の準備として、GCP 環境を構築します。
環境構築は Terraform を使用して行います。gcloud コマンドや UI コンソールを使用しても同様の操作が可能ですが、ここでの説明は割愛します。
# GCP 環境構築用のディレクトリを作成
mkdir gcp-setup
cd gcp-setup
touch main.tf
provider "google" {
project = "my-project-id" # GCP プロジェクト ID を指定
}
Artifact Registry のリポジトリ作成
Cloud Run へデプロイするためのコンテナイメージを格納するためのリポジトリを作成します。
resource "google_artifact_registry_repository" "repo" {
location = "asia-northeast1"
repository_id = "my-repository"
format = "DOCKER"
}
Workload Identity プールの作成
Workload Identity プールを作成します。
resource "google_iam_workload_identity_pool" "pool" {
workload_identity_pool_id = "my-pool"
display_name = "my-pool"
}
Workload Identity プロバイダの作成
上記で作った Workload Identity プールに対して、Github Actions からの認証を許可するプロバイダを作成します。
repository_owner は、リポジトリ URL( https://github.com/<repository-owner>/<repository-name> )の <repository-owner> の部分です。
resource "google_iam_workload_identity_pool_provider" "provider" {
workload_identity_pool_id = google_iam_workload_identity_pool.pool.workload_identity_pool_id
workload_identity_pool_provider_id = "github-actions-provider"
display_name = "github-actions-provider"
attribute_mapping = {
"google.subject" = "assertion.sub"
"attribute.repository" = "assertion.repository"
"attribute.actor" = "assertion.actor"
"attribute.repository" = "assertion.repository"
"attribute.repository_owner" = "assertion.repository_owner"
}
oidc {
issuer_uri = "https://token.actions.githubusercontent.com"
}
attribute_condition = "assertion.repository_owner == 'my-github-repository-owner'"
depends_on = [google_iam_workload_identity_pool.pool]
}
Workload Identity プリンシパルに対する IAM ロールの付与
Github Actions から Cloud Run へのデプロイを許可するために、Workload Identity プリンシパルに対して必要な IAM ロールを付与します。
Artifact Registry へのコンテナのプッシュ、および Cloud Run へのデプロイには、以下のロールが必要です
roles/run.developerroles/iam.serviceAccountUser-
roles/artifactregistry.writer(作成した Artifact Registry リポジトリに対して)
対象のプリンシパルは、attribute.repository 属性が自分の Github リポジトリである ID グループにしておきます。
variable "principal" {
default = "principalSet://iam.googleapis.com/projects/<project-number>/locations/global/workloadIdentityPools/<workload_identity_pool_id>/attribute.repository/<repository-owner>/<repository-name>"
}
resource "google_project_iam_member" "run_developer" {
project = "my-project-id"
role = "roles/run.developer"
member = var.principal
}
resource "google_project_iam_member" "iam_service_account_user" {
project = "my-project-id"
role = "roles/iam.serviceAccountUser"
member = var.principal
}
resource "google_artifact_registry_repository_iam_member" "repository_writer" {
project = "my-project-id"
location = "asia-northeast1"
repository = "my-repository"
role = "roles/artifactregistry.writer"
member = var.principal
}
Terraform の実行
これまでの Terraform コードを main.tf に追加し、以下のコマンドで GCP 環境を構築します。
# Terraform の初期化
terraform init
# Terraform の適用内容確認
terraform plan
# Terraform の適用
terraform apply
Github Actions の設定
Github Actions のワークフローを作成します。
name: Deploy to Cloud Run
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Authenticate to GCP
id: auth
uses: google-github-actions/auth@v2
with:
workload_identity_provider: "projects/<project-number>/locations/global/workloadIdentityPools/<workload_identity_pool_id>/providers/github-actions-provider"
- name: Set up Google Cloud SDK
uses: google-github-actions/setup-gcloud@v2
- name: Configure Docker
run: |
gcloud auth configure-docker asia-northeast1-docker.pkg.dev
- name: Build and Push Docker Image
run: |
docker build -t asia-northeast1-docker.pkg.dev/<project-id>/my-repository/my-image:${{ github.sha }} .
docker push asia-northeast1-docker.pkg.dev/<project-id>/my-repository/my-image:${{ github.sha }}
- name: Deploy to Cloud Run
run: |
gcloud run deploy my-service \
--project <project-id> \
--image asia-northeast1-docker.pkg.dev/<project-id>/my-repository/my-image:${{ github.sha }} \
--region asia-northeast1 \
--platform managed
こちらを作成したら、main ブランチにプッシュして、デプロイが完了することを確認してください。
まとめ
Direct Workload Identity Federation を使用するようにしたことで、サービスアカウントの認証情報 JSON ファイル無しで Cloud Run への自動デプロイが実現できました。
チーム内でのキーの共有や管理だったり、Gtihub Secrets への登録だったり、そういった作業が面倒だなあと思っていたので、これでかなり楽になりました。
セキュリティリスクも減らせるので一石二鳥です。
参考
実装や記事の執筆にあたって、以下の記事を大変参考にさせていただきました。この場を借りて感謝申し上げます。
Discussion