TL

ポリシーアズコードと継続的コンプライアンス

「禁止構成は人のレビュー頼み」から卒業。インフラ・デプロイの制約をコードで宣言し、CI と admission で機械的に強制、ドリフト検知と監査証跡まで自動化する原理を解説。

応用Policy as CodeOPARegoコンプライアンスガードレールセキュリティ最終更新: 2026-06-21
TL;DR要点だけ先に
  • 1.ポリシーアズコードは『あるべき制約』を宣言的なコード(OPA/Rego・Kyverno・CEL 等)で書き、評価エンジンが入力(JSON 化した構成・リクエスト)に対し allow/deny を純粋関数として返す。判定はバージョン管理・テスト・再現が可能になる。
  • 2.強制点(PEP)は CI のゲート、admission webhook、IaC の plan 検査の三層に置ける。早い層ほど安価に弾けるが、admission など実行時の層がないと経路を迂回した変更を止められない。
  • 3.継続的コンプライアンスは『定期スキャン=検知』『強制=防止』『監査ログ=証拠』を分離する。ドリフトは実体を再評価して見つけ、判定の入出力を不変ログに残すことで証跡が自動生成される。

なぜ制約を「コード」にするのか

「本番の Pod は root で動かさない」「S3 バケットは暗号化必須」「タグのないリソースは作らせない」――こうした組織のルールは、長らく Wiki の文章とレビュアーの目視で守られてきました。問題は、自然言語のルールが実行可能でも検証可能でもないことです。レビュアーが見落とせば素通りし、ルールが更新されても古い構成は放置され、「いま全環境がルールに準拠しているか」を機械的に答えられません。

ポリシーアズコード(Policy as Code, PaC)は、この制約を宣言的なコードとして表現し、評価エンジンに機械判定させる考え方です。本質は、ポリシーを次の形の関数に還元することにあります。

decision = policy(input)
# input  : 評価対象を JSON 化したもの(マニフェスト、Terraform plan、API リクエスト…)
# policy : 制約を記述したルール集合(Rego, CEL, YAML…)
# decision: allow / deny(+ 違反理由)

この関数は理想的には**純粋(副作用なし・決定的)**です。同じ入力には常に同じ判定を返すため、テスト・バージョン管理・コードレビュー・再現が普通のコードと同じようにできます。これが目視レビューに対する決定的な優位です。

評価エンジンの内部:Rego を例に

代表格が CNCF の OPA(Open Policy Agent) と、その言語 Rego です。Rego は宣言的なクエリ言語で、命令的な手順ではなく「違反が成立する条件」を書きます。

package kubernetes.security

# Pod が root 実行なら deny に違反メッセージが入る
deny[msg] {
    input.kind == "Pod"
    some c
    container := input.spec.containers[c]
    not container.securityContext.runAsNonRoot
    msg := sprintf("container %v は runAsNonRoot=true が必須", [container.name])
}

ここで効いている原理が2つあります。第一に deny は集合(set) で、ルールを満たす全違反が要素として集まります。空集合なら準拠、要素があれば違反です。第二に、本体の各行は論理 AND で結ばれ、input.spec.containers[c]c のような未束縛変数は全要素に対する反復として展開されます(内部的には全解の探索)。つまり「どれかのコンテナが runAsNonRoot 未設定なら…」を明示的なループなしに表現できます。

評価器は入力を**ドキュメント(木構造の JSON)**として読み、ルールを評価して仮想ドキュメントを生成します。Kubernetes の CEL も思想は近く、式が真偽を返す点は共通ですが、CEL は API サーバー内蔵で軽量・反復が限定的、Rego/Kyverno は外部エンジンで表現力が高い、というトレードオフがあります(/devops/admission-control-policy/ で強制点ごとに整理)。

強制点(PEP)をどこに置くか

ポリシーを書いても、それを**評価して実際に止める場所(Policy Enforcement Point, PEP)**がなければ絵に描いた餅です。PEP は実務上3つの層に置けます。

強制点止めるタイミング止められるもの/弱点
CI ゲートマージ前・apply 前PR の構成を弾く。安価で速いが、CI を迂回した変更は見逃す
IaC plan 検査terraform plan の差分適用前の差分を評価。実体ではなく宣言が対象
admission webhookAPI サーバーが etcd 保存する直前経路を問わず実行時に強制。最後の砦だが運用ミスで全 API が詰まる

重要なのは、これらが代替ではなく多層防御だという点です。CI ゲートは「速いフィードバックで開発者体験を守る」層、admission は「どの経路から来た変更も取りこぼさない」最終層です。CI だけだと、kubectl apply を直接叩く、別ツールが書き込む、といった迂回路を塞げません。逆に admission だけだと、違反に気づくのがデプロイ直前になり手戻りが大きくなります。

admission を最終防壁にする時の落とし穴

admission webhook は API サーバーが同期 HTTP で呼ぶため、failurePolicyFail にすると webhook 障害時に全 API 操作が止まります。逆に Ignore にすると障害時にポリシーが素通りします。可用性と強制力はトレードオフで、対象を matchConditions で絞り、タイムアウトを短く保つ設計が前提です。

ガードレールとポリシーの分類

