☁️

GitHub ActionsでWorkload Identityでの認証を入れてGoogle CloudのAPIを叩く

2023/09/11に公開

概要

正直難しいと思ってたのですが、資料を読んでいくと表面上、実装は難しくありませんでした。
GitHub ActionsとGoogle Cloudを連携する場合、json管理とかしなくても済むし、基本的にやっておいて損はないと思います。
ユースケースとしては、例えば、GitHub Actionsで実行した結果(report)をGoogle Cloud Storageにデータを送りたいなどの際に使えると思います。

Identity Poolに対して、providerは複数作成できるため、いろんな GitHub Actionsから利用されるようなパターンでも、provider:script=1:1のような形にしておくのが良いのかと思いました

手順

  1. Google Cloud上でworkload Identityのpoolの作成する
  2. その中に認証providerを作成する
  3. poolに対して、サービスアカウントを紐付ける
  4. GitHub Actionsのstepとpermissionに追加する

実際にやってみる

基本はこちらのsetupの部分をなぞって行きます。
https://github.com/google-github-actions/auth#setting-up-workload-identity-federation

  1. Google Cloud上でworkload Identityのpoolの作成する

    1. 利用したいprojectのIDを環境変数にセットしておく

      export PROJECT_ID="my-project" # update with your value
      
    2. APIを利用できるようにする

      gcloud services enable iamcredentials.googleapis.com --project "${PROJECT_ID}"
      
    3. poolを作成する(locationは global でよいかと

      gcloud iam workload-identity-pools create "my-pool" --project="${PROJECT_ID}" --location="global" --display-name="Demo pool"
      
    4. 作成したpoolのIDを保存しておく

      export WORKLOAD_IDENTITY_POOL_ID=$(gcloud iam workload-identity-pools describe "my-pool" --project="${PROJECT_ID}" --location="global" --format="value(name)")
      
  2. その中に認証providerを作成する

    • prividerを作成する

      gcloud iam workload-identity-pools providers create-oidc "my-provider" \
        --project="${PROJECT_ID}" \
        --location="global" \
        --workload-identity-pool="my-pool" \
        --display-name="sample provider" \
        --attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.repository" \
        --issuer-uri="https://token.actions.githubusercontent.com"
      
  3. poolに対して、サービスアカウントを紐付ける

    1. 紐付ける予定のサービスアカウントを作成する

      gcloud iam service-accounts create "my-service-account" \
        --project "${PROJECT_ID}"
      
    2. poolとservice accountを紐付ける

      # レポジトリを指定します
      export REPO="username/name"
      
      gcloud iam service-accounts add-iam-policy-binding "my-service-account@${PROJECT_ID}.iam.gserviceaccount.com" \
        --project="${PROJECT_ID}" \
        --role="roles/iam.workloadIdentityUser" \
        --member="principalSet://iam.googleapis.com/${WORKLOAD_IDENTITY_POOL_ID}/attribute.repository/${REPO}"
      
    3. 作成できたproviderのIDを取得する

      gcloud iam workload-identity-pools providers describe "my-provider" \
        --project="${PROJECT_ID}" \
        --location="global" \
        --workload-identity-pool="my-pool" \
        --format="value(name)"
      
      • workload identity poolのマッピングを構成してから、権限が利用可能になるまで、最大5分かかることがあるようなので、少し待ちます
  4. GitHub Actionsのstepとpermissionに追加する

    jobs:
      job_id:
        # Add "id-token" with the intended permissions.
        permissions:
          contents: 'read'
          id-token: 'write'
    
        steps:
        - uses: 'actions/checkout@v3' # 大体のユースケースではcheckoutはしていると思うのですが、これが無いとディレクトリがないためworningがでます
    
        - id: 'auth'
          name: 'Authenticate to Google Cloud'
          uses: 'google-github-actions/auth@v1'
          with:
            workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider'
            service_account: 'my-service-account@my-project.iam.gserviceaccount.com'
    
    google-github-actions/auth failed with: retry function failed after 4 attempts: gitHub Actions did not inject $ACTIONS_ID_TOKEN_REQUEST_TOKEN or $ACTIONS_ID_TOKEN_REQUEST_URL into this job. This most likely means the GitHub Actions workflow permissions are incorrect, or this job is being run from a fork. For more information, please see https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token
    
    • workload_identity_providerは poolに対して、サービスアカウントを紐付ける で取得したproviderのIDを入れる
    • service_accountは poolに対して、サービスアカウントを紐付ける で作成したサービスアカウントのemailを入れる

これでGitHub Actionsの後ろのstepに環境変数にADCとして、サービスアカウントが認証されている状態になります。必要な権限を与えてあげて、スクリプトを叩くなりしても良いと思います。

疑問点

Q.workload_identity_providerとservice_accountがバレているとGitHub Actionsからなら、どこからでもできてしまうんでないの?

A. access tokenが取得できないため、できませんでした。
詳しい図などは、こちらの方の記事の図を参考にしていただけると幸いです
https://christina04.hatenablog.com/entry/workload-identity-federation

  • 動作確認してみた感じできる用に見える(認証ができていてjsonが作成できている)

    Run google-github-actions/auth@v1
      with:
        workload_identity_provider: projects/{projectId}/locations/global/workloadIdentityPools/{poolId}/providers/{providerId}
        service_account: service-account@{project}.iam.gserviceaccount.com
        create_credentials_file: true
        export_environment_variables: true
        cleanup_credentials: true
        access_token_lifetime: 3600s
        access_token_scopes: https://www.googleapis.com/auth/cloud-platform
        retries: 3
        backoff: 250
        id_token_include_email: false
    Created credentials file at "/home/runner/work/test-github-actions/test-github-actions/gha-creds-375e94e42c109da8.json"
    
  • ただし、そのアクセストークンを使ってgcloud コマンドを叩こうとすると、gcloudのsetupはできるがaccess tokenが作成できないというエラーで落ちます。(Unable to acquire impersonated credentials=偽装された認証情報を取得できませんって言っているので、ダメ
    一応権限もつけてみたけどダメなものはダメ。変わらずエラーでした

    Run google-github-actions/setup-gcloud@v1
      with:
        skip_install: false
        version: latest
      env:
        CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE: /home/runner/work/test-github-actions/test-github-actions/gha-creds-375e94e42c109da8.json
        GOOGLE_APPLICATION_CREDENTIALS: /home/runner/work/test-github-actions/test-github-actions/gha-creds-375e94e42c109da8.json
        GOOGLE_GHA_CREDS_PATH: /home/runner/work/test-github-actions/test-github-actions/gha-creds-375e94e42c109da8.json
        CLOUDSDK_CORE_PROJECT: {projectId}
        CLOUDSDK_PROJECT: {projectId}
        GCLOUD_PROJECT: {projectId}
        GCP_PROJECT: {projectId}
        GOOGLE_CLOUD_PROJECT: {projectId}
    /usr/bin/tar xz --warning=no-unknown-keyword --overwrite -C /home/runner/work/_temp/6053ebc2-0e1e-41e6-a3d4-2a27248306f0 -f /home/runner/work/_temp/a1627134-a18d-4916-9fe6-2fdf46d1d97b
    Successfully authenticated
    
    Run gcloud storage cp gs://{gcs_path}/hoge.txt ./hoge.txt
      gcloud storage cp gs://{gcs_path}/hoge.txt ./hoge.txt
      shell: /usr/bin/bash -e {0}
      env:
        CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE: /home/runner/work/test-github-actions/test-github-actions/gha-creds-375e94e42c109da8.json
        GOOGLE_APPLICATION_CREDENTIALS: /home/runner/work/test-github-actions/test-github-actions/gha-creds-375e94e42c109da8.json
        GOOGLE_GHA_CREDS_PATH: /home/runner/work/test-github-actions/test-github-actions/gha-creds-375e94e42c109da8.json
        CLOUDSDK_CORE_PROJECT: {projectId}
        CLOUDSDK_PROJECT: {projectId}
        GCLOUD_PROJECT: {projectId}
        GCP_PROJECT: {projectId}
        GOOGLE_CLOUD_PROJECT: {projectId}
        CLOUDSDK_METRICS_ENVIRONMENT: github-actions-setup-gcloud
        CLOUDSDK_METRICS_ENVIRONMENT_VERSION: 1.1.1
      
    
    ERROR: (gcloud.storage.cp) There was a problem refreshing your current auth tokens: ('Unable to acquire impersonated credentials', '{
      "error": {
        "code": 403,
        "message": "Permission \'iam.serviceAccounts.getAccessToken\' denied on resource (or it may not exist).",
        "status": "PERMISSION_DENIED",
        "details": [
          {
            "@type": "type.googleapis.com/google.rpc.ErrorInfo",
            "reason": "IAM_PERMISSION_DENIED",
            "domain": "iam.googleapis.com",
            "metadata": {
              "permission": "iam.serviceAccounts.getAccessToken"
            }
          }
        ]
      }
    }
    ')
    
    Please run:
    
      $ gcloud auth login
    
    to obtain new credentials.
    
    If you have already logged in with a different account, run:
    
      $ gcloud config set account ACCOUNT
    
    to select an already authenticated account to use.
    
    

Discussion