くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

GitHub Actions+OIDCでCloud Runにデプロイする(Node.js)

前回の続き

以前、Cloud Buildをつかった自動デプロイを使ってたけど、
いつの間にか進化していて、かなり楽にデプロイできるようになってた(*´ω`*)

www.memory-lovers.blog

実際に試してみたら、すごい設定が少なくリリースできるようになってたので、
ハマったところを含めてまとめてみた備忘録(*´ω`*)

ざっくり、よくなったところとしては、

  • OIDCでGCPへの認証が簡単になった(秘密鍵とか不要)
  • Buildpacksでソースからデプロイできる(Dockerfile不要)

公式のドキュメントだとこの辺りを起点に読むとよいかも。

GitHub Actionsのworkflowファイル

yamlはこんな感じに。

  • actions/checkout@v3でチェックアウト
  • google-github-actions/auth@v1でOIDC認証
  • gcloud run deployでソースからデプロイ
name: "[MAIN] Deploy"
"on":
  push:
    branches:
      - main
  workflow_dispatch:

env:
  SERVICE_ACCOUNT: "YOUR_SERVICE_ACCOUNT_EMAIL"
  PROVIDER: "YOUR_WORKLOAD_IDENTITY_PROVIDER"
  SERVICE_NAME: "YOUR_CLOUD_RUN_SERVICE_NAME"
  SERVICE_REGION: "asia-northeast1"

jobs:
  build_and_deploy_dev:
    runs-on: ubuntu-latest
    # Add "id-token" with the intended permissions.
    permissions:
      id-token: write
      contents: read
    steps:
      # actions/checkout MUST come before auth
      - uses: actions/checkout@v3

      # Configure Workload Identity Federation via a credentials file.
      - uses: google-github-actions/auth@v1
        with:
          service_account: ${{ env.SERVICE_ACCOUNT }}
          workload_identity_provider: ${{ env.PROVIDER }}

      - run: gcloud run deploy ${SERVICE_NAME} --source . --region ${SERVICE_REGION}

uses: google-github-actions/auth@v1で認証ができるように、
GCP側の設定をしていく。

Buildpacksでソースからデプロイについては、こちら

www.memory-lovers.blog

ローカルからはgcloud run deployが実行できるよう、
APIの有効化などはできている想定。

Workload Identity連携の設定

以下のSetting up Workload Identity Federationに従って設定していく。

IAM Service Account Credentials APIの有効化

APIとサービス > ライブラリ」から
「IAM Service Account Credentials API」を検索して有効化

$ gcloud services enable iamcredentials.googleapis.com \
  --project "${PROJECT_ID}"

Workload Identity Poolの作成

「IAMと管理 > Workload Identity連携」にある
「プールを作成」からWorkload Identity Poolを追加

$ gcloud iam workload-identity-pools create "github-actions-pool" \
  --project="${PROJECT_ID}" \
  --location="global" \
  --display-name="github-actions-pool"

Workload Identity Providerの作成

「IAMと管理 > Workload Identity連携」にある
「プロバイダの追加」からWorkload Identity Providerを追加

Google OIDC
google.subject assertion.sub
attribute.repository assertion.repository
attribute.actor assertion.actor
attribute.repository_owner assertion.repository_owner
$ gcloud iam workload-identity-pools providers create-oidc "github-actions-provider" \
  --project="${PROJECT_ID}" \
  --location="global" \
  --workload-identity-pool="github-actions-pool" \
  --display-name="github-actions-provider" \
  --attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository,attribute.repository_owner=assertion.repository_owner" \
  --issuer-uri="https://token.actions.githubusercontent.com"

Workload Identity PoolのIAM プリンシパルを取得

「IAMと管理 > Workload Identity連携」にある
作成したプール(github-actions-pool)を選択し、
「IAMプリンシパル」の内容をコピー

principalSet://iam.googleapis.com/projects/<PROJECT_NO>/locations/global/workloadIdentityPools/github-actions-pool/*
$ gcloud iam workload-identity-pools describe "github-actions-pool" \
  --project="${PROJECT_ID}" \
  --location="global" \
  --format="value(name)"
# =>
# projects/<PROJECT_NO>/locations/global/workloadIdentityPools/github-actions-pool

# 表示された内容を環境変数としてexport
$ export WORKLOAD_IDENTITY_POOL_ID="..." # value from above

# POOL_IDからIAMプリンシパルを準備し、環境変数としてexport
$ export IAM_PRINCIPAL="principalSet://iam.googleapis.com/${WORKLOAD_IDENTITY_POOL_ID}/*"

サービスアカウントの作成

「IAMと管理 > サービスアカウント」で、GitHub Actionsで利用するサービスアカウントを作成。

github-actions@${PROJECT_ID}.iam.gserviceaccount.com

// gcloudコマンドの場合
$ gcloud iam service-accounts create "github-actions" \
  --project "${PROJECT_ID}"

workflowファイルenv.SERVICE_ACCOUNTに設定。

env:
  SERVICE_ACCOUNT: "github-actions@${PROJECT_ID}.iam.gserviceaccount.com"

サービスアカウントにIAMプリンシパルを追加

