🌊

Workload Identity 連携を利用して GitHub Actions を動かす

2023/04/14に公開

こんにちは、SREディビジョンの松島です。
この記事では Workload Identity 連携の機能紹介と、GitHub Actions から Google Cloud を操作するハンズオンを行います。

Workload Identity 連携とは

外部のワークロードに対して Google Cloud のサービスアカウントの権限を利用させることができる機能です。
AWSや、OpenID Connect、SAML 2.0 について Workload Identity による ID の連携が可能となっています。

Workload Identity 連携のいいところ

サービスアカウントキーを発行せずに外部のワークロードに対して Google Cloud の操作を行わせることができる点が良いところです。

サービスアカウントキーは発行自体が認証情報の漏洩機会の増加などにつながるなど、ベストプラクティスにも記載があるように、可能な限り利用すべきではありません。
Workload Identity 連携を利用することで、サービスアカウントキーを発行することなく外部の ID に対して Google Cloud のサービスアカウントの権限を利用させることができます。

各種設定のイメージ

ここでは GitHub Actions を例に、各構成要素間の関連を説明します。

まず、全体像は下図のようになります。
(図内の点線の四角は自動で値が決まるものを表しています)
workload_identity_overview.png

続いて主な構成要素である、GitHub Actions, Workload Identity 連携, IAM それぞれをみていきます。
上の図と見比べながら眺めてみてください。

GitHub Actions

GitHub Actions のワークフローからは Workload Identity 連携で ID 連携を実施するため、GitHub Actions の auth アクションを利用し、下記の値を指定します。

  • workload_identity_provider
    • 外部IDを構成する Workload Identity プロバイダを指定する
  • serviceaccount
    • 権限を利用するサービスアカウントを指定する

また、GitHub から発行されたリポジトリ名、組織名などの情報を含んだ OIDC トークンが Google Cloud に認証する際に使用され、Workload Identity プロバイダ側では Assertion なる値として設定に利用されます。
GitHub Actions でOIDCトークンに設定される値についてはGitHub Acions 公式情報及びauth アクションのドキュメントをご参照ください

Workload Identity 連携

Google Cloud 側では Workload Identity 連携に関する設定として、Workload Identity プールとその配下に Workload Identity プロバイダを作成します。
それぞれの役割は下記の通りです。

  • Workload Identity プール
    • 外部 ID の集合として機能
    • Workload Identity プール本体の情報と配下に作成する Workload Identity プロバイダの情報を加えた外部 ID が Google Cloud 上のサービスアカウントの権限を利用する形となる
  • Workload Identity プロバイダ
    • Google Cloud と外部 ID プロバイダとの関係を表したもの
    • GitHub Actions 側の OIDC トークンの属性値を assertion から抽出してプロバイダ属性とマッピングする

IAM

Workload Identity プール ID、Workload Identity プロバイダ属性情報から下記の形式で外部 ID が構成されます。

principal://iam.googleapis.com/projects/<プロジェクト番号>/locations/global/workloadIdentityPools/<Workload Identity プール ID>/<属性マッピングに応じて変わる部分>

この外部 ID に対して、利用したいサービスアカウントに Workload Identity ユーザー (roles/iam.workloadIdentityUser) ロールを付与します。

なお、外部 ID の構成は Workload Identity プロバイダの属性マッピングによって形式が変わります。
詳細は公式ドキュメントをご参照ください。

Workload Identity 連携を使って GitHub Actions を動かす

実際に Cloud Storage にファイルをアップロードする GitHub Actions を構成し、動かしてみます。
公式ドキュメントを参考に、下記のようなフローで設定を行います。

  1. 下準備
  2. Workload Identity プールの設定
  3. Workload Identity プールへのプロバイダ追加
  4. 外部 ID に対してサービス アカウントの権限借用を許可
  5. GitHub Actions ワークフロー定義
  6. 動かす

以下、各手順及び設定時の注意点などを順に説明します。

1. 下準備

下記を準備します。
(趣旨と外れるため、これらの詳細については割愛します。)

  • GitHub Actions を動かす GitHub リポジトリ
  • GitHub Actions からのファイルアップロード先となる GCS バケット
  • GitHub Actions で利用するサービスアカウントと、GCS バケットへのオブジェクト管理者ロール付与

2. Workload Identity プールの設定

Workload Identity プールを作成します。
コンソールで「IAMと管理」→「Workload Identity 連携」を開き、「プールを作成」をクリックします。

下表の値を入力して「続行」をクリックします。

項目名 設定値 説明
名前 test 作成したい Workload Identity プールの表示名および ID
説明 テスト 作成したい Workload Identity プールの用途などを記載する

create_workload_identity_pool.png

3. Workload Identity プールへのプロバイダ追加

続いて Workload Identity プロバイダを作成します
まずは下表の値を入力し、続行をクリックします。

項目名 設定値 説明
プロバイダの選択 Open ID Connect GitHub Actions ではOIDCを使用するため、OIDC を選択します
プロバイダ名 GitHub プロバイダの表示名
プロバイダID github プロバイダの識別子
発行元 https://token.actions.githubusercontent.com OIDC トークンの提供元
GitHub 公式ドキュメントに記載の値を入れる
オーディエンス デフォルトのオーディエンス 受け入れ可能な OIDC トークンの aud 属性の値
この例では変更の必要がないためデフォルトを使用する

set_workload_identity_provider.png

続いて属性を設定し、「保存」をクリック、Workload Identity プロバイダを作成します。

