サプライチェーン攻撃などのリスクを軽減するため、CI/CDパイプラインでのセキュリティスキャンは欠かせないステップになりつつあります。しかしCI/CDパイプラインでの防御策における課題が1つあります。それは迂回の防止です。
一度パイプラインでセキュリティスキャンジョブを追加しても、現場レベルでジョブがコメントアウトされたり、警告を無視するなどの迂回や形骸化が発生してしまうことがあります。クリティカルな脆弱性が検出されても「リリースを止められない」という判断でスキャンがスキップされ、対応チケットだけが蓄積されていく状況です。
この記事では、CircleCIのConfig Policyを使って、セキュリティスキャンの実行を組織として強制する方法を紹介します。Soft Fail(警告のみ)から始めてHard Fail(強制停止)へ移行する3ステップのアプローチで、現場の混乱を最小限に抑えながら段階的にガバナンスを強化できます。
Config Policyは Scale Plan および CircleCI Server v4.2以降でのみ利用可能です。プランの確認は CircleCI ダッシュボードの Org > Plan から行えます。
なぜセキュリティスキャンがスキップされるのか
セキュリティスキャンをCI/CDに導入している組織でも、運用の形骸化が見られます。クリティカルな脆弱性が検出されても「リリースを止められない」という判断でスキャンがスキップされ、対応チケットは優先順位が上がらないまま蓄積されます。
この問題はスキャン後の対応フローが工数を圧迫することにあります。スキャンをスキップする作業は数秒で完了しますが、脆弱性を調査して修正する作業には数時間から数日を要します。一方で、セキュリティリスクと共存するコストは組織全体に分散されるため、個人レベルでは見えにくくなっています。
この非対称性を解消するには、個人の判断に委ねるのではなく、組織のルールとして強制することが必要です。Config Policyはその仕組みを提供します。
Config Policyとは
Config Policyは、CircleCIプロジェクトの設定(config.yml)に対してルールを定義し、組織全体で強制できる機能です。ポリシーはOPA(Open Policy Agent)であるRegoにて記述され、パイプラインがトリガーされる前に設定内容を評価します。
ポリシーは組織単位で設定できるため、運用チームやプラットフォームチームが中央集権的に統制をかけることができます。また、プロジェクトやブランチごとにポリシー違反時の動作を変更することもRegoで行えます。新規のパイプラインを構築する際やより厳格な運用を行いたい場合は、パイプライン実行を停止するhard_failを設定します。一方で猶予期間を設けたい場合などでは、soft_failを設定することで警告を出すだけにとどめることも可能です。
また「いつ、どのプロジェクトでポリシー違反が発生したか」をCLIの circleci policy logs コマンドで追跡することもできます。これによってコンプライアンス要件への対応にも活用できます。
Config Policyの管理にはCircleCI CLIが必要です。インストール方法はCircleCI CLI のインストールを参照してください。
3ステップで段階的に導入する
いきなり全プロジェクトでHard Failを適用すると、既存のビルドが停止して開発の混乱を招きます。次のような3ステップで段階的に導入し、組織への影響を最小限に抑えながらガバナンスを確立できます。
ステップ1: Soft Failで全社展開
最初のステップは、ポリシーをSoft Fail(警告のみ)で全組織に展開します。この段階ではポリシー違反が検出されてもビルドは停止せず、違反がPolicy Decision Logに記録されます。
Soft Failで展開する目的は2つです。まず、既存のビルドプロセスを中断せずに現状を把握できます。どのプロジェクトでポリシー違反が発生しているかをデータとして収集できます。次に、開発者に準備期間を与えられます。将来的にHard Failへ切り替わることを予告として機能させることができます。
最初にConfig Policy評価を組織で有効化します。
circleci policy settings --enabled=true --owner-id <your-org-id>
次に、セキュリティスキャンの実行を検証するポリシーファイルを作成します。以下はTrivyスキャンを含むジョブが存在するかをチェックするポリシーの例です。
このポリシーは、コンパイル済みのconfig(input._compiled_)を対象に評価します。Orbや再利用可能なジョブを使ってスキャンを定義している場合でも、展開後の設定を正確に評価するためです。
package org
import future.keywords
policy_name["require_security_scan"]
# Soft Failとして有効化(ビルドは停止しない)
enable_rule["require_security_scan"]
soft_fail["require_security_scan"]
# コンパイル済みジョブにセキュリティスキャンが含まれているかをチェック
require_security_scan = reason {
not has_security_scan
reason := "セキュリティスキャンジョブが設定に含まれていません"
}
has_security_scan {
some _, job in input._compiled_.jobs
contains(lower(job.docker[_].image), "trivy")
}
has_security_scan {
some _, step in input._compiled_.jobs[_].steps
contains(lower(step.run.command), "trivy")
}
ポリシーファイルを作成したら、CLIでCircleCIに適用します。
circleci policy push ./policy-bundle --owner-id <your-org-id>
組織IDはCircleCIダッシュボードの Org > Overview から取得できます。
これでポリシーがCircleCIの組織単位で適用されました。もしポリシーに違反しているパイプラインが実行された場合、ダッシュボードに警告が表示されるようになります。
ステップ2: 可視化とモニタリング
Soft Failで展開した後、ポリシー違反の状況を把握します。
最も手早く調査できるのはPolicy Decision Logs です。Config Policyの違反詳細(どのルールに違反したか、SOFT_FAILかHARD_FAILか)は、CLIの circleci policy logs コマンドで取得します。
# ポリシー違反のログを取得
circleci policy logs --owner-id <your-org-id>
# 日付やブランチでフィルタ
circleci policy logs --owner-id <your-org-id> --after 2026/04/01 --branch main
--status フラグで SOFT_FAIL や HARD_FAIL のみをフィルタすることも可能です。
ステップ3: クリティカルなプロジェクトでHard Failへ移行
Soft Failで1〜2週間データを収集した後、クリティカルなプロジェクトから段階的にHard Failへ切り替えます。
Hard Failを優先的に適用すべきプロジェクトの例は次の通りです。
- 決済処理を扱うプロジェクト
- 認証・認可機能を提供するプロジェクト
- 個人情報を扱うプロジェクト
- 外部公開APIを提供するプロジェクト
Config Policyでは、data.meta.vcs.branch を使ってブランチごとにポリシーの強度を変えることができます。以下のポリシーは、mainブランチではHard Fail、feature/プレフィックスのブランチではSoft Failを適用する例です。
package org
import future.keywords
policy_name["require_security_scan_by_branch"]
# mainブランチではHard Fail(ビルドを停止)
enable_hard["require_security_scan"] {
data.meta.vcs.branch == "main"
}
# feature/ブランチではSoft Fail(警告のみ)
enable_rule["require_security_scan"] {
startswith(data.meta.vcs.branch, "feature/")
}
soft_fail["require_security_scan"] {
startswith(data.meta.vcs.branch, "feature/")
}
require_security_scan = reason {
not has_security_scan
reason := "セキュリティスキャンジョブが設定に含まれていません"
}
has_security_scan {
some _, job in input._compiled_.jobs
contains(lower(job.docker[_].image), "trivy")
}
has_security_scan {
some _, step in input._compiled_.jobs[_].steps
contains(lower(step.run.command), "trivy")
}
enable_hard は、enable_rule と hard_fail を同時に指定するショートハンドです。mainブランチへのプッシュ時にポリシー違反があれば、パイプラインの実行は停止します。
なお、このポリシーでは main でも feature/ でもないブランチ(例: develop, release/)ではルールが有効化されません。すべてのブランチでポリシーを適用したい場合は、条件なしの enable_rule を追加するか、ブランチ条件を調整してください。
更新したポリシーをCircleCIに適用するには、再度 circleci policy push を実行します。変更前にローカルで動作確認する場合は circleci policy decide コマンドを使用できます。
# ローカルで動作確認
circleci policy decide --input .circleci/config.yml ./policy-bundle \
--metafile meta.json # data.metaの値をモックするJSONファイル
# CircleCIに適用
circleci policy push ./policy-bundle --owner-id <your-org-id>
data.meta.vcs.branch などのメタデータを使用するポリシーのローカルテストには、--metafile フラグでメタデータをモックするJSONファイルを指定する必要があります。詳細はConfig policies for use with the CLIを参照してください。
まとめ
CircleCIのConfig Policyを使うことで、セキュリティスキャンの実行を組織のルールとして強制できます。Soft Fail → 可視化 → Hard Failの3ステップで段階的に導入することで、開発の流れを中断せずにセキュリティガバナンスを確立できます。
今回はセキュリティスキャンを例に説明しましたが、同じアプローチは config.yml で表現できる他のガバナンス要件にも適用できます。ランタイムバージョンの制限、リソースクラスの統制など、パイプラインの設定内容に対して組織ポリシーを強制したい場面で活用できます。
より詳しく学ぶ
- Config policies overview: https://circleci.com/docs/guides/config-policies/config-policy-management-overview/
- Config policy reference: https://circleci.com/docs/guides/config-policies/config-policy-reference/
- Use the CLI for config and policy development: https://circleci.com/docs/use-the-cli-for-config-and-policy-development/
- Using Insights: https://circleci.com/docs/insights/