「IAMと管理 > サービスアカウント」で、
作成したサービスアカウントを選択し、
「権限」タブの「GRANT ACCESS」から追加

  • プリンシパル: principalSet://iam.googleapis.com/projects/<PROJECT_NO>/locations/global/workloadIdentityPools/github-actions-pool/*
  • ロール: Workload Identity ユーザ
$ gcloud iam service-accounts add-iam-policy-binding "github-actions@${PROJECT_ID}.iam.gserviceaccount.com" \
  --project="${PROJECT_ID}" \
  --role="roles/iam.workloadIdentityUser" \
  --member="${IAM_PRINCIPAL}"

※このプリンシパルだと、誰でもサービスアカウントを使えてしまうので、
以下に書いているオーナー指定やリポジトリ指定などが必要

workflowファイルのenv.PROVIDERの設定

env:
  PROVIDER: "projects/<PROJECT_NO>/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider"
$ gcloud iam workload-identity-pools providers describe "github-actions-provider" \
  --project="${PROJECT_ID}" \
  --location="global" \
  --workload-identity-pool="github-actions-pool" \ 
  --format="value(name)"
# =>
# projects/<PROJECT_NO>/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider

サービスアカウントに必要なロールを設定

「IAMと管理 > サービスアカウント」で、
作成したサービスアカウントの編集から、
やりたいコマンドに必要なロールを割り当てる。

今回はソースからCloud Runデプロイなので、以下を設定

  • Artifact Registry 書き込み
  • Cloud Build サービスアカウント
  • Cloud Run デベロッパ
  • Cloud Run 起動元

実行してみる

これでプッシュしたりして、うまくデプロイできていればOK(´ω`)

おまけ

属性のマッピング

プロバイダで設定した「属性のマッピング」は、以下のような感じらしい。

assertion.<属性名>attribute.<属性名>マッピングする設定をすると、
Google側でattribute.<属性名>として扱えるようになる。

Google側のattribute.<属性名>を利用して、
認証を許可するリポジトリなどの制限をかけることができる。

{
  "jti": "...",
  "sub": "repo:username/reponame:ref:refs/heads/main",
  "aud": "https://iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider",
  "ref": "refs/heads/main",
  "sha": "d11880f4f451ee35192135525dc974c56a3c1b28",
  "repository": "username/reponame",
  "repository_owner": "username",
  "repository_visibility": "private",
  "repository_id": "74",
  "repository_owner_id": "65",
  "run_id": "1238222155",
  "run_number": "18",
  "run_attempt": "1",
  "actor": "username",
  "actor_id": "12",
  "workflow": "oidc",
  "head_ref": "",
  "base_ref": "",
  "event_name": "push",
  "ref_type": "branch",
  "job_workflow_ref": "username/reponame/.github/workflows/token.yml@refs/heads/main",
  "iss": "https://token.actions.githubusercontent.com",
  "nbf": 1631718827,
  "exp": 1631719727,
  "iat": 1631719427
}

属性を利用したリポジトリの制限

2つの方法がある

  • サービスアカウントに追加した権限のプリンシパルで指定
  • プロバイダの属性条件で指定

サービスアカウントに追加した権限のプリンシパルで指定

上記では、こんな感じでだれでもOKだったけど、

principalSet://<...略>/github-actions-pool/*

https://github.com/my-org/my-repoのようなGitHubリポジトリの場合、

principalSet://<...略>/github-actions-pool//attribute.repository/my-org/my-repo

と指定すると、このリポジトリのみに制限できる。

指定の仕方はいろいろあり、属性マッピングをしておけば、
リポジトリ指定(attribute.repository)や
リポジトリオーナー指定(attribute.repository_owner)などもできる

ID ID の形式
プール内のすべての ID principalSet://iam.googleapis.com/<...略>/*
特定の属性値を持つすべての ID principalSet://iam.googleapis.com/<...略>/attribute.ATTRIBUTE_NAME/ATTRIBUTE_VALUE
単一の ID principal://iam.googleapis.com/<...略>/subject/SUBJECT_ATTRIBUTE_VALUE
グループ内のすべての ID principalSet://iam.googleapis.com/<...略>/group/GROUP_ID

プロバイダの属性条件で指定

プロバイダの「属性のマッピング」の下にある「属性条件」で指定してもOK

https://github.com/my-org/my-repoのようなGitHubリポジトリの場合、

attribute.repository_owner=="my-org" && 
attribute.repository=="my-org/my-repo"

こんな感じで指定すると、リポジトリ指定とリポジトリオーナー指定を両方設定できる。

ハマったポイント

権限や属性周りがわからなくて、かなり時間かかった。。
だいたいこのあたり。。

  • IAMプリンシパルの設定ミス
    • attribute.repositoryのオーナーがない
    • 属性マッピング正しくない
    • ロール割当に「Workload Identity ユーザ」がない
  • サービスアカウントの権限不足
    • Cloud Runのデプロイに必要な権限がない

どんな属性があるのかは、
github/actions-oidc-debuggerを使うと確認できるので、
必要なときは使ってみるといいかも(*´ω`*)


以上!! 仕組みが分かれば、かなり簡単に実行できるように(*´ω`*)!!

参考にしたサイト様