From bfd790a0cc5b106efdef16905ef674efc993c1ba Mon Sep 17 00:00:00 2001 From: "A.suzuki" Date: Tue, 11 Aug 2026 08:18:14 +0000 Subject: [PATCH 1/5] chore: add gcloud and gh command to devcontainer. --- .devcontainer/devcontainer-lock.json | 10 +++++ .devcontainer/devcontainer.json | 59 ++++++++++++++-------------- 2 files changed, 40 insertions(+), 29 deletions(-) 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..a3b9cb5 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,33 +1,34 @@ // 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", + // 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", + "nvmVersion": "latest" + }, + "ghcr.io/devcontainers/features/github-cli": {}, + "ghcr.io/joshuanianji/devcontainer-features/google-cloud-cli": {} + } + // 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" } \ No newline at end of file From 8f7b4fcd9482c3dcfb049b227967244ffc83a54c Mon Sep 17 00:00:00 2001 From: "A.suzuki" Date: Tue, 11 Aug 2026 08:19:15 +0000 Subject: [PATCH 2/5] feat: Add oidc key exchange (GCP). --- .../articles-oidc-authentication.yaml | 199 ++++++++++++++++-- docs/articles/oidc-authentication.md | 151 +++++++++++++ 2 files changed, 338 insertions(+), 12 deletions(-) create mode 100644 docs/articles/oidc-authentication.md 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/docs/articles/oidc-authentication.md b/docs/articles/oidc-authentication.md new file mode 100644 index 0000000..9b69565 --- /dev/null +++ b/docs/articles/oidc-authentication.md @@ -0,0 +1,151 @@ +# 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) From 2ea5e3a3dd7a01f152eb413b14539ce1d1ea33b7 Mon Sep 17 00:00:00 2001 From: "A.suzuki" Date: Tue, 11 Aug 2026 08:54:34 +0000 Subject: [PATCH 3/5] chore: markdownlint and cspell. --- .devcontainer/devcontainer.json | 21 +++++++++---------- .markdownlint-cli2.yaml | 28 +++++++++++++++++++++++++ .vscode/cspell.json | 31 ++++++++++++++++++++++++++++ README.md | 3 ++- chapters/go/README.md | 4 ++-- docs/articles/oidc-authentication.md | 27 ++++++++++++++++-------- 6 files changed, 91 insertions(+), 23 deletions(-) create mode 100644 .markdownlint-cli2.yaml create mode 100644 .vscode/cspell.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a3b9cb5..5718a14 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,8 +2,8 @@ // 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", + "remoteUser": "vscode", "features": { "ghcr.io/devcontainers/features/dotnet": { "tabCompletions": true, @@ -20,15 +20,14 @@ }, "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" + ] + } } - // 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" } \ No newline at end of file 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..ef1e768 --- /dev/null +++ b/.vscode/cspell.json @@ -0,0 +1,31 @@ +{ + "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", + "resourcemanager", + "serviceusage", + "suzu", + "viewports" + ], + "ignoreWords": [], + "import": [] +} \ No newline at end of file diff --git a/README.md b/README.md index 9384328..405f8c1 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ 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 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 index 9b69565..f040774 100644 --- a/docs/articles/oidc-authentication.md +++ b/docs/articles/oidc-authentication.md @@ -2,8 +2,10 @@ ## 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. +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. @@ -11,7 +13,9 @@ With Auth0, I couldn't set up token exchange from the management screen, so I ga ### 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 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) @@ -65,9 +69,11 @@ All operations can be completed using the browser's management screen (console). 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. + > 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]/...). + 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: @@ -135,15 +141,18 @@ Play it safe and hide and manage all GCP infrastructure configuration informatio ### 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). +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. + 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 From 8759e69f2eeda2b492c985160b542974b2ff61d2 Mon Sep 17 00:00:00 2001 From: "A.suzuki" Date: Tue, 11 Aug 2026 09:13:28 +0000 Subject: [PATCH 4/5] chore: vscode settings. --- .vscode/cspell.json | 5 +++-- .vscode/settings.json | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/cspell.json b/.vscode/cspell.json index ef1e768..8388bf7 100644 --- a/.vscode/cspell.json +++ b/.vscode/cspell.json @@ -12,7 +12,7 @@ "yarn.lock", "pnpm-lock.yaml", "**/*.csproj", - "packages.lock.json", + "packages.lock.json" ], "dictionaryDefinitions": [], "dictionaries": [], @@ -21,6 +21,7 @@ "devworks", "Durandal", "gonic", + "multiplatform", "resourcemanager", "serviceusage", "suzu", @@ -28,4 +29,4 @@ ], "ignoreWords": [], "import": [] -} \ No newline at end of file +} 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 From 1698392aae5074e2ecdac3794ecb8ec37812afc3 Mon Sep 17 00:00:00 2001 From: "A.suzuki" Date: Tue, 11 Aug 2026 09:13:39 +0000 Subject: [PATCH 5/5] docs: README.md. --- README.md | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 405f8c1..3f87148 100644 --- a/README.md +++ b/README.md @@ -14,33 +14,40 @@ 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)