静的なシークレットを卒業しよう:GitHub Actions OIDCの実践ガイド

Security tutorial - IT technology blog
Security tutorial - IT technology blog

$5,000のモーニングコール

朝、起きた時にAWSの請求額が一晩で5,000ドルも急増したという通知ほど、最悪な目覚めはありません。このホラーストーリーは通常、プライベートリポジトリに埋もれたり、GitHub Secretsに放置されたりしていた1つのIAMアクセスキーが、悪意のある者の手に渡ることから始まります。私もこれまで、深夜のシフトでSSHブルートフォース攻撃と戦ってきた経験があります。これらの経験から学んだ過酷な真実は、「有効期限のない静的な認証情報はすべて負債である」ということです。パスワードであれAPIキーであれ、有効期限がないものは、いつ爆発してもおかしくない時限爆弾のようなものです。

長年、GitHub ActionsからAWSやGCPにデプロイするには、長寿命のサービスアカウントキーが必要でした。キーを生成し、コピーして、GitHubのリポジトリシークレットに貼り付けるという手順です。「隠されている」から安全だと感じていたかもしれませんが、そのシークレットに有効期限はありません。もし攻撃者がそのキーを手に入れれば、インフラへの永続的なバックステージパスを手に入れたことになります。手動でキーをローテーションしたり、アクセス権を剥奪したりするまで、攻撃者は課金を増やし続けたり、データを持ち出したりすることができてしまいます。

長寿命の認証情報がプレッシャー下で失敗する理由

認証情報の漏洩によるCI/CDの侵害における問題は、通常、暗号化の不足ではありません。それは認証情報の「寿命」にあります。静的なシークレットには、3つの致命的な欠陥があります。

  • 無期限の有効性: 標準的なAWSアクセスキーは、削除しない限り何年も有効です。古く、忘れ去られたプロジェクトが最大のセキュリティホールになります。
  • ローテーションの手間: セキュリティのベストプラクティスでは、90日ごとのキーローテーションが推奨されています。しかし現実には、ほとんどのチームがこれを面倒だと感じています。ローテーションは後回しにされ、シークレットは古いまま放置されます。
  • グローバルな影響範囲: 悪意のある依存関係や設定ミスのログを通じてシークレットが漏洩した場合、攻撃者はいつでも、どこからでもそのキーを使用できます。

私たちに必要なのは、GitHub Actionsが永続的な「パスポート」なしでアイデンティティを証明できるシステムです。自己消滅する「一時的なビジターバッジ」が必要なのです。

転換:GitHub Secrets vs. OIDC

この移行を理解するには、アイデンティティをどのように検証するかを見る必要があります。

従来の方法:マスターキー

IAMキーやJSONファイルを生成してGitHubに保存します。パイプラインが実行されるたびに、GitHubはこの同じキーを送信します。キーは変わることがなく、永久に使えるマスターキーとして機能します。

現代的な方法:OpenID Connect (OIDC)

OIDCを使用すると、GitHub Actionsは短命のトークンをクラウドの一時的な認証情報と交換できるようになります。ワークフローが開始されると、GitHubはIDプロバイダーとして機能し、そのジョブ専用に署名された一意のJWT(JSON Web Token)を発行します。クラウドプロバイダー(AWSまたはGCP)は署名を検証し、そのトークンが特定のリポジトリとブランチのものであることを確認します。検証が完了すると、通常60分で期限が切れる一時的なトークンが返されます。GitHub側には何も保存されません。

OIDCのセットアップ:ステップバイステップの移行

主要なクラウドプロバイダーにこれを実装するには、GitHubのIDサーバーを「信頼」するようにクラウド側を設定する必要があります。

1. AWSでのOIDC設定

まず、AWS IAMコンソールでOIDC IDプロバイダーを作成します。これにより、AWSがGitHubによって発行されたトークンを認識できるようになります。

# AWS CLIを使用してOIDCプロバイダーを作成する
aws iam create-open-id-connect-provider \
    --url "https://token.actions.githubusercontent.com" \
    --client-id-list "sts.amazonaws.com" \
    --thumbprint-list "6938fd4d98bab03faadb97b34396831e3780aea1"

次に、GitHub Action用のIAMロールを定義します。信頼ポリシー(Trust Policy)がゲートキーパーとなります。これにより、特定のリポジトリだけがこのロールを引き受けられるようになります。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:YourUsername/YourRepoName:*"
        }
      }
    }
  ]
}

2. GCPでのOIDC設定

Google CloudではWorkload Identityプールを使用します。これはより抽象的なアプローチですが、きめ細かな制御が可能です。

# 1. Workload Identityプールを初期化する
gcloud iam workload-identity-pools create "github-pool" \
    --location="global" --display-name="GitHub Actions Pool"

# 2. OIDCプロバイダーをリンクする
gcloud iam workload-identity-pools providers create-oidc "github-provider" \
    --location="global" \
    --workload-identity-pool="github-pool" \
    --issuer-uri="https://token.actions.githubusercontent.com" \
    --attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository"

# 3. サービスアカウントをGitHubリポジトリにバインドする
gcloud iam service-accounts add-iam-policy-binding "[email protected]" \
    --role="roles/iam.workloadIdentityUser" \
    --member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool/attribute.repository/YourUsername/YourRepoName"

3. ワークフローの更新

YAMLファイルに1つ、特定の変更を加える必要があります。ジョブに id-token: write 権限を付与しなければなりません。これがないと、GitHubはハンドシェイクに必要なJWTを生成できません。

name: セキュアなクラウドデプロイ
on: [push]

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: AWS認証
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubActionRole
          aws-region: us-east-1

      - name: IDの検証
        run: aws sts get-caller-identity

戦略を鉄壁にするために

OIDCに切り替えることで、シークレットローテーションの悩みから解放され、パイプラインが強化されます。セキュリティを最大化するために、次の3つのルールに従ってください。

  • スコープを絞る: repo:YourUsername/* のようなワイルドカードは使用しないでください。特定のリポジトリを指定し、可能であればデプロイ用のブランチ(例: ref:refs/heads/main)も指定してください。
  • 最小権限の原則を徹底する: OIDCを使用している場合、ロールにAdministrator権限を与えるべきではありません。ジョブがS3へのアップロードのみを行うのであれば、S3のPutObject権限のみを付与してください。
  • ログを監視する: AWS CloudTrailやGCP Cloud Audit Logsを使用しましょう。不審なアイデンティティ連携のリクエストがないか監視してください。

エフェメラル(一時的)なセキュリティこそがゴールドスタンダードです。永続的なシークレットを排除することで、万が一トークンが傍受されても、攻撃者がそれを使おうとする頃には無価値になっていることを保証できます。これが、予期せぬ5,000ドルの請求書で二度と目を覚まさないための最善の方法です。

Share: