GitHub ActionsからGCPにOpenID Connectでアクセスする

GitHub ActionsがOIDC (OpenID Connect)に対応したと公式から発表があったため、GCPへのアクセスを行ってみました。

github.blog

GCPのWebコンソールやgcloudコマンドを使ったやり方は公式ドキュメントで紹介されているため、ここではTerraformを使ったやり方を紹介します。

docs.github.com

github.com

上記の公式ドキュメントで紹介されているコマンドはデモ用のためセキュリティ的にはあまり良い状態ではありません。
今回は特定のリポジトリのみで使用できるように設定していきます。

Terraformで設定する

まずはGitHub Actionsで使用するサービスアカウントを作成します。

resource "google_service_account" "demo" {
  account_id   = "demo"
  display_name = "demo"
  description  = "demo"
}

サービスアカウントへの権限設定は今までと変わらないため省略します。

次にWorkload Identity Poolを作成します。
GCPによると環境によって分けることを推奨するようです。

Workload Identity プールは相互に分離されていますが、1 つのプールは任意の数のサービス アカウントになり代わることができます。一般に、開発、ステージング、本番環境など、環境ごとに新しいプールを作成することをおすすめします。

cloud.google.com

resource "google_iam_workload_identity_pool" "github_actions_demo" {
  provider = google-beta

  workload_identity_pool_id = "github-actions-demo"
  display_name              = "GitHub Actions demo"
  disabled                  = false
}

次は先程作成したPoolにWorkload Identity Providerを作成します。

ここで行う属性のマッピングがセキュリティを高めるために重要になってきます。

リポジトリで権限の制御を行うために "attribute.repository" = "assertion.repository" として属性にリポジトリ名を追加します。

リポジトリだけではなくブランチに対しても制御することができます。
例として、mainブランチのみにデプロイ用の権限を許可する、などがあるかと思います。

resource "google_iam_workload_identity_pool_provider" "demo" {
  provider = google-beta

  workload_identity_pool_id          = google_iam_workload_identity_pool.github_actions_demo.workload_identity_pool_id
  workload_identity_pool_provider_id = "demo-provider"
  display_name                       = "demo"

  attribute_mapping = {
    "google.subject"       = "assertion.sub"
    "attribute.actor"      = "assertion.actor"
    "attribute.aud"        = "assertion.aud"
    "attribute.repository" = "assertion.repository"
  }

  oidc {
    issuer_uri = "https://token.actions.githubusercontent.com"
  }
}

属性のマッピングについての詳細は以下の公式ドキュメントが参考になります。

cloud.google.com

最後にサービスアカウントと紐付けます。

resource "google_service_account_iam_binding" "gha_demo" {
  service_account_id = google_service_account.demo.name
  role               = "roles/iam.workloadIdentityUser"

  members = [
    "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github_actions_demo.name}/attribute.repository/[your_github_name]/[your_repository_name]",
  ]
}

最後の [your_github_name]/[your_repository_name] はサービスアカウントの使用を許可するリポジトリになります。

GitHub Actions

GitHub Actionsでは以下のようにProviderとサービスアカウントの指定をすればアクセスできます。

jobs:
  demo:
    runs-on: ubuntu-latest
    permissions:
      contents: 'read'
      id-token: 'write'
    steps:
      - name: Authenticate to Google Cloud
        id: auth
        uses: google-github-actions/auth@v0.3.1
        with:
          create_credentials_file: 'true'
          workload_identity_provider: 'projects/xxxxxxxxxxx/locations/global/workloadIdentityPools/github-actions-demo/providers/demo-provider'
          service_account: 'demo@xxxxxxxxxxxx.iam.gserviceaccount.com'

      - name: gcloud
        run: |-
          gcloud auth login --brief --cred-file=${{ steps.auth.outputs.credentials_file_path }}