T-CREATOR

GitHub Actions “permission denied” を解く:権限不足・保護ブランチ・環境保護の罠

GitHub Actions “permission denied” を解く:権限不足・保護ブランチ・環境保護の罠

GitHub Actions を実行していて突然「Error: permission denied」というメッセージに遭遇したことはありませんか?ワークフローは正常に見えるのに、なぜか権限エラーで失敗してしまう。このエラーは初心者だけでなく、経験者でも原因の特定に時間がかかることがあります。

本記事では、GitHub Actions で発生する permission denied エラーの主要な 3 つのパターンを徹底解説します。権限不足、保護ブランチ、環境保護という 3 つの「罠」を理解し、それぞれの解決策を具体的なコード例とともに学んでいきましょう。エラーコードと解決手順を明確に示すので、次回エラーに遭遇したときにはすぐに対処できるようになりますよ。

背景

GitHub Actions の権限システム

GitHub Actions には複数のレイヤーで権限管理の仕組みが組み込まれています。ワークフローが実行されるとき、GitHub は自動的に GITHUB_TOKEN という一時的なトークンを発行します。このトークンを使って、コードのチェックアウト、プルリクエストへのコメント、リリースの作成などの操作を行うのです。

しかし、このトークンの権限は無制限ではありません。デフォルトでは読み取り専用になっている操作もあれば、書き込み権限が必要な操作もあります。加えて、リポジトリの設定やブランチ保護ルール、環境保護ルールによって、さらに制限がかかることがあります。

権限の 3 つのレイヤー

GitHub Actions の権限は以下の 3 つのレイヤーで制御されていて、それぞれが permission denied エラーの原因となり得ます。

以下の図で、権限チェックの流れを確認してみましょう。

mermaidflowchart TD
  start["ワークフロー実行開始"] --> token["GITHUB_TOKEN 発行"]
  token --> check1["レイヤー1:<br/>トークン権限チェック"]
  check1 -->|権限不足| error1["Error: permission denied<br/>(トークン権限不足)"]
  check1 -->|OK| check2["レイヤー2:<br/>ブランチ保護ルール"]
  check2 -->|違反| error2["Error: permission denied<br/>(保護ブランチ)"]
  check2 -->|OK| check3["レイヤー3:<br/>環境保護ルール"]
  check3 -->|承認待ち/違反| error3["Error: permission denied<br/>(環境保護)"]
  check3 -->|OK| success["実行成功"]

図のように、3 つのチェックポイントを通過して初めてワークフローが正常に実行されます。どこか 1 つでも引っかかると permission denied が発生してしまいます。

#レイヤー名チェック内容設定場所
1トークン権限GITHUB_TOKEN の読み書き権限ワークフロー YAML または リポジトリ設定
2ブランチ保護プッシュ・マージの制限リポジトリ設定 > Branches
3環境保護デプロイ承認・制限リポジトリ設定 > Environments

この 3 つのレイヤーを理解することが、permission denied エラーを素早く解決する鍵となるのです。

課題

permission denied が発生する 3 つのパターン

GitHub Actions で permission denied エラーが発生するとき、大きく分けて 3 つのパターンがあります。それぞれのパターンでエラーメッセージや原因が異なるため、正確に見極めることが重要です。

以下では、各パターンの特徴と典型的なエラーメッセージを整理しました。

パターン 1:トークン権限不足

最もよく遭遇するのが、GITHUB_TOKEN の権限不足によるエラーです。

典型的なエラーメッセージ

plaintextError: Resource not accessible by integration
HTTP Error: 403 Forbidden
Error: permission denied while trying to connect to the Docker daemon socket

発生するタイミング

  • プルリクエストにコメントを投稿しようとしたとき
  • Issue を作成・更新しようとしたとき
  • リリースを作成しようとしたとき
  • パッケージを公開しようとしたとき
  • Docker イメージをプッシュしようとしたとき

このパターンは、ワークフローファイルで permissions を明示的に設定していない、または必要な権限を指定していないことが原因です。GitHub のデフォルト設定では、一部の操作に対して読み取り専用になっていることがあるため注意が必要でしょう。

