CI/CDとIaCのシークレットは、コードよりもstateとログと長期キーから漏れます。OIDCによるキーレス認証、Vault・SOPS・クラウドSecrets Managerの使い分け、最小権限・ローテーション・監査までを、海外の一次情報をたどりながら日本の現場向けに整理します。

CI/CDとIaCのシークレット管理は、「秘密の値をどこにしまうか」という置き場所の話に見えて、実際には「どこから漏れるか」を先に潰す設計の話です。コードにハードコードしなければ安全、という単純な話では済みません。漏洩の主経路は、コードよりもむしろstateジョブログ長期キーの三つに寄っています。本稿では、この三経路を軸に、OIDCによるキーレス認証、Vault / SOPS / クラウドのSecrets Managerの使い分け、そして最小権限・ローテーション・監査の実務を、海外の一次情報をたどりながら整理します。

01漏れる経路は三つ — state・ログ・長期キー

まず「どこから漏れるか」を三つに分解します。設計の議論はここを共通認識にしないと噛み合いません。

現場のコツ:「シークレットをどこに保管するか」を議論する前に、この三経路のうち自社のパイプラインでどれが開いているかを棚卸ししてください。多くの現場では長期キーが一番の穴で、そこはツール選定ではなく後述のOIDCで根本的に閉じられます。
シークレットが漏れる三つの経路 CI/CD ・ IaC パイプライン実行 state / plan 平文で機微値を保存 ジョブログ echo / env で露出 長期キー CI登録の長寿命鍵 対策:暗号化 ・ OIDCキーレス ・ マスク 三経路それぞれを別々に塞ぐ
図:漏洩は単一の対策では塞げない。state・ログ・長期キーの三経路をそれぞれ別の手段で閉じる。

02長期キーを捨てる — OIDCによるキーレス認証

三経路のうち、被害が最も大きい長期キーは、CI/CDとクラウドの間のOIDC連携で根本的に無くせます。GitHub Actionsの公式ドキュメントは、ワークフローが実行のたびにGitHubのOIDCプロバイダから発行される短命トークン(発行者は https://token.actions.githubusercontent.com)をクラウド側に提示し、短命のアクセストークンを直接受け取る仕組みを説明しています。GitHubに長期のクラウド認証情報を保存しない、いわゆる「No cloud secrets」の構成です。

現場のコツ:信頼ポリシーの sub 条件は必ずリポジトリとブランチ(環境)まで絞り込んでください。repo:org/* のようなワイルドカードは、フォークや別ブランチからの意図しないロール引き受けを許す典型的な設定ミスです。ここを緩くするとキーレス化のメリットが一気に崩れます。

OIDCへの移行は、IaCのパイプライン設計とも密接に関わります。パイプライン全体の組み方はIaCのCI/CDパイプライン設計、複数アカウントにまたがる権限の統制はマルチアカウント統制もあわせてご覧ください。

03state / plan の暗号化とephemeral値

OIDCで長期キーを消しても、stateに残る機微値は別問題です。ここは二段構えで対処します。

第一に、そもそもstateに残さない。HashiCorpは Terraform 1.10 以降で ephemeral 値(ephemeral変数・ephemeralブロック・write-only引数)を導入し、実行時には利用できるがstateやplanには一切書き込まない値として扱えるようにしています。DBパスワードのような「渡すだけで保存不要」な値は、まずこれで state から追い出せないか検討します。OpenTofuでも同等の機能が提供されているため、フォーク側を採用している現場は公式で対応状況を要確認です。

第二に、残る分は暗号化と隔離。stateはリモートバックエンドに置き、保存時暗号化を効かせます。S3バックエンドなら encrypt オプション、GCSなら顧客管理鍵(CMEK)といった具合に、暗号化方式はバックエンド依存なので公式で要確認です。stateへのアクセスは最小権限に絞り、監査ログを取る。stateファイルの詳しい扱いはIaCのstate管理で掘り下げています。

現場のコツ:sensitive = true はCLI出力とログでの表示を伏せるだけで、state内では平文のままです。「sensitiveにしたから安全」という誤解は監査で必ず突かれます。表示制御(sensitive)と保存制御(ephemeral・暗号化)は別レイヤだと切り分けてください。

04秘密の置き場所 — Vault / SOPS / クラウドの使い分け

ephemeralやOIDCで減らしきれない「保管が必要な秘密」については、保管先を選びます。代表的な三つを、性格の違いで整理します。

保管先の使い分け Vault 動的シークレット 要求時に生成 TTLで失効 使い捨て認証情報 向く場面 CI実行ごとの 短命DB/クラウド鍵 クラウド Secrets マネージド保管 自動ローテーション IAM連携 監査ログ標準装備 向く場面 クラウド内で完結 するDB/API資格情報 SOPS ファイル暗号化 値だけ暗号化 Gitでdiff可 KMS / age で鍵管理 向く場面 GitOpsで秘密を リポジトリ管理 短命性が要るならVault、クラウド完結ならSecrets Manager、Git管理ならSOPS
図:三択ではなく併用が現実解。短命性・クラウド完結・Git管理という軸で置き場所を分ける。

実務では三択ではなく併用になります。例えば「CIのDB接続はVaultの動的シークレット、アプリ実行時のAPIキーはクラウドのSecrets Manager、GitOpsで配布するKubernetes SecretはSOPS」という組み合わせは自然です。SOPSとGitOpsの組み合わせはGitOps(Argo CD / Flux)の文脈でも要になります。

05最小権限 — 権限を絞ってこそキーレスが効く

OIDCで長期キーを消し、Vaultで短命化しても、引き受けるロールの権限が広ければ意味が薄れます。最小権限の基本は変わりません。

「最小権限は理想論で運用が回らない」という声は現場で必ず出ます。その落としどころは最小権限の現実解で整理していますので、あわせてご覧ください。

06ローテーションと監査 — 事故を前提に短く回す

どれだけ絞っても漏洩ゼロは保証できません。だからこそローテーションで「漏れても短時間で失効する」状態を作り、監査で「誰がいつ何を読んだか」を追える状態を作ります。

現場のコツ:ローテーションを入れる前に「アプリがローテーション後の新しい値を確実に取りにいくか」を検証してください。古い値をキャッシュし続けるアプリにローテーションを被せると、鍵は変わったのに障害だけ起きます。ローテーションは失効の仕組みとアプリの再取得がセットで初めて機能します。

まとめ

CI/CDとIaCのシークレット管理は、単一のツール導入では完結しません。漏洩経路をstate・ログ・長期キーの三つに分けて捉え、それぞれに手当てを当てるのが実務の勘所です。

いずれもバージョンや料金に依存する挙動は公式で要確認です。自社のパイプラインとIaC構成を前提に、どの経路から手を付けるべきかはお問い合わせください。

参考(一次情報)

シークレット管理の設計レビューやOIDC移行の伴走が必要でしたら、お問い合わせください。現状のパイプラインとIaC構成を前提に、実装可能な手順に落とし込みます。

相談する
← ブログ一覧へ戻る