項目名 設定値 説明
属性マッピング google.subject = assertion.repository 外部IDを構成する属性と OIDC トークン情報のマッピング
この例ではリポジトリ名称を利用する
属性条件 assertion.repository == "GitHub組織名/リポジトリ名" Wokload Identity の利用を OIDC トークンの内容に応じて制限する機能です
この例では自分の GitHub オーナー名を指定し、自分のリポジトリからのみ利用を許可する

set_attribute_mapping.png

4. 外部 ID に対してサービス アカウントの権限借用を許可

作成した Workload Identity プールの画面を開き、「アクセスを許可」をクリックします。
画面に従い、下表の値を入力して「保存」をクリックします。

項目名 設定値 説明
サービスアカウント 手順1で準備したサービスアカウント GitHub Actions に利用させたいサービスアカウント
プリンシパル フィルタに一致するIDのみ
subject=リポジトリ所有者/リポジトリ名
ロールを付与する外部 ID の条件を指定する
フィルタにはプロバイダの属性マッピングでマッピングした値が使用可能
この例では自分の GitHub の特定リポジトリのみを指定する

set_access.png

ここまで完了した状態で手順1で準備したサービスアカウントの権限情報を見ると、上記までで指定した値から生成された外部 ID に権限が付与されていることが確認できます。

> gcloud iam service-accounts get-iam-policy <手順1で準備したサービスアカウント>

bindings:
- members:
  - principal://iam.googleapis.com/projects/<プロジェクト番号>/locations/global/workloadIdentityPools/github/subject/<GitHub リポジトリオーナー>/<GitHub リポジトリ名>
  role: roles/iam.workloadIdentityUser

5. GitHub Actions ワークフロー定義

用意したリポジトリの .github/workflows/ に下記の GitHub Actions ワークフロー定義ファイルを配置します。

name: test
on:
  workflow_dispatch:
  push:
    branches:
      - main
jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - name: checkout
        uses: actions/checkout@v3
      - id: 'auth'
        name: 'Authenticate to Google Cloud'
        uses: 'google-github-actions/auth@v1'
        with:
          workload_identity_provider: 'projects/<プロジェクト番号>/locations/global/workloadIdentityPools/<Workload Identity プール ID>/providers/<Workload Identity プロバイダ ID>'
          service_account: '<利用したいサービスアカウント>'
      - name: 'Set up Cloud SDK'
        uses: 'google-github-actions/setup-gcloud@v1'
        with:
          version: '>= 379.0.0'
      - name: 'Upload test file'
        run: 'echo "test" > test.txt && gsutil cp test.txt gs://<用意したGCSバケット名>'

6. GitHub Actions を動かす

手順5で作成したファイルを GitHub リポジトリの main ブランチに push すると、GitHub Actions が動作し、指定した GCS バケットにテストファイルがアップロードされます

おまけ: Terraform で書くと

Terraform だと下記のようになります。
コンソールで作成したのと同じような設定値を入れることになるので混乱は少ないかと思います。

locals {
  project_id        = "<プロジェクトID>"
  github_repo_owner = "<GitHub所有者(オーナー)名>"
  github_repo_name  = "<GitHubリポジトリ名>"
}

resource "google_iam_workload_identity_pool" "main" {
  workload_identity_pool_id = "github"
  display_name              = "GitHub"
  description               = "GitHub Actions 用 Workload Identity Pool"
  disabled                  = false
  project                   = local.project_id
}

resource "google_iam_workload_identity_pool_provider" "main" {
  workload_identity_pool_id          = google_iam_workload_identity_pool.main.workload_identity_pool_id
  workload_identity_pool_provider_id = "github"
  display_name                       = "GitHub"
  description                        = "GitHub Actions 用 Workload Identity Poolプロバイダ"
  disabled                           = false
  attribute_condition                = "assertion.repository_owner == \"${local.github_repo_owner}\""
  attribute_mapping = {
    "google.subject" = "assertion.repository"
  }
  oidc {
    issuer_uri = "https://token.actions.githubusercontent.com"
  }
  project = local.project_id
}

resource "google_service_account_iam_member" "workload_identity_sa_iam" {
  service_account_id = google_service_account.main.name
  role               = "roles/iam.workloadIdentityUser"
  member             = "principal://iam.googleapis.com/${google_iam_workload_identity_pool.main.name}/subject/${local.github_repo_owner}/${local.github_repo_name}"
}

resource "google_service_account" "main" {
  account_id   = "github-test"
  display_name = "github-test"
  description  = "GitHub Actions 動作確認用"
  project      = local.project_id
}

Workload Identity 連携設定上の注意点

下記の条件で設定を行なった場合、サービスアカウントと Workload Identity プロバイダ名がわかれば好きにサービスアカウントを利用できてしまいます。

  • Workload Identity プロバイダ設定時に属性条件を指定しない
  • IAM 設定時に Workload Identity プール配下の外部ID全てに対して権限を付与する

そのため、例えば GitHub の場合は Workload Identity プロバイダの属性条件、もしくは IAM 設定で特定組織配下のみしか利用できないような制限をかけるのが望ましいと考えられます。
(上記のハンズオンではそうしてみました)

また、IAM 設定時にマッピングした属性を利用することで、特定のリポジトリからは特定のサービスアカウントのみが利用できるような権限統制も可能です。
例えば、インフラチームは Terraform を利用したいので編集者などの高権限が必要ですが、アプリケーションチームはデプロイだけできれば良いので Cloud Run だけ設定変更できればいよい場合など、チームごとに利用できるサービスアカウントを分けたい場合に有用な手段だと思います。

おわりに

今回は Workload Identity 連携を紹介しました。
サービスアカウントキーを利用する方法よりセキュアな認証方式となっておりますので、GitHub Actions や AWS をはじめ、他の外部サービスから Google Cloud を操作させたい場合に機会があればぜひ利用してみてください。

Discussion