パターン 2:保護ブランチルール違反

2 つ目のパターンは、ブランチ保護ルールに引っかかるケースです。

典型的なエラーメッセージ

plaintextError: Resource not accessible by integration
Error: push declined due to repository rule violations
protected branch hook declined
Error: Required status check "build" must pass before merging

発生するタイミング

  • mainmaster などの保護されたブランチに直接プッシュしようとしたとき
  • ステータスチェックが完了していない状態でマージしようとしたとき
  • レビュー承認を得ていないプルリクエストをマージしようとしたとき
  • 管理者以外が保護ブランチを更新しようとしたとき

このパターンは、リポジトリのブランチ保護設定と、ワークフローで実行しようとしている操作が矛盾していることが原因になります。

パターン 3:環境保護ルール違反

3 つ目のパターンは、環境(Environments)の保護ルールに関連したエラーです。

典型的なエラーメッセージ

plaintextError: Resource not accessible by integration
Error: Deployment to environment "production" requires approval
Error: Environment protection rules prevent deployment
Error: Required reviewers have not approved this deployment

発生するタイミング

  • 本番環境へのデプロイを実行しようとしたとき
  • 承認者の承認を得ずにデプロイしようとしたとき
  • 特定のブランチ以外からデプロイしようとしたとき
  • 待機時間(wait timer)が経過していない状態でデプロイしようとしたとき

環境保護は、本番環境への誤ったデプロイを防ぐための重要な仕組みです。しかし、設定が厳しすぎると開発の妨げになることもあるため、適切なバランスが求められます。

以下の図で、3 つのパターンの関係性を整理してみましょう。

mermaidflowchart LR
  workflow["ワークフロー実行"] --> pattern1["パターン1<br/>トークン権限不足"]
  workflow --> pattern2["パターン2<br/>保護ブランチ違反"]
  workflow --> pattern3["パターン3<br/>環境保護違反"]

  pattern1 --> cause1["原因:<br/>permissions 未設定"]
  pattern2 --> cause2["原因:<br/>ブランチ保護ルール"]
  pattern3 --> cause3["原因:<br/>環境保護ルール"]

  cause1 --> fix1["解決:<br/>YAML で権限追加"]
  cause2 --> fix2["解決:<br/>別ブランチ経由/<br/>例外設定"]
  cause3 --> fix3["解決:<br/>承認フロー/<br/>ルール調整"]

この 3 つのパターンを見極めることで、エラーの解決時間を大幅に短縮できるのです。

解決策

ここからは、3 つのパターンそれぞれの具体的な解決策を段階的に見ていきます。エラーログを確認しながら、どのパターンに該当するかを判断し、適切な対処を行いましょう。

パターン 1 の解決策:トークン権限の付与

トークン権限不足による permission denied は、ワークフローファイルで permissions を明示的に設定することで解決できます。

手順 1:エラーログの確認

まず、ワークフローの実行ログを確認して、どの操作で permission denied が発生しているかを特定します。

plaintextError: Resource not accessible by integration
HTTP 403: Forbidden

このエラーが出ている場合、トークン権限不足の可能性が高いです。

手順 2:必要な権限の特定

操作に応じて、以下の表から必要な権限を特定してください。

#操作内容必要な権限スコープ名
1プルリクエストへのコメント書き込みpull-requests: write
2Issue の作成・更新書き込みissues: write
3リリースの作成書き込みcontents: write
4パッケージの公開書き込みpackages: write
5ワークフロー実行のトリガー書き込みactions: write

手順 3:permissions の追加

ワークフローファイルの冒頭またはジョブ単位で permissions を設定します。

パターン A:ワークフロー全体に権限を付与

yamlname: CI Workflow

on:
  pull_request:
    branches: [main]
yaml# ワークフロー全体に適用される権限設定
permissions:
  contents: write # リポジトリへの書き込み
  pull-requests: write # PR へのコメント
  issues: write # Issue の操作

この設定により、ワークフロー内のすべてのジョブが指定した権限を持つようになります。

パターン B:ジョブ単位で権限を付与