PaC の制約は強制の仕方で大別できます。ハードガードレールは違反を deny で物理的に阻止します(防止的・preventive)。ソフトガードレールは警告のみで通し、後から是正を促します(発見的・detective)。組織を回す現実解は、リスクの高いルールだけハード、残りはソフトから始め、違反率を見て段階的に締める運用です。最初から全部ハードにすると、誤検知(false positive)で開発が止まり、ポリシー自体が無効化されます。

評価の観点でも、mutating(構成を改変して準拠させる。例:欠けたラベルを自動付与)validating(改変せず合否だけ返す) を区別します。admission では mutating が先、validating が後に走り、validating 後はオブジェクトを変えられない――この順序は決定性のために重要で、検証した後で誰かが書き換える余地を残さないためです。

継続的コンプライアンス:検知・防止・証跡の三層

ここまでは「入る前に止める(防止)」話でした。継続的コンプライアンスはこれに検知証跡を足し、3つの独立した役割に分解します。

  1. 防止(強制):PEP が違反を入口で止める。前述の三層。
  2. 検知(ドリフト):すでに存在する実体を定期的に再評価し、後から逸脱したものを見つける。
  3. 証跡(監査):いつ・何が・どのルールで・どう判定されたかを不変ログに残す。

検知が必要なのは、防止が完璧ではないからです。ポリシーが追加される前に作られたリソース、admission を持たない経路、緊急対応の手動変更などで、実体は静かにドリフトします。検知は実体(実クラウド・実クラスタの現状)を取得して JSON 化し、それを同じ policy(input) に通すことで、防止と同一のルールを再利用して逸脱を洗い出します。IaC では plan のリフレッシュがこの実体取得に相当します(/devops/iac-state-drift-locking/)。

GitOps とも自然に噛み合います。望ましい状態を Git に置き、収束ループが実体を Git に引き戻す仕組み(/devops/gitops-reconciliation/)に PaC を載せると、「Git にコミットされる構成」と「収束で適用される構成」の両方をポリシーで検査でき、ドリフトの是正と準拠の維持が一体化します。

証跡は『判定の入出力』を残すと自動で揃う

監査証跡を後付けで作るのは大変ですが、PaC では policy(input)input・ルールのバージョン・decision をそのままイベントとして不変ストレージに書き出せば、コンプライアンス証拠が判定のたびに自動生成されます。ポリシーが Git 管理なら、「この判定はどのコミットのルールで下されたか」までコミットハッシュで遡れます。これはサプライチェーンの来歴(/devops/sbom-slsa-provenance/)と同じ『主張を検証可能な記録にする』発想です。

ポリシー自体をどう正しく保つか

ポリシーはコードなので、コードと同じ品質問題を抱えます。誤って正当な構成を弾けば(false positive)開発が止まり、見逃せば(false negative)防御が穴になります。だからポリシーにはユニットテストが必須です。Rego なら test_ プレフィックスのルールで「この入力なら deny が空」「あの入力なら違反 1 件」を assert し、CI で回します。

混同しやすい3点

(1) ポリシーの定義(言語/ルール)ポリシーの強制点(PEP の場所) は別物――同じ Rego を CI でも admission でも使える。(2) 防止と検知は別の役割――admission は新規を止めるが既存のドリフトは検知側が担う。(3) mutating と validating の順序――改変が先、検証が後で、検証後は不変。この3点の取り違えが設計ミスの大半を生みます。

もう一つの原理的注意は判定の決定性です。ポリシーが現在時刻や外部 API の応答に依存すると、同じ入力でも判定が揺れ、テストも監査も再現できなくなります。外部データ(許可リスト等)が要る場合は、評価時点のスナップショットを input に焼き込みpolicy(input) を純粋関数に保つのが鉄則です。

まとめ

  • 制約をコード化する本質は、目視レビューを decision = policy(input) というテスト・再現可能な純粋関数に置き換えること。
  • 強制点は多層(CI ゲート・IaC plan・admission)。早い層は安く速く、実行時の層は迂回路を塞ぐ最終防壁。代替ではなく併用する。
  • 継続的コンプライアンスは防止・検知・証跡を分離する。同じポリシーを実体に再適用してドリフトを検知し、判定の入出力を不変ログに残せば監査証跡が自動で揃う。
  • ポリシー自体もテストし・決定的に保つ。これがなければ PaC は新たな見落としと不信の源になる。

「ルールは Wiki に書く」から「ルールはテストされ実行されるコード」へ。この転換が、準拠を人の注意力から機械の保証へ引き上げます。

DevOps/インフラ Article

ポリシーアズコードと継続的コンプライアンスを実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

Policy as Code

比較で見る軸

難易度: advanced / カテゴリ: DevOps/インフラ / タグ数: 6

導入後に効く点

強制点(PEP)は CI のゲート、admission webhook、IaC の plan 検査の三層に置ける。早い層ほど安価に弾けるが、admission など実行時の層がないと経路を迂回した変更を止められない。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
DevOps/インフラ
タグ数
6

判断チェックリスト

  • 自社の用途が「Policy as Code / OPA」に近いか確認する。
  • 強みである「ポリシーアズコードは『あるべき制約』を宣言的なコード(OPA/Rego・Kyverno・CEL 等)で書き、評価エンジンが入力(JSON 化した構成・リクエスト)に対し allow/deny を純粋関数として返す。判定はバージョン管理・テスト・再現が可能になる。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

Policy as CodeOPARegoコンプライアンスガードレールPolicy as CodeOPARego