GitHub Actions の secrets と環境変数の管理【入門】
概要
GitHub Actions でワークフローを組むと、必ず「APIキーやトークンをどこに置くか」という問題にぶつかります。平文で YAML に書けば動きますが、リポジトリに秘密情報が残ってしまいます。
この記事では、GitHub Actions における secrets と 環境変数(variables / env) の違いと使い分け、設定のスコープ(repository / environment / organization)、そしてよくある事故とその防ぎ方を、入門者向けに整理します。公開ドキュメントで確認できる範囲の一般的な内容のみを扱います。
3つの「入れ物」を区別する
GitHub Actions には似て非なる3つの入れ物があります。まずここを混同しないことが出発点です。
| 種類 | 用途 | ログ表示 | 参照 |
|---|---|---|---|
| Secrets | 秘密情報(鍵・トークン・パスワード) | マスク(***) |
${{ secrets.NAME }} |
| Variables | 秘密でない設定値 | そのまま表示 | ${{ vars.NAME }} |
| env | ステップ/ジョブ単位の環境変数 | そのまま表示 | $NAME |
判断基準はシンプルで、漏れたら困るかです。困るなら secrets、困らないが環境ごとに変えたいなら variables、ワークフロー内だけで使うなら env に置きます。
スコープは3層、狭い方が優先される
secrets / variables は以下の3つのスコープで定義でき、同名の場合は狭いスコープが優先されます。
- Organization レベル: 複数リポジトリで共有
- Repository レベル: そのリポジトリ専用
- Environment レベル:
productionなどの環境に紐づき、承認フローやデプロイブランチ制限といった保護ルールをかけられる
優先順位は Environment > Repository > Organization。本番デプロイ用の鍵は Environment に置き、保護ルールと組み合わせるのが定石です。
基本のデプロイ例
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production # Environment スコープを使う
env:
APP_ENV: production # ジョブ全体で使う非機密の環境変数
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }} # 非機密は vars
- name: Deploy
run: ./deploy.sh
env:
STACK_NAME: ${{ vars.STACK_NAME }}
ポイントは aws-region や STACK_NAME のような非機密値を vars にしていることです。これらを secrets にしてしまうとデバッグ時に値が見えず、原因究明が遅れます。
run の中では env 経由で渡す
run: の中で ${{ secrets.X }} を直接文字列展開すると、値にクォートや特殊文字が含まれたときにシェルが壊れることがあります。env: 経由で環境変数として渡すのが安全です。
- name: Call API
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
run: |
curl -s -H "Authorization: Bearer $API_TOKEN" https://example.com/api
長期鍵を置かない:OIDC という選択肢
クラウドへのデプロイでは、長期的なアクセスキーを secrets に保存する代わりに、OIDC による短期クレデンシャルの取得が推奨されます。この場合、鍵そのものを保管する必要すらなくなり、漏洩面が根本から小さくなります。
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ vars.DEPLOY_ROLE_ARN }}
aws-region: ${{ vars.AWS_REGION }}
確認方法
- マスクの確認:
echo "${{ secrets.FOO }}"を実行するとログには***と出る(マスクが効いている)。ただし Base64 等で加工するとマスクをすり抜けるため、加工して出力しないこと。 - PR からの挙動確認: フォークからの
pull_requestでは secrets は原則渡らない(空になる)。これは信頼できない PR から秘密を抜き取られないための意図された安全策。 - スコープの優先順位: 同名の Repository variable と Environment variable を用意し、
environment:指定ジョブで値が切り替わることを確認する。
注意点
- フォーク PR では secrets は渡らない。CI のビルド確認と、secrets が必要なデプロイを分けて設計する。
- secrets をログに出さない。マスクは万能ではなく、分割・エンコードした値はすり抜ける。
- 秘密でない値まで secrets にしない。デバッグ時に値が見えず原因究明が遅れる。
- 最小権限。
permissions:を明示し、GITHUB_TOKENに不要な書き込み権限を与えない。 - Environment の保護ルールを本番鍵とセットで使い、誰でも本番にデプロイできる状態を避ける。
まとめ
- 秘密情報は secrets、見えて困らない設定値は variables、一時的な値は env に分ける
- スコープは Organization / Repository / Environment の3層、狭い方が優先
- ログ流出を防ぐにはマスク任せにせず「加工して出力しない」を徹底する
- フォーク PR に secrets は渡らない仕様を前提に設計する
- 可能なら長期鍵を置かず OIDC で短期クレデンシャルに寄せる
入門段階でこの切り分けを体に入れておくと、後からワークフローが増えても秘密情報の管理が破綻しにくくなります。