yamljobs:
  comment:
    runs-on: ubuntu-latest

    # このジョブのみに適用される権限設定
    permissions:
      pull-requests: write
yamlsteps:
  - name: Comment on PR
    uses: actions/github-script@v7
    with:
      script: |
        github.rest.issues.createComment({
          issue_number: context.issue.number,
          owner: context.repo.owner,
          repo: context.repo.repo,
          body: 'ビルドが成功しました!'
        })

ジョブ単位で設定することで、最小権限の原則に従ったセキュアな構成が実現できます。

手順 4:権限の検証

設定を変更したら、ワークフローを再実行して権限が正しく付与されているか確認しましょう。

yaml- name: Verify permissions
  run: |
    echo "Token permissions:"
    curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
         https://api.github.com/rate_limit

API レスポンスで権限スコープが確認できれば成功です。

パターン 2 の解決策:保護ブランチへの対処

保護ブランチに直接プッシュしようとして permission denied が発生した場合は、以下の解決策を検討します。

手順 1:エラー内容の確認

ログに以下のようなメッセージが含まれているか確認してください。

plaintextError: push declined due to repository rule violations
protected branch hook declined

このエラーが出ている場合、ブランチ保護ルールに抵触しています。

手順 2:ブランチ保護設定の確認

リポジトリの設定画面で、保護ブランチのルールを確認します。

  1. リポジトリページで Settings をクリック
  2. 左メニューから Branches を選択
  3. Branch protection rules セクションで該当ブランチを確認

手順 3:解決方法の選択

保護ブランチへの対処には、主に 3 つのアプローチがあります。

方法 A:プルリクエスト経由でマージ

最も推奨される方法は、保護ブランチに直接プッシュせず、別ブランチからプルリクエストを作成してマージすることです。

yamlname: Update Version

on:
  workflow_dispatch:

jobs:
  update:
    runs-on: ubuntu-latest
yamlpermissions:
  contents: write
  pull-requests: write

steps:
  # リポジトリをチェックアウト
  - uses: actions/checkout@v4
yaml# 新しいブランチを作成
- name: Create branch
  run: |
    git checkout -b update-version-${{ github.run_number }}
    git config user.name "github-actions[bot]"
    git config user.email "github-actions[bot]@users.noreply.github.com"
yaml# ファイルを更新してコミット
- name: Update version
  run: |
    echo "1.2.3" > VERSION
    git add VERSION
    git commit -m "chore: update version to 1.2.3"
    git push origin update-version-${{ github.run_number }}
yaml# プルリクエストを作成
- name: Create Pull Request
  uses: actions/github-script@v7
  with:
    script: |
      await github.rest.pulls.create({
        owner: context.repo.owner,
        repo: context.repo.repo,
        title: 'Update version to 1.2.3',
        head: `update-version-${context.runNumber}`,
        base: 'main'
      })

このワークフローは、保護ブランチに直接プッシュせず、プルリクエストを自動作成します。

方法 B:GitHub App トークンの使用

管理者権限が必要な場合、GitHub App を作成してそのトークンを使用する方法があります。

yaml- name: Generate token
  id: generate_token
  uses: actions/create-github-app-token@v1
  with:
    app-id: ${{ secrets.APP_ID }}
    private-key: ${{ secrets.APP_PRIVATE_KEY }}
yaml- name: Push to protected branch
  env:
    GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
  run: |
    git config user.name "github-actions[bot]"
    git config user.email "github-actions[bot]@users.noreply.github.com"
    git push origin main

GitHub App トークンは、GITHUB_TOKEN よりも強力な権限を持つため、保護ブランチへのプッシュが可能になります。

方法 C:保護ルールの例外設定

リポジトリ管理者は、GitHub Actions を保護ルールの例外として設定できます。

  1. Settings > Branches > 該当ブランチの Edit をクリック
  2. Do not allow bypassing the above settings のチェックを外す
  3. Allow specified actors to bypass required pull requestsgithub-actions を追加

ただし、この方法はセキュリティリスクが高まるため、慎重に検討する必要があります。

パターン 3 の解決策:環境保護ルールへの対処

