↓メインコンテンツへスキップ
  1. Blogs/

TerraformでGitHub Actions用Workload Identity Federationを構築してみた

2 分
Terraform GoogleCloud GitHubActions WorkloadIdentity
0222-nnn
著者
0222-nnn
猫が好き
目次
Terraform-GoogleCloud - この記事は連載の一部です
パート 21: この記事

概要
#

GitHub ActionsからGoogle Cloudへ認証する際、Service Account KeyをGitHub Secretsへ置く方法は漏洩の経路になります。長期間有効なJSON Keyは外部システムへ置きたくありません。

そこでWorkload Identity Federation(WIF)を使います。GitHub ActionsのOIDC TokenをGoogle Cloudが直接受け入れる仕組みです。Service AccountをImpersonateでき、鍵ファイル自体を作りません。

Terraformの基本操作とGitHub Actionsのworkflow構文は前提としています。

Service Account Keyの発行は扱いません。Branch/Environment単位の細かいCondition設計や、Private Repositoryでの追加設定も対象外です。

検証すること
#

  • GitHub Actions向けWorkload Identity PoolとProviderを作成できること
  • OIDC ClaimとIAMの両方でRepositoryを限定できること
  • Service Account KeyなしでWorkflowからGoogle Cloudへ認証できること
  • Demo Bucketの読取で権限を確認できること

前提環境
#

  • Google Cloud CLIとApplication Default Credentials(ADC)、Terraform
  • GitHub Actionsを実行できるGitHub Repository
  • IAM、Service Account、Cloud Storageを操作できるGoogle Cloud Project
  • 検証時のバージョン:Terraform 1.14.3、google 7.43.0

今回の構成
#

Workload Identity Poolは、外部のIDをGoogle Cloudへ受け入れる入口です。Projectの直下に置かれ、Regionには属しません。

flowchart TB
    subgraph GH["GitHub"]
        Wf["Actions Workflow"]
        OIDC["OIDC Token 発行"]
        Wf --- OIDC
    end

    subgraph GCP["Google Cloud"]
        subgraph Project["Project"]
            subgraph WIF["Workload Identity Federation"]
                Pool["Pool"]
                Prov["Provider
リポジトリで制限"] Pool --- Prov end SA["Service Account
(鍵は発行しない)"] Bucket["Demo Bucket"] end end OIDC -->|"トークンを提示"| Prov Prov -.->|"権限借用を許可"| SA SA -->|"読み取り"| Bucket

Service Account Keyを作らないのが要点です。鍵ファイルをGitHubのSecretsへ置く必要がなく、漏洩の経路が減ります。

認証の流れは次のとおりです。

sequenceDiagram
    participant Wf as Actions Workflow
    participant Prov as WIF Provider
    participant STS as Security Token Service
    participant SA as Service Account
    participant GCS as Cloud Storage

    Wf->>Prov: OIDC Token を提示
    Note over Prov: リポジトリ名などの条件を検証
    Prov->>STS: 条件を満たせば交換
    STS-->>Wf: 一時的なアクセストークン
    Wf->>SA: 権限を借用
    SA->>GCS: gcloud storage ls
    GCS-->>Wf: 一覧を返す

受け取るのは一時的なトークンなので、有効期限が切れれば使えなくなります。

使用するTerraformコード
#

GitHubのClaimをGoogle Cloudへ渡す
#

resource "google_iam_workload_identity_pool_provider" "github" {
  workload_identity_pool_id          = google_iam_workload_identity_pool.github.workload_identity_pool_id
  workload_identity_pool_provider_id = var.provider_id

  attribute_mapping = {
    "google.subject"       = "assertion.sub"
    "attribute.repository" = "assertion.repository"
  }
  attribute_condition = "assertion.repository == '${var.github_repository}'"
  oidc { issuer_uri = "https://token.actions.githubusercontent.com" }
}

ProviderのConditionとService Account IAMのprincipalSetの両方でOWNER/REPOSITORYを限定します。これにより同じGitHub OIDC Issuerを利用する無関係なRepositoryからの認証を拒否します。

member = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github.name}/attribute.repository/${var.github_repository}"
role   = "roles/iam.workloadIdentityUser"

