はじめに
ども!丸一日GitHub Actionsのドキュメントを読み漁って関連記事を調査していた龍ちゃんです。さすがに頭が痛いです。先日「GitHub Actions再利用可能ワークフローでEnvironment Secrets継承を実証」という記事で、secrets: inherit
による予想外の動作について検証しました。
あの記事では「公式ドキュメントでは不可能とされているが実際には動作する」という微妙な状況をお伝えしましたが、今回はもっとダイナミックで確実な回避策を思いついたので共有します!
OpenSSL暗号化を使ってEnvironment Secretsを確実に再利用可能ワークフローに渡す方法です。さすがにダイナミックすぎるので、別記事として独立させました。
事前準備:暗号化パスワードの設定
実装前に、暗号化に使用するパスワードを生成してGitHubのRepository Secretsに設定する必要があります。
# 32文字のランダムな文字列を生成
openssl rand -base64 32
# 実行例(毎回異なる値が生成されます)
# 出力例: K9mX7vQ2pL8nR5wE3tY9oU6iA1sD4fG7hJ0kM2nB5vC8
GitHub Repository Secretsへの登録
- GitHubリポジトリの設定画面へ移動
- リポジトリ → Settings → Secrets and variables → Actions
- New repository secretをクリック
- Secret情報を入力
- Name:
ENCRYPTION_PASSWORD
- Secret: 上記で生成したランダム文字列を貼り付け
- Name:
- Add secretで保存
Environment Secretsの設定
併せて、対象となるEnvironment Secretsも設定しておきます:
- Environment設定
- リポジトリ → Settings → Environments
- 対象環境(例:production)を選択
- Environment Secretを追加
- Name:
API_KEY
(例) - Value: 実際のAPIキーや機密情報
- Name:
これで暗号化による受け渡しの準備が完了します。
暗号化による回避策の実装
基本的な発想
Environment Secretsは直接渡せないけど、inputsは渡せる。だったらEnvironment Secretsを暗号化してinputsで渡し、再利用可能ワークフロー側で復号化すればいいじゃない!という発想です。
暗号化にはOpenSSLを使用し、暗号化パスワード自体はRepository Secretsとして管理します。
呼び出し側ワークフロー
name: ci-encryption
on:
workflow_dispatch:
inputs:
deployment_target:
description: "Deployment target"
required: true
default: "production"
type: environment
jobs:
get-secrets:
runs-on: ubuntu-22.04
environment: ${{ inputs.deployment_target }}
outputs:
api_key: ${{ steps.get-secret.outputs.api_key }}
steps:
- name: Get environment secret
id: get-secret
run: |
# Environment SecretsをOpenSSLで暗号化してBase64エンコード
ENCRYPTED_KEY=$(echo -n "${{ secrets.API_KEY }}" | openssl enc -aes-256-cbc -pbkdf2 -salt -k "${{ secrets.ENCRYPTION_PASSWORD }}" | base64 -w 0)
echo "api_key=$ENCRYPTED_KEY" >> $GITHUB_OUTPUT
call-deploy:
needs: [get-secrets]
uses: ./.github/workflows/cd-encryption.yml
with:
api_key: ${{ needs.get-secrets.outputs.api_key }}
secrets:
ENCRYPTION_PASSWORD: ${{ secrets.ENCRYPTION_PASSWORD }}
再利用可能ワークフロー側
name: cd-encryption
on:
workflow_call:
inputs:
api_key:
required: true
type: string
secrets:
ENCRYPTION_PASSWORD:
required: true
jobs:
deploy-job:
runs-on: ubuntu-22.04
name: Deploy with decrypted secrets
steps:
- name: Deploy with decrypted API key
run: |
echo "🔍 暗号化されたシークレットを復号化中..."
if [ -z "${{ inputs.api_key }}" ]; then
echo "::error title=Missing API Key::暗号化されたapi_keyが必要です"
exit 1
fi
# Base64デコード → OpenSSL復号化
DECRYPTED_KEY=$(echo "${{ inputs.api_key }}" | base64 -d | openssl enc -aes-256-cbc -pbkdf2 -d -salt -k "${{ secrets.ENCRYPTION_PASSWORD }}")
echo "::add-mask::$DECRYPTED_KEY"
echo "✅ Environment Secretsの復号化に成功しました"
# ここで実際のデプロイ処理を実行
# curl -H "Authorization: Bearer $DECRYPTED_KEY" ...
動作のポイント
なぜこの方法が動作するのか
- Environment Secretsは環境指定されたjobで取得: 最初のjobで
environment:
を指定してEnvironment Secretsを取得 - 暗号化してoutputsで受け渡し: OpenSSLで暗号化し、Base64エンコードしてoutputsに設定
- inputsで再利用可能ワークフローに渡す: 暗号化された値なのでinputsでの受け渡しが可能
- Repository Secretsで復号化: 暗号化パスワード自体はRepository Secretsなので
secrets: inherit
で渡せる
セキュリティ上の考慮点
メリット:
- Environment Secretsの承認フローが正常に機能
- 暗号化されているためログに平文で残らない
secrets: inherit
の予想外動作に依存しない
注意点:
- 暗号化パスワード(
ENCRYPTION_PASSWORD
)の管理が重要 - Base64エンコードは暗号化ではないので、暗号化は必須
- outputsはログに記録されるため、必ず暗号化済みの値のみ設定する
実装時のポイント
# 暗号化時のコマンド詳細
echo -n "$SECRET_VALUE" | openssl enc -aes-256-cbc -pbkdf2 -salt -k "$ENCRYPTION_PASSWORD" | base64 -w 0
# 復号化時のコマンド詳細
echo "$ENCRYPTED_VALUE" | base64 -d | openssl enc -aes-256-cbc -pbkdf2 -d -salt -k "$ENCRYPTION_PASSWORD"
pbkdf2
: パスワード強化のためのPBKDF2使用salt
: レインボーテーブル攻撃対策base64 -w 0
: 改行なしのBase64エンコード(GITHUB_OUTPUTでの改行問題回避)echo -n
: 末尾改行文字を含めない(復号化時の問題回避)
他手法との比較
手法 | 確実性 | 実装複雑度 | セキュリティ | 公式サポート |
---|---|---|---|---|
secrets: inherit | △(予想外動作) | 低 | 中 | ❌ 不明 |
暗号化手法 | ✅ 確実 | 中 | 高 | ✅ 標準機能のみ |
Repository Secrets | ✅ 確実 | 低 | 低 | ✅ 公式 |
専用キー管理 | ✅ 確実 | 高 | 最高 | ✅ 公式 |
まとめ
今回紹介した暗号化手法は確かにダイナミックすぎる回避策ですが、以下の重要なメリットがあります:
- 確実に動作: GitHubの予想外動作に依存しない標準機能のみで実現
- 適切なEnvironment管理: 環境ごとの承認プロセスと管理が正常に機能
- セキュリティリスク最小化: 必要なSecretのみを選択的に暗号化して受け渡し
- GitHub Secret Scanning回避: 暗号化によりSecret検知機能に引っかからない
特に「secrets: inherit
だと全てのSecretが渡されてしまうのが不安」「Environmentsでしっかり環境管理したい」「GitHubのSecret検知を避けたい」という要望がある場合、この手法は非常に有効です。
ただし、実装の複雑さを考えると、セキュリティ要件がそれほど厳しくない場合は前記事のsecrets: inherit
手法、より高いセキュリティが必要な場合は専用キー管理サービスを選択する方が現実的かもしれません。
それでも「Environment Secretsを確実かつ安全に再利用可能ワークフローで使いたい」という特定のニーズがある場合、この暗号化手法は理想的な解決策になると思います!