環境保護によって permission denied が発生した場合の解決策を見ていきます。

手順 1:エラー内容の確認

以下のようなエラーメッセージが表示されているか確認してください。

plaintextError: Deployment to environment "production" requires approval
Error: Environment protection rules prevent deployment

このエラーは、環境保護ルールが原因です。

手順 2:環境設定の確認

リポジトリの環境設定を確認します。

  1. リポジトリページで Settings をクリック
  2. 左メニューから Environments を選択
  3. 該当環境(例:production)の設定を確認

手順 3:解決方法の選択

環境保護への対処には、以下のアプローチがあります。

方法 A:承認フローの実装

本番環境へのデプロイには承認フローを組み込みましょう。

yamlname: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest

    # 環境を指定(承認が必要)
    environment:
      name: production
      url: https://example.com
yamlsteps:
  - uses: actions/checkout@v4

  - name: Deploy application
    run: |
      echo "Deploying to production..."
      # デプロイコマンドをここに記述

この設定により、デプロイ実行前に承認者の承認が必要になります。承認待ちの状態では、ワークフローは一時停止し、承認後に自動的に再開されます。

方法 B:環境ごとにジョブを分離

開発環境と本番環境で異なる保護レベルを設定する場合、ジョブを分離すると管理しやすくなります。

yamljobs:
  deploy-dev:
    runs-on: ubuntu-latest
    environment: development

    steps:
      - name: Deploy to dev
        run: echo "Deploying to development..."
yamldeploy-prod:
  runs-on: ubuntu-latest
  # 本番環境は dev デプロイ成功後に実行
  needs: deploy-dev
  environment: production

  steps:
    - name: Deploy to production
      run: echo "Deploying to production..."

needs を使うことで、開発環境デプロイが成功してから本番環境デプロイが実行されるようになります。

方法 C:ブランチ制限の設定

環境設定で、デプロイ可能なブランチを制限することもできます。

  1. Settings > Environments > 該当環境を選択
  2. Deployment branchesSelected branches を選択
  3. Add deployment branch rulemain など許可するブランチを追加
yaml# main ブランチからのみデプロイ可能
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production

この設定により、指定したブランチ以外からのデプロイは自動的にブロックされます。

方法 D:待機時間の設定

即座にデプロイせず、一定時間待機してからデプロイする設定も有効です。

  1. Settings > Environments > 該当環境を選択
  2. Wait timer で待機時間(分)を設定(例:5 分)
yamlenvironment:
  name: production

設定した時間が経過するまで、デプロイは開始されません。この間に問題に気づいた場合は、デプロイをキャンセルできます。

具体例

ここでは、実際のプロジェクトで遭遇しやすいシナリオを取り上げ、permission denied エラーの発生から解決までを具体的に見ていきましょう。

シナリオ 1:プルリクエストへのコメント投稿失敗

状況

プルリクエストが作成されたときに、自動でコメントを投稿するワークフローを実装しました。しかし、実行すると permission denied が発生してしまいます。

失敗したワークフロー

yamlname: PR Comment

on:
  pull_request:
    types: [opened]

jobs:
  comment:
    runs-on: ubuntu-latest
yamlsteps:
  - name: Comment on PR
    uses: actions/github-script@v7
    with:
      script: |
        github.rest.issues.createComment({
          issue_number: context.issue.number,
          owner: context.repo.owner,
          repo: context.repo.repo,
          body: 'プルリクエストありがとうございます!'
        })

エラーログ

plaintextError: HttpError: Resource not accessible by integration
    at /home/runner/work/_actions/actions/github-script/v7/dist/index.js:1234:21
Status: 403
Message: Resource not accessible by integration

エラーコード 403 Forbidden と「Resource not accessible by integration」というメッセージから、トークン権限不足であることが分かります。

解決手順

  1. プルリクエストへのコメントには pull-requests: write 権限が必要
  2. ワークフローファイルに permissions を追加

修正後のワークフロー

yamlname: PR Comment

on:
  pull_request:
    types: [opened]

jobs:
  comment:
    runs-on: ubuntu-latest

    # 必要な権限を明示的に付与
    permissions:
      pull-requests: write