TerraformとWorkflow
#

cd Advanced-Examples/05-wif-github-actions
cp terraform.tfvars.example terraform.tfvars
terraform init
terraform fmt -check
terraform validate
terraform apply

Pool、Provider、Service Account、Demo Bucketが作成されます。合わせて12リソースです。

Apply complete! Resources: 12 added, 0 changed, 0 destroyed.

Outputs:

demo_bucket_name = "tf-adv-wif-demo-YOUR_PROJECT_ID"
github_repository = "tech-0222/terraform-gcp-examples"
service_account_email = "tf-adv-github-actions@YOUR_PROJECT_ID.iam.gserviceaccount.com"
workload_identity_provider_name = "projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/tf-adv-github-pool/providers/tf-adv-github-provider"

WorkflowにはOIDC Token発行権限を与えます。Terraform OutputのProvider名とService Accountを指定します。

permissions:
  contents: read
  id-token: write

steps:
  - uses: google-github-actions/auth@v2
    with:
      workload_identity_provider: projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL/providers/PROVIDER
      service_account: github-actions@PROJECT_ID.iam.gserviceaccount.com
  - run: gcloud storage ls gs://DEMO_BUCKET

GCP側で確認する
#

Pool / Providerが有効になっているか確認します。

gcloud iam workload-identity-pools describe "$(terraform output -raw workload_identity_pool_id)"
gcloud iam workload-identity-pools providers describe \
  "$(terraform output -raw workload_identity_provider_name)"
state: ACTIVE

両方ともACTIVEでした。Demo Bucketの内容も確認します。

gcloud storage cat "gs://$(terraform output -raw demo_bucket_name)/hello-wif.txt"
hello-from-wif-demo

ここまではローカルのgcloud(自分の認証)での確認です。実際にGitHub Actionsから鍵なしで認証できるかは別に確認します。Workflowを実行し、Service Account KeyなしでBucketを一覧できるかを見ます。確認する対象はGitHub Actionsのログに出るgcloud storage lsの結果です。

後片付けと注意点
#

terraform destroy
Destroy complete! Resources: 12 destroyed.

destroyしたPool / Providerは30日間ソフトデリート状態で残ります。 同じIDで再度applyすると409 Requested entity already existsで失敗します。再検証する場合はgcloud iam workload-identity-pools undeleteで復元し、terraform importでstateへ取り込んでから続けます。

Repositoryだけでなく、必要に応じてBranch、Environment、WorkflowなどのClaimもConditionへ加えます。GitHub Actions側のpermissionsはJobごとに最小化します。Google Cloud側の権限も、検証用BucketのViewerなど必要最小限に絞ります。

まとめ
#

  • GitHub SecretsへService Account Keyを保存せず認証できる
  • Attribute ConditionとprincipalSetでRepositoryを限定できる
  • 短時間Credentialを使い、Key Rotationの運用を減らせる
  • Demo Bucketへのアクセスで権限をEnd-to-End確認できる

参考資料
#

次回
#

次回はCloud RunからCloud SQL for PostgreSQLへ接続する構成を作ります。

Terraform-GoogleCloud - この記事は連載の一部です
パート 21: この記事

関連記事

TerraformでGKE Workload IdentityからCloud Storageへアクセスしてみた
2 分
Terraform GoogleCloud GKE WorkloadIdentity
Terraform StateをCloud Storage Remote Backendへ移行してみた
2 分
Terraform GoogleCloud CloudStorage
TerraformからGoogle Cloudへ接続してProject情報を取得してみた
3 分
Terraform GoogleCloud
TerraformでArtifact RegistryのDocker Repositoryを作成してみた
2 分
Terraform GoogleCloud Docker
TerraformでArtifact RegistryのコンテナをCloud Runへデプロイしてみた
2 分
Terraform GoogleCloud CloudRun ArtifactRegistry
TerraformでBigQuery DatasetとTableを作成してみた
2 分
Terraform GoogleCloud