GitHub Actions Environment SecretsをOpenSSL暗号化で安全継承する回避策

GitHub Actions Environment SecretsをOpenSSL暗号化で安全継承する回避策

はじめに

ども!丸一日GitHub Actionsのドキュメントを読み漁って関連記事を調査していた龍ちゃんです。さすがに頭が痛いです。先日「GitHub Actions再利用可能ワークフローでEnvironment Secrets継承を実証」という記事で、secrets: inheritによる予想外の動作について検証しました。

あの記事では「公式ドキュメントでは不可能とされているが実際には動作する」という微妙な状況をお伝えしましたが、今回はもっとダイナミックで確実な回避策を思いついたので共有します!

OpenSSL暗号化を使ってEnvironment Secretsを確実に再利用可能ワークフローに渡す方法です。さすがにダイナミックすぎるので、別記事として独立させました。

事前準備:暗号化パスワードの設定

実装前に、暗号化に使用するパスワードを生成してGitHubのRepository Secretsに設定する必要があります。

# 32文字のランダムな文字列を生成
openssl rand -base64 32

# 実行例(毎回異なる値が生成されます)
# 出力例: K9mX7vQ2pL8nR5wE3tY9oU6iA1sD4fG7hJ0kM2nB5vC8

GitHub Repository Secretsへの登録

  1. GitHubリポジトリの設定画面へ移動
    • リポジトリ → Settings → Secrets and variables → Actions
  2. New repository secretをクリック
  3. Secret情報を入力
    • Name: ENCRYPTION_PASSWORD
    • Secret: 上記で生成したランダム文字列を貼り付け
  4. Add secretで保存

Environment Secretsの設定

併せて、対象となるEnvironment Secretsも設定しておきます:

  1. Environment設定
    • リポジトリ → Settings → Environments
    • 対象環境(例:production)を選択
  2. Environment Secretを追加
    • Name: API_KEY(例)
    • Value: 実際のAPIキーや機密情報

これで暗号化による受け渡しの準備が完了します。

暗号化による回避策の実装

基本的な発想

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" ...

動作のポイント

なぜこの方法が動作するのか

  1. Environment Secretsは環境指定されたjobで取得: 最初のjobでenvironment:を指定してEnvironment Secretsを取得
  2. 暗号化してoutputsで受け渡し: OpenSSLで暗号化し、Base64エンコードしてoutputsに設定
  3. inputsで再利用可能ワークフローに渡す: 暗号化された値なのでinputsでの受け渡しが可能
  4. 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を確実かつ安全に再利用可能ワークフローで使いたい」という特定のニーズがある場合、この暗号化手法は理想的な解決策になると思います!


参考リンク

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です