yamlsteps:
  - name: Comment on PR
    uses: actions/github-script@v7
    with:
      script: |
        github.rest.issues.createComment({
          issue_number: context.issue.number,
          owner: context.repo.owner,
          repo: context.repo.repo,
          body: 'プルリクエストありがとうございます!'
        })

この修正により、ワークフローは正常にコメントを投稿できるようになりました。

シナリオ 2:保護ブランチへのバージョンタグ作成失敗

状況

リリースワークフローで、main ブランチに自動的にバージョンタグを作成しようとしましたが、permission denied が発生しました。main ブランチは保護されています。

失敗したワークフロー

yamlname: Create Release Tag

on:
  workflow_dispatch:
    inputs:
      version:
        description: 'Version number'
        required: true

jobs:
  tag:
    runs-on: ubuntu-latest
    permissions:
      contents: write
yamlsteps:
  - uses: actions/checkout@v4

  - name: Create tag
    run: |
      git config user.name "github-actions[bot]"
      git config user.email "github-actions[bot]@users.noreply.github.com"
      git tag v${{ github.event.inputs.version }}
      git push origin v${{ github.event.inputs.version }}

エラーログ

plaintextremote: error: GH006: Protected branch update failed for refs/heads/main.
remote: error: Cannot push to protected branch
To https://github.com/user/repo.git
 ! [remote rejected] v1.0.0 -> v1.0.0 (protected branch hook declined)
error: failed to push some refs to 'https://github.com/user/repo.git'
Error: Process completed with exit code 1.

「protected branch hook declined」から、保護ブランチルールに抵触していることが分かります。

解決手順

  1. タグ作成は別ブランチから行い、プルリクエスト経由で main にマージ
  2. または、GitHub App トークンを使用して保護ブランチルールをバイパス

修正後のワークフロー(方法 A:GitHub App 使用)

yamlname: Create Release Tag

on:
  workflow_dispatch:
    inputs:
      version:
        description: 'Version number'
        required: true

jobs:
  tag:
    runs-on: ubuntu-latest
yamlsteps:
  - uses: actions/checkout@v4

  # GitHub App トークンを生成
  - name: Generate token
    id: generate_token
    uses: actions/create-github-app-token@v1
    with:
      app-id: ${{ secrets.APP_ID }}
      private-key: ${{ secrets.APP_PRIVATE_KEY }}
yaml# App トークンを使ってタグを作成
- name: Create and push tag
  env:
    GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
  run: |
    git config user.name "github-actions[bot]"
    git config user.email "github-actions[bot]@users.noreply.github.com"
    git tag v${{ github.event.inputs.version }}
    git push origin v${{ github.event.inputs.version }}

GitHub App を作成し、そのトークンを使用することで、保護ブランチへのプッシュが可能になりました。

シナリオ 3:本番環境へのデプロイが承認待ちでブロック

状況

本番環境へのデプロイワークフローを実行したところ、「deployment requires approval」というメッセージが表示され、デプロイが進みません。

実行したワークフロー

yamlname: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
yamlsteps:
  - uses: actions/checkout@v4

  - name: Deploy
    run: |
      echo "Deploying to production..."
      # デプロイスクリプト実行

エラーログ

plaintextWaiting for deployment approval...
Deployment to environment "production" requires approval from required reviewers.

これはエラーではなく、環境保護ルールが正常に機能している状態です。承認者の承認が必要です。

解決手順

  1. リポジトリの Actions タブから該当ワークフローを開く
  2. Review deployments ボタンをクリック
  3. 承認者が内容を確認し、Approve and deploy をクリック

承認フローの可視化

mermaidsequenceDiagram
  participant Dev as 開発者
  participant GH as GitHub Actions
  participant Approver as 承認者
  participant Prod as 本番環境

  Dev->>GH: main ブランチへプッシュ
  GH->>GH: ワークフロー実行開始
  GH->>Approver: 承認リクエスト送信
  Note over GH,Approver: 承認待ち状態
  Approver->>GH: デプロイ承認
  GH->>Prod: デプロイ実行
  Prod->>GH: デプロイ完了
  GH->>Dev: 実行結果通知

