diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json index e1f9ea3..49719d0 100644 --- a/.devcontainer/devcontainer-lock.json +++ b/.devcontainer/devcontainer-lock.json @@ -5,6 +5,11 @@ "resolved": "ghcr.io/devcontainers/features/dotnet@sha256:0fc16547ed4db6d7ff2a9f5981d2b93eb314e568affb9958029ad794f1f9a093", "integrity": "sha256:0fc16547ed4db6d7ff2a9f5981d2b93eb314e568affb9958029ad794f1f9a093" }, + "ghcr.io/devcontainers/features/github-cli": { + "version": "1.1.0", + "resolved": "ghcr.io/devcontainers/features/github-cli@sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671", + "integrity": "sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671" + }, "ghcr.io/devcontainers/features/go": { "version": "1.3.4", "resolved": "ghcr.io/devcontainers/features/go@sha256:d85e921f91b41340055bb12b325d9d551170ed04b3b832e33530bf42f167c032", @@ -14,6 +19,11 @@ "version": "2.1.0", "resolved": "ghcr.io/devcontainers/features/node@sha256:586c9a6f7dd40bd3ba2cd41e7f2f88dcc31fbe5d1442afcbf07ffbc66b686857", "integrity": "sha256:586c9a6f7dd40bd3ba2cd41e7f2f88dcc31fbe5d1442afcbf07ffbc66b686857" + }, + "ghcr.io/joshuanianji/devcontainer-features/google-cloud-cli": { + "version": "1.0.0", + "resolved": "ghcr.io/joshuanianji/devcontainer-features/google-cloud-cli@sha256:115b3b4a6c7948c660414a6f9aa601674aa1712e9616f7e436760daeeb4b95f2", + "integrity": "sha256:115b3b4a6c7948c660414a6f9aa601674aa1712e9616f7e436760daeeb4b95f2" } } } diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 97092ac..5718a14 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,33 +1,33 @@ // For format details, see https://aka.ms/devcontainer.json. For config options, see the // README at: https://github.com/devcontainers/templates/tree/main/src/ubuntu { - "name": "Ubuntu", - // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "image": "mcr.microsoft.com/devcontainers/base:ubuntu", - "features": { - "ghcr.io/devcontainers/features/dotnet": { - "tabCompletions": true, - "version": "latest" - }, - "ghcr.io/devcontainers/features/go": { - "version": "latest" - }, - "ghcr.io/devcontainers/features/node": { - "nodeGypDependencies": true, - "version": "lts", - "npmVersion": "lts", - "pnpmVersion": "latest", - "nvmVersion": "latest" - } - } - // Features to add to the dev container. More info: https://containers.dev/features. - // "features": {}, - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - // Use 'postCreateCommand' to run commands after the container is created. - // "postCreateCommand": "uname -a", - // Configure tool-specific properties. - // "customizations": {}, - // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. - // "remoteUser": "root" + "name": "Ubuntu", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "remoteUser": "vscode", + "features": { + "ghcr.io/devcontainers/features/dotnet": { + "tabCompletions": true, + "version": "latest" + }, + "ghcr.io/devcontainers/features/go": { + "version": "latest" + }, + "ghcr.io/devcontainers/features/node": { + "nodeGypDependencies": true, + "version": "lts", + "npmVersion": "lts", + "nvmVersion": "latest" + }, + "ghcr.io/devcontainers/features/github-cli": {}, + "ghcr.io/joshuanianji/devcontainer-features/google-cloud-cli": {} + }, + "customizations": { + "vscode": { + "extensions": [ + "EditorConfig.EditorConfig", + "streetsidesoftware.code-spell-checker", + "DavidAnson.vscode-markdownlint" + ] + } + } } \ No newline at end of file diff --git a/.github/workflows/articles-oidc-authentication.yaml b/.github/workflows/articles-oidc-authentication.yaml index b6e0c14..4264333 100644 --- a/.github/workflows/articles-oidc-authentication.yaml +++ b/.github/workflows/articles-oidc-authentication.yaml @@ -6,13 +6,13 @@ on: workflow_dispatch: inputs: auth_method: - description: '認証方法' + description: '認証方法を選択してください' required: true - default: 'auth0' + default: 'action' type: choice options: - - google - - auth0 + - action + - curl permissions: {} defaults: @@ -24,18 +24,193 @@ concurrency: cancel-in-progress: true jobs: - google-auth: - name: Authenticate to Google Cloud + validation: runs-on: ubuntu-latest - if: ${{ github.event.inputs.auth_method == 'google' }} timeout-minutes: 5 + permissions: + contents: read + steps: - - uses: actions/checkout@v7 + # 🟢 シークレット未設定時の安全対策防衛ステップ + - name: Check Secrets Configuration + env: + GCP_PROJECT_NUMBER: ${{ secrets.GCP_PROJECT_NUMBER }} + GCP_POOL_ID: ${{ secrets.GCP_POOL_ID }} + GCP_PROVIDER_ID: ${{ secrets.GCP_PROVIDER_ID }} + GCP_SA_EMAIL: ${{ secrets.GCP_SA_EMAIL }} + if: env.GCP_PROJECT_NUMBER == '' || env.GCP_POOL_ID == '' || env.GCP_PROVIDER_ID == '' || env.GCP_SA_EMAIL == '' + run: | + echo "::error::GitHub Secrets が設定されていません。リポジトリの設定画面から登録してください。" + exit 1 + + - name: Successful Secrets Validation + run: | + echo "GitHub Secrets が正しく設定されていることを確認しました。" + + oidc-auth-action: + # ------------------------------------------------------------- + # パターンA: 公式Actionを使用する方法 + # ------------------------------------------------------------- + name: OIDC Authentication via Action + needs: validation + if: ${{ inputs.auth_method == 'action' }} - oidc-auth0-curl: - name: OIDC authentication with curl (Auth0) runs-on: ubuntu-latest - if: ${{ github.event.inputs.auth_method == 'auth0' }} timeout-minutes: 5 + permissions: + contents: read + id-token: write # 🟢 一時的な身分証明書(JWT)を発行するために必須 + steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@v7 # 🟢 必ず行ってください + + - name: '[Action] Authenticate to Google Cloud' + id: 'auth' + uses: google-github-actions/auth@v3 + with: + # 以下の入力項目は、ワークロードIDフェデレーションを介してGoogle Cloudへの認証を行うためのものです。 + workload_identity_provider: 'projects/${{ secrets.GCP_PROJECT_NUMBER }}/locations/global/workloadIdentityPools/${{ secrets.GCP_POOL_ID }}/providers/${{ secrets.GCP_PROVIDER_ID }}' + service_account: '${{ secrets.GCP_SA_EMAIL }}' + create_credentials_file: 'true' + + - name: '[Action] Run gcloud CLI Test' + # google-github-actions/auth Actionを使用して認証した後、gcloud CLIを使用してGoogle Cloudのリソースにアクセスできるかをテストします。 + env: + GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }} + run: | + echo "公式Actionによる認証に成功しました。" + gcloud auth list + + echo "------------------------------" + gcloud projects describe "${GCP_PROJECT_ID}" \ + || echo "権限が不足しています。" + + echo "------------------------------" + gcloud services list --project="${GCP_PROJECT_ID}" --enabled \ + || echo "権限が不足しています。" + + echo "------------------------------" + gcloud storage buckets list --project="${GCP_PROJECT_ID}" \ + || echo "権限が不足しています。" + + oidc-auth-curl: + # ------------------------------------------------------------- + # パターンB: curlを使用する場合(OIDCトークン交換の可視化) + # ------------------------------------------------------------- + name: OIDC Authentication with curl + needs: validation + if: ${{ inputs.auth_method == 'curl' }} + + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + id-token: write # 🟢 一時的な身分証明書(JWT)を発行するために必須 + env: + GCP_WORKLOAD_IDENTITY_PROVIDER: 'projects/${{ secrets.GCP_PROJECT_NUMBER }}/locations/global/workloadIdentityPools/${{ secrets.GCP_POOL_ID }}/providers/${{ secrets.GCP_PROVIDER_ID }}' + steps: + - name: '[curl] 1. Get GitHub OIDC Token' + run: | + AUDIENCE="https://iam.googleapis.com/${GCP_WORKLOAD_IDENTITY_PROVIDER}" + + # GCP専用のAudience(デフォルトURL)を指定してGitHubサーバーから生のJWTを取得 + GH_JWT=$(curl -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ + "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=$AUDIENCE" \ + | jq -r '.value') + + # マスクを適用して環境変数ファイルに保存 + echo "::add-mask::$GH_JWT" + echo "GH_JWT=$GH_JWT" >> "$GITHUB_ENV" + echo "JWT length: ${#GH_JWT}" + + # STS(Security Token Service) エンドポイントに送り、GCPの「連携トークン」に交換する + # https://docs.cloud.google.com/iam/docs/reference/sts/rest/v1/TopLevel/token + - name: '[curl] 2. Exchange Token via GCP STS' + env: + GCP_WORKLOAD_IDENTITY_PROVIDER: 'projects/${{ secrets.GCP_PROJECT_NUMBER }}/locations/global/workloadIdentityPools/${{ secrets.GCP_POOL_ID }}/providers/${{ secrets.GCP_PROVIDER_ID }}' + run: | + AUDIENCE="//iam.googleapis.com/${GCP_WORKLOAD_IDENTITY_PROVIDER}" + + RESPONSE=$(curl -X POST \ + "https://sts.googleapis.com/v1/token" \ + -H "Content-Type: application/json" \ + -d "{ + \"grantType\": \"urn:ietf:params:oauth:grant-type:token-exchange\", + \"audience\": \"$AUDIENCE\", + \"scope\": \"https://www.googleapis.com/auth/cloud-platform\", + \"requestedTokenType\": \"urn:ietf:params:oauth:token-type:access_token\", + \"subjectTokenType\": \"urn:ietf:params:oauth:token-type:jwt\", + \"subjectToken\": \"$GH_JWT\" + }") + + # Extract access_token (leave empty string if it does not exist) + FEDERATED_TOKEN=$(echo "$RESPONSE" | jq -r '.access_token // empty') + echo "Token length: ${#FEDERATED_TOKEN}" + + if [ -z "$FEDERATED_TOKEN" ]; then + echo "::error::Failed to exchange token via GCP STS." + echo "$RESPONSE" | jq -r '{error: .error, description: .error_description}' + exit 1 + fi + + echo "::add-mask::$FEDERATED_TOKEN" + echo "FEDERATED_TOKEN=$FEDERATED_TOKEN" >> "$GITHUB_ENV" + + # 連携トークンを使い、指定したサービスアカウントの「最終アクセストークン」を生成する + # https://docs.cloud.google.com/iam/docs/reference/credentials/rest/v1/projects.serviceAccounts/generateAccessToken + - name: '[curl] 3. Assume GCP Service Account' + env: + GCP_NAME: 'projects/-/serviceAccounts/${{ secrets.GCP_SA_EMAIL }}' + run: | + RESPONSE=$(curl -X POST \ + "https://iamcredentials.googleapis.com/v1/${GCP_NAME}:generateAccessToken" \ + -H "Authorization: Bearer $FEDERATED_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{ + \"scope\": [ + \"https://www.googleapis.com/auth/iam\", + \"https://www.googleapis.com/auth/cloud-platform.read-only\", + \"https://www.googleapis.com/auth/devstorage.read_only\" + ] + }") + + # Extract access_token (leave empty string if it does not exist) + GCP_ACCESS_TOKEN=$(echo "$RESPONSE" | jq -r '.accessToken // empty') + echo "Token length: ${#GCP_ACCESS_TOKEN}" + + if [ -z "$GCP_ACCESS_TOKEN" ]; then + echo "::error::Failed to assume GCP Service Account." + echo "$RESPONSE" | jq -r '{error: .error, description: .error_description}' + exit 1 + fi + + echo "::add-mask::$GCP_ACCESS_TOKEN" + echo "GCP_ACCESS_TOKEN=$GCP_ACCESS_TOKEN" >> "$GITHUB_ENV" + + # 生のトークンを使ってGCPのREST APIを直接叩いてみる + - name: '[curl] 4. Run Cloud Storage API Test' + env: + GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }} + run: | + echo "curlによる生のトークン交換(Token Exchange)に成功しました!" + + # 取得した最終トークンをBearerヘッダーにセットしてAPIを実行 + + echo "------------------------------" + curl -f -X GET \ + "https://cloudresourcemanager.googleapis.com/v1/projects/${GCP_PROJECT_ID}" \ + -H "Authorization: Bearer $GCP_ACCESS_TOKEN" \ + -H "Accept: application/json" + + echo "------------------------------" + curl -f -X GET \ + "https://serviceusage.googleapis.com/v1/projects/${GCP_PROJECT_ID}/services?filter=state:ENABLED" \ + -H "Authorization: Bearer $GCP_ACCESS_TOKEN" \ + -H "Accept: application/json" \ + | jq -r '[ .services[].config.name ]' + + echo "------------------------------" + curl -f -X GET \ + "https://storage.googleapis.com/storage/v1/b?project=${GCP_PROJECT_ID}" \ + -H "Authorization: Bearer $GCP_ACCESS_TOKEN" \ + -H "Accept: application/json" diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml new file mode 100644 index 0000000..e052eee --- /dev/null +++ b/.markdownlint-cli2.yaml @@ -0,0 +1,28 @@ +globs: + - "**/*.md" + +ignores: + - ".github/instructions/*.md" + - "**/AnalyzerReleases.*.md" + +gitignore: true + +config: + default: true + + MD013: + line_length: 120 + code_blocks: false + tables: false + headings: false + + MD024: + siblings_only: true + + MD029: + style: ordered + + MD046: + style: fenced + + MD060: false diff --git a/.vscode/cspell.json b/.vscode/cspell.json new file mode 100644 index 0000000..8388bf7 --- /dev/null +++ b/.vscode/cspell.json @@ -0,0 +1,32 @@ +{ + "version": "0.2", + "useGitignore": true, + "ignorePaths": [ + "**/.*/**", + ".editorconfig", + ".git*", + "*.json", + "*.yaml", + "go.mod", + "go.sum", + "yarn.lock", + "pnpm-lock.yaml", + "**/*.csproj", + "packages.lock.json" + ], + "dictionaryDefinitions": [], + "dictionaries": [], + "words": [ + "cicdhandson", + "devworks", + "Durandal", + "gonic", + "multiplatform", + "resourcemanager", + "serviceusage", + "suzu", + "viewports" + ], + "ignoreWords": [], + "import": [] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..02240a7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,21 @@ +{ + "[json][jsonc]": { + "editor.defaultFormatter": "vscode.json-language-features" + }, + "[markdown]": { + "editor.codeActionsOnSave": { + "source.fixAll.markdownlint": "explicit" + }, + "editor.defaultFormatter": "DavidAnson.vscode-markdownlint" + }, + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "files.associations": { + ".*ignore": "ignore", + }, + "files.watcherExclude": { + "**/.git/**": true, + "**/bin/**": true, + "**/obj/**": true + } +} \ No newline at end of file diff --git a/README.md b/README.md index 9384328..3f87148 100644 --- a/README.md +++ b/README.md @@ -8,38 +8,46 @@ This repository is just my personal playground for learning and experimenting wi The content here might actually be helpful to other developers facing similar issues. -However, please keep in mind that this code is based solely on my own perspective and probably has lots of inaccurate or questionable parts! +However, please keep in mind that this code is based solely on my own perspective and probably has lots of +inaccurate or questionable parts! ## Examples - Chapter 1 - Basics of GitHub Actions - - [Hello World](./.github/workflows/go.yaml) + - [Hello World](./.github/workflows/chapter-1-hello.yaml) - Chapter 2 - Hands-on CI/CD implementation - - [Implement CI using pull request triggers](./.github/workflows/ci.yaml) - - [Implement CD functionality using a merge-based approach](./.github/workflows/cd.yaml) + - [Implement CI using pull request triggers](./.github/workflows/chapter-2-1-ci.yaml) + - [Implement CD functionality using a merge-based approach](./.github/workflows/chapter-2-2-cd.yaml) - Chapter 3 - Features required for real-world deployments - - [Available values ​​(context) within the workflow](./.github/workflows/context.yaml) - - [Saving job outputs (artifacts) and passing them on to the next job](./.github/workflows/artifacts.yaml) - - [Job and step execution control (Control Flow)](./.github/workflows/control-flow.yaml) - - [Accelerating CI processes through cache utilization](./.github/workflows/cache.yaml) - - [Workflow triggers](./.github/workflows/trigger.yaml) - - [About GitHub Actions permissions](./.github/workflows/permissions.yaml) + - [Available values ​​(context) within the workflow](./.github/workflows/chapter-3-1-contexts.yaml) + - [Saving job outputs (artifacts) and passing them on to the next job](./.github/workflows/chapter-3-2-artifacts.yaml) + - [Job and step execution control (Control Flow)](./.github/workflows/chapter-3-3-control-flow.yaml) + - [Accelerating CI processes through cache utilization](./.github/workflows/chapter-3-4-caching.yaml) + - [Workflow triggers](./.github/workflows/chapter-3-5-triggers.yaml) + - [About GitHub Actions permissions](./.github/workflows/chapter-3-6-permissions.yaml) - Chapter 4 - Challenges and solutions of using GitHub Actions in large-scale development - - [Using containers via jobs](./.github/workflows/containers.yaml) - - [Application of job execution using matrix strategy](./.github/workflows/matrix.yaml) - - [Use case 1: Dividing tests](./.github/workflows/matrix-use-case1.yaml) - - [Use case 2: Dynamically building jobs](./.github/workflows/matrix-use-case2.yaml) - - [Control the timing of the cache](./.github/workflows/cache-restore.yaml) + - [Using containers via jobs](./.github/workflows/chapter-4-1-containers.yaml) + - [Application of job execution using matrix strategy](./.github/workflows/chapter-4-2-matrix.yaml) + - [Use case 1: Test Splitting](./.github/workflows/chapter-4-2-matrix1-splitting.yaml) + - [Use case 2: Dynamically building jobs](./.github/workflows/chapter-4-2-matrix2-dynamic.yaml) + - [Control the timing of the cache](./.github/workflows/chapter-4-3-cache-handling.yaml) - Chapter 5 - Workflow design for large-scale development - A workflow that takes maintainability into consideration + - [Reuseable workflow (Caller)](./.github/workflows/chapter-5-1-e2e-test.yaml) - [Reuseable workflow (Playwright)](./.github/workflows/reusable-e2e-test.yaml) + - [Custom action (setup-playwright)](./.github/actions/setup-playwright/action.yaml) - Execute at various times - - [When a pull request is opened](./.github/workflows/trigger-test-environment-create.yaml) - - [When the pull request is updated](./.github/workflows/trigger-test-environment-update.yaml) - - [When the pull request is closed](./.github/workflows/trigger-test-environment-destroy.yaml) + - [When a pull request is opened](./.github/workflows/chapter-5-2-timings1-test-environment-create.yaml) + - [When the pull request is updated](./.github/workflows/chapter-5-2-timings2-test-environment-update.yaml) + - [When the pull request is closed](./.github/workflows/chapter-5-2-timings3-test-environment-destroy.yaml) - Workflow optimization - - [Skip the job based on whether there are any changes](./.github/workflows/skips-if-no-changes-detected.yaml) - - [Rerun only failed tests](./.github/workflows/rerun-only-failed-tests.yaml) + - [Skip the job based on whether there are any changes](./.github/workflows/chapter-5-3-skips-if-no-changes-detected.yaml) + - [Rerun only failed tests](./.github/workflows/chapter-5-4-rerun-only-failed-tests.yaml) + +- Articles + - [Automatically detect changes and execute in parallel for each project](./.github/workflows/articles-changed-detection.yaml) + - [Build and push multiplatform Docker images to GitHub Container Registry](./.github/workflows/articles-multi-platform-docker-build.yaml) + - [OIDC authentication (Workload Identity federation)](./.github/workflows/articles-oidc-authentication.yaml) - Appendix - [11 Best Practices for GitHub Actions](./.github/workflows/ex-11-good-practices.yaml) diff --git a/chapters/go/README.md b/chapters/go/README.md index 8348d56..1167215 100644 --- a/chapters/go/README.md +++ b/chapters/go/README.md @@ -1,6 +1,6 @@ # github.com/suzu-devworks/examples-github-action/chapters/go -## Create a new Go module for this chapter. +## Create a new Go module for this chapter 最初のモジュール作成 @@ -39,4 +39,4 @@ go get go@latest ```bash go mod tidy -``` \ No newline at end of file +``` diff --git a/docs/articles/oidc-authentication.md b/docs/articles/oidc-authentication.md new file mode 100644 index 0000000..f040774 --- /dev/null +++ b/docs/articles/oidc-authentication.md @@ -0,0 +1,160 @@ +# OAuth 2.0 and OpenID Connect (OIDC) Authentication + +## Overview + +Learn how GitHub Actions can completely eliminate static passwords (such as private key JSON) and +use OIDC (OpenID Connect) Token Exchange (RFC 8693) to securely access GCP (Google Cloud) resources. +You can experience the essence of OIDC authentication by using curl to reproduce the "raw HTTP communication" +behind the scenes that is automated by the official Action. + +With Auth0, I couldn't set up token exchange from the management screen, so I gave up. + +## Authentication from GitHub Actions using GCP's Workload Identity Federation + +### What is Workload Identity integration? + +Workload Identity integration is a mechanism that securely changes OIDC ID tokens issued by external ID providers +(such as GitHub and AWS) to temporary access tokens in the cloud (such as Google Cloud) through token exchange based +on OAuth 2.0 specifications (RFC 8693), eliminating the need to manage long-term private keys. + +- [Workload Identity Federation - Google Cloud Documentation](https://docs.cloud.google.com/iam/docs/workload-identity-federation?hl=ja) + +### Step 1. Settings on GCP (Google Cloud) side + +All operations can be completed using the browser's management screen (console). + +#### 1. Create a service account + + 1. Open "IAM & Management" > "Service Accounts" in the GCP console. + 2. Click Create Service Account. + 3. Enter any name (e.g. github-actions-study) and click "Create and continue". + 4. Assign the following roles (permissions) to the service account and click "Done". + - "Browser" (roles/browser) Required for the project describe API. + - Required privilege: `resourcemanager.projects.get` + - "Service Usage Viewer" (roles/serviceusage.serviceUsageViewer) Required for the service list API. + - Required privilege: `serviceusage.services.list` + - "Storage Viewer" (roles/storage.viewer) Required for the Cloud Storage bucket list API. + - Required privilege: `storage.buckets.list` + 5. Make a note of the email address of the service account created. + + If you're doing it via the command line: + + ```bash + # Create a service account + gcloud iam service-accounts create github-actions-study \ + --project="{PROJECT ID}" \ + --description="Service account for GitHub Actions OIDC authentication" \ + --display-name="github-actions-study" + + # Assign roles to the service account + gcloud projects add-iam-policy-binding "{PROJECT ID}" \ + --member="serviceAccount:github-actions-study@{PROJECT ID}.iam.gserviceaccount.com" \ + --role="roles/browser" + ``` + +#### 2. Workload Identity linkage (token exchange office) settings + + 1. Open "IAM and Management" > "Workload Identity Integration" from the left menu. + 2. Click “Create a pool” and enter `github-pool` as the name and click Next. + 3. Enter the following on the Add Provider screen. + - Provider selection: `OpenID Connect (OIDC)` + - Provider name: `github-provider` + - Publisher (URL): `https://token.actions.githubusercontent.com` + - Audience: Leave the default selected and continue. + + 4. Configure attribute mapping. + - `google.subject` = `assertion.sub` + - `attribute.repository` = `assertion.repository` + + 5. Configure attribute conditions. + - `assertion.repository_id=="{GITHUB REPOSITORY_ID}"` + > [!NOTE] + > Using name attributes such as `repository` or `repository_owner` presents an attack risk, + so numeric attributes such as `*_id` are recommended. + + 6. Click Save. Make a note of the numerical part of the project number from the complete resource name of + "Provider" displayed on the screen (projects/[project number]/...). + + If you're doing it via the command line: + + ```bash + # Create a workload identity pool + gcloud iam workload-identity-pools create github-pool \ + --project="{PROJECT ID}" \ + --location="global" \ + --display-name="GitHub Actions OIDC Pool" + + # Create a workload identity provider + gcloud iam workload-identity-pools providers create-oidc github-provider \ + --project="{PROJECT ID}" \ + --location="global" \ + --workload-identity-pool="github-pool" \ + --display-name="GitHub Actions OIDC Provider" \ + --issuer-uri="https://token.actions.githubusercontent.com" \ + --attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository" \ + --attribute-condition="assertion.repository_id==\"{GITHUB REPOSITORY_ID}\"" + ``` + + You can find the `{GITHUB REPOSITORY_ID}` in the repository's meta tags or by using the following command: + + ```bash + curl -s https://api.github.com/repos/{username}/{repository} | jq '{id, name}' + ``` + + or + + ```bash + gh api repos/{username}/{repository} --jq '{id, name}' + ``` + +#### 3. Permission to access service account (building trust relationship) + + 1. On the details screen for the pool you created, click "Allow access" at the top. + 2. Under Select Service Account, select the service account you created earlier. + 3. Select "Subset in pool" and enter the attribute name `repository` and the value exactly as your GitHub "username/repository". + 4. Click Save. + + If you're doing it via the command line: + + ```bash + # Allow access to the service account from the workload identity pool + gcloud iam service-accounts add-iam-policy-binding github-actions-study@{PROJECT ID}.iam.gserviceaccount.com \ + --project="{PROJECT ID}" \ + --role="roles/iam.workloadIdentityUser" \ + --member="principalSet://iam.googleapis.com/projects/{PROJECT NUMBER}/locations/global/workloadIdentityPools/github-pool/attribute.repository/{GITHUB REPOSITORY}" + ``` + +### Step 2. Register Secrets on GitHub + +Play it safe and hide and manage all GCP infrastructure configuration information in a secret repository. + + 1. Open Settings > Secrets and variables > Actions in your GitHub repository. + 2. Click "New repository secret" and register all five below. + +| Name to register (half-width uppercase letters) | Value to set (example/confirmation method) | +| --- | --- | +| GCP_PROJECT_NUMBER | Project number displayed on the GCP console (numbers only) | +| GCP_PROJECT_ID | Project ID displayed in the GCP console (string) | +| GCP_POOL_ID | github-pool | +| GCP_PROVIDER_ID | github-provider | +| GCP_SA_EMAIL | Email address of the service account you created | + +### Step 3. Deploy the GitHub Actions workflow + +Place the sample workflow in [.github/workflows/articles-oidc-authentication.yaml](.github/workflows/articles-oidc-authentication.yaml) +in the repository. When starting manually (workflow_dispatch), you can test by switching between official action + (action) and raw HTTP request (curl). + +### Step 4. Try it out + + 1. Open the Actions tab of your GitHub repository. + 2. Select “GCP_OIDC_Method_Comparison” from the left menu. + 3. Press the "Run workflow" button on the right side of the screen, select the authentication method (action or curl), + and execute. + 4. After the execution is complete, open the logs and confirm that access to the GCP side is successful even though + we have not passed any static passwords. + +## References + +- [Configuring OpenID Connect in Google Cloud Platform - GitHub Docs](https://docs.github.com/ja/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-google-cloud-platform) +- [Configure Workload Identity Federation with deployment pipelines  |  Identity and Access Management (IAM)  |  Google Cloud Documentation](https://docs.cloud.google.com/iam/docs/workload-identity-federation-with-deployment-pipelines?hl=ja#github-actions)