【GitHub Actions】PAT を GitHub App トークンに置き換える手順と 3 つの落とし穴
組織のセキュリティ強化の流れで、GitHub の PAT(Personal Access Token)の利用が禁止・制限されるケースが増えています。
個人の PAT は SSH 鍵や OAuth で代替しやすい一方、厄介なのが GitHub Actions に埋め込まれた machine user(bot ユーザー)の PAT です。
本記事では、この PAT を GitHub App のトークンに置き換える手順と、実際に移行検証をして踏んだ 3 つの落とし穴を紹介します。
なぜ machine user の PAT が使われてきたのか
GitHub Actions には、workflow の実行ごとに自動発行される GITHUB_TOKEN があります。
それでも PAT が使われてきたのは、GITHUB_TOKEN に大きな制約が 2 つあるためです。
- 自リポジトリにしか届かない — 別リポジトリの checkout や API 呼び出しができない
- 作成したイベントが後続 workflow を発火させない — 無限ループ防止のため、
GITHUB_TOKENで push した commit や作成した PR ではon: push/on: pull_requestの workflow が動かない
この 2 つを回避する手軽な方法が「bot 用ユーザーを作って PAT を発行し、secret に入れる」だったわけです。
しかし PAT は無期限・広範囲になりがちで、発行したユーザーのアカウントに依存するため、セキュリティ観点では筋の悪い方式でした。
GitHub App のインストールトークンは、この 2 つの制約を回避しつつ、1 時間で自動失効・リポジトリと権限を絞れるという PAT の弱点を解消した代替手段になります。
GitHub App トークンによる置き換えの基本
App の作成でつまずきやすいポイント
Organization の Settings → Developer settings → GitHub Apps から App を作成します。
作成フォームで注意すべきは Webhook の「Active」チェックを外すことです。
デフォルトで有効になっており、そのままだと Webhook URL の入力が必須になって作成できません(トークン発行用途では Webhook は不要です)。
Repository permissions は用途に応じて Contents / Pull requests / Actions あたりを Read and write で付与し、Organization にインストールします。
なお、OAuth App と違って Client Secret は使いません(生成しない限り存在せず、Actions からのトークン生成には不要です)。
workflow での標準形
公式の actions/create-github-app-token を使い、workflow 実行時にトークンを生成します。
steps:
- uses: actions/create-github-app-token@v3
id: app-token
with:
client-id: ${{ vars.ROBOT_APP_CLIENT_ID }}
private-key: ${{ secrets.ROBOT_APP_PRIVATE_KEY }}
# 用途に応じて最小権限に絞る
# repositories: target-repo
# permission-contents: read
- uses: actions/checkout@v7
with:
token: ${{ steps.app-token.outputs.token }}Client ID は秘密情報ではないので Organization variable、秘密鍵(PEM)は Organization secret に登録すると、複数リポジトリへの展開が楽になります。
repositories: と permission-*: を指定すれば、App に付与した権限を上限として、workflow ごとにさらに狭いトークンを発行できます。
ここまでが基本形で、以降は実際の移行検証で踏んだ落とし穴です。
落とし穴 1:GITHUB_TOKEN でも発火する「例外イベント」がある
「GITHUB_TOKEN のイベントは後続 workflow を発火させない」という知識だけで置き換え対象を仕分けすると、判定を誤ります。
GitHub Docs には、この抑止ルールの例外が明記されています。
workflow_dispatch と repository_dispatch は、GITHUB_TOKEN 起因でも常に workflow run を作成します。
つまり「API で dispatch を投げて次の workflow を起動する」タイプの連鎖は、同一リポジトリ内であれば GitHub App トークンすら不要で、GITHUB_TOKEN に置き換えられます。
検証結果から整理した判断基準は次の通りです。
| ケース | 置き換え先 | 理由 |
|---|---|---|
| 同一リポジトリの checkout(後続発火が不要) | GITHUB_TOKEN | 最小権限で足りる |
| 同一リポジトリ内の dispatch 連鎖 | GITHUB_TOKEN | dispatch 系イベントは例外的に発火する |
| push / PR 作成による連鎖 | App トークン | GITHUB_TOKEN では発火が抑止される |
| 別リポジトリの参照(checkout / API / dispatch) | App トークン | GITHUB_TOKEN は自リポジトリにしか届かない |
App トークンより GITHUB_TOKEN の方が管理コストは圧倒的に低いので、この例外を知っているかどうかで移行後の構成のシンプルさが変わります。
落とし穴 2:app-id は非推奨、client-id を使う
create-github-app-token の入力には、歴史的に数値の App ID を渡す app-id が使われてきました。
しかし v3 系で実行すると、次の warning が出ます。
Input ‘app-id’ has been deprecated with message: Use ‘client-id’ instead.
現在は Client ID(Iv23... 形式)を client-id に渡すのが正式な方法です。
ネット上のサンプルコードは app-id のものが大半なので、これから組むなら最初から client-id で統一し、variable 名も ROBOT_APP_CLIENT_ID のように中身と一致した名前にしておくのがおすすめです。
数十ファイルに横展開した後で直すのは大変なので、展開前に気付けるかが分かれ目になります。
落とし穴 3:actor 名が変わり、actor 依存の条件が壊れる
GitHub App を作成すると、GitHub は App 専用の bot ユーザーを自動作成します。
その名前は <App のスラッグ>[bot] という規則で自動的に決まります(App 名 my-robot-app なら my-robot-app[bot])。
問題は、既存 workflow に潜んでいる actor 名依存の条件です。
# 無限ループ防止(bot 自身の push では動かさない)
if: github.actor != 'robot-user'
# bot が作った PR だけ自動 approve する
if: github.event.pull_request.user.login == 'robot-user'トークンを App に切り替えると actor は machine user 名から my-robot-app[bot] に変わるため、この種の条件は移行した瞬間に効かなくなります。
無限ループ防止が壊れると workflow が連鎖暴走し、自動 approve が壊れるとリリースの自動化が止まる、という両方向の事故があり得ます。
移行前に machine user 名で全リポジトリを横断検索して、この条件を洗い出しておくことを強くおすすめします。
また、切り替えを安全にやるなら、次の二段階方式が有効です。
- 先に条件を新旧両対応にする(machine user 名と
<app-slug>[bot]の両方を許可) - トークンを App に切り替える(actor が bot に変わる)
- 安定後に旧 machine user 名を条件から除去する
もう 1 つの注意として、actor 名は App 名から自動で決まるため、後から App をリネームすると actor 名も変わって条件が再び壊れます。
移行を始めたら App 名は凍結する、という運用ルールをセットにしておくと安全です。
まとめ
- machine user の PAT は GitHub App のインストールトークン(1 時間で失効・権限を絞れる)で置き換えられる
- workflow_dispatch / repository_dispatch は GITHUB_TOKEN でも発火する例外があり、同一リポジトリ内の dispatch 連鎖なら App も不要
- create-github-app-token は app-id が非推奨になっており、client-id(Client ID)を使う
- actor 名は <app-slug>[bot] に変わるため、actor 依存の条件を洗い出して二段階で書き換える
PAT の棚卸しから置き換えまでの道のりは長いですが、判断基準さえ固まれば、あとは機械的に展開できる作業です。
同じ移行に取り組む方の参考になれば幸いです。
あわせて読みたい(GitHub Actions 運用)
- GitHub ActionsでPRを自動作成する方法|gh CLIへの移行と落とし穴(PR 作成に必要なトークンの選び方)
- GitHub Actionsのバージョン指定 vs コミットハッシュ指定:どちらを使うべき?(サプライチェーン視点の固定方法)
- GitHub Actions の gh CLI 移行で踏んだ落とし穴