この図のように、承認者が承認するまでワークフローは待機状態となり、承認後に自動的にデプロイが再開されます。

自動承認が必要な場合の対応

開発環境など、承認不要にしたい場合は環境設定を変更します。

  1. Settings > Environments > 該当環境を選択
  2. Required reviewers のチェックを外す
  3. または、開発環境用の別環境を作成
yamljobs:
  deploy-dev:
    runs-on: ubuntu-latest
    # 開発環境は承認不要
    environment: development
    steps:
      - name: Deploy to dev
        run: echo "Deploying..."

  deploy-prod:
    runs-on: ubuntu-latest
    needs: deploy-dev
    # 本番環境は承認必要
    environment: production
    steps:
      - name: Deploy to prod
        run: echo "Deploying..."

この設定により、開発環境へは自動デプロイ、本番環境へは承認後デプロイという運用が実現できます。

シナリオ 4:複数権限が必要な複雑なワークフロー

状況

以下の操作を 1 つのワークフローで実行したいケースです。

  • プルリクエストへのコメント
  • テストカバレッジレポートの Issue 作成
  • ビルド成果物の Release への添付

完全なワークフロー例

yamlname: CI with Multiple Permissions

on:
  pull_request:
    branches: [main]

jobs:
  ci:
    runs-on: ubuntu-latest

    # 必要な権限をすべて列挙
    permissions:
      contents: write # Release 作成
      pull-requests: write # PR コメント
      issues: write # Issue 作成
yamlsteps:
  - uses: actions/checkout@v4

  # テストの実行
  - name: Run tests
    run: |
      yarn install
      yarn test --coverage
yaml# プルリクエストにコメント
- name: Comment coverage
  uses: actions/github-script@v7
  with:
    script: |
      const coverage = '85%'; // 実際は計算結果を使用
      github.rest.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body: `テストカバレッジ: ${coverage}`
      })
yaml# カバレッジが低い場合 Issue を作成
- name: Create issue if coverage low
  if: failure()
  uses: actions/github-script@v7
  with:
    script: |
      github.rest.issues.create({
        owner: context.repo.owner,
        repo: context.repo.repo,
        title: 'テストカバレッジが低下しています',
        body: '現在のカバレッジが基準を下回っています。'
      })

このように、複数の権限が必要な場合は permissions セクションにすべての必要な権限を列挙することで、permission denied を回避できます。

まとめ

GitHub Actions で発生する permission denied エラーは、3 つの主要なパターンに分類できます。それぞれのパターンを正確に見極め、適切な解決策を適用することが重要です。

まず、トークン権限不足のパターンでは、ワークフローファイルで permissions を明示的に設定することで解決します。contentspull-requestsissues などの必要な権限を、ワークフロー全体またはジョブ単位で付与しましょう。最小権限の原則に従い、必要な権限だけを付与することがセキュリティ上の推奨事項となります。

次に、保護ブランチルール違反のパターンでは、直接プッシュを避けてプルリクエスト経由でマージするか、GitHub App トークンを使用する方法が有効です。保護ブランチは重要なコードを守るための仕組みなので、安易に例外設定を追加せず、ワークフローの設計を見直すことをお勧めします。

最後に、環境保護ルール違反のパターンでは、承認フローを組み込むことで安全なデプロイが実現できます。本番環境へのデプロイには承認者の承認を必須とし、開発環境は自動デプロイにするなど、環境ごとに適切な保護レベルを設定しましょう。

これらの知識を身につけることで、GitHub Actions の permission denied エラーに遭遇したときも、慌てずに原因を特定し、迅速に対処できるようになります。エラーログをしっかり読み、どのレイヤーで権限チェックが失敗しているのかを見極めることが、解決への最短ルートです。

今後のワークフロー開発では、最初から適切な権限設定を行うことで、permission denied エラーを未然に防ぐことができるでしょう。セキュリティと利便性のバランスを取りながら、効率的な CI/CD パイプラインを構築していきましょう。

関連リンク