パイプライン外でインフラが変更されたとき:ポリシー準拠のためのドリフト検出

ポリシーを書き、CI/CDパイプラインに自動チェックを組み込みました。すべてのデプロイは本番環境に到達する前に検証を通過します。チームはインフラがルールに従っていると確信しています。

そんなある日、チームの誰かが午後10時に本番環境の障害をデバッグする必要に迫られます。クラウドコンソールにログインし、自宅のIPアドレスに対してセキュリティグループのポートを開放し、問題を修正して就寝します。変更を元に戻すのを忘れました。翌朝、そのセキュリティグループはまだ開いたままです。パイプラインはその変更を認識できず、ポリシーも違反を捕捉できません。

このシナリオは珍しくありません。インシデント対応時にエンジニアがショートカットを取った場合、パイプラインが遅すぎるために別のチームが手動でリソースを作成した場合、あるいはオートスケーリングがポリシーに違反するデフォルト設定で新しいインスタンスを起動した場合などに発生します。これらの変更はすべてCI/CDパイプラインの外で行われるため、planステップとapplyステップに慎重に組み込んだポリシーチェックはそれらを検知できません。

ドリフト検出の実際の意味

ドリフト検出とは、実際のインフラ状態をポリシーが定めるべき状態と比較するプロセスです。これは定期的に実行されます—1時間ごと、1日ごと、あるいはチームにとって適切な間隔で—そしてルールから逸脱したリソースを報告します。

目的は変更を防ぐことではありません。パイプラインポリシーは、非準拠のデプロイが発生する前にブロックすることで、すでにその役割を果たしています。ドリフト検出は、パイプライン外で既に発生した変更を捕捉します。これは、自動化だけでは制御できないインフラのためのセーフティネットです。

ドリフト検出の実践的な仕組み

仕組みは単純です。ツールまたはスクリプトがクラウドプロバイダーのAPIを呼び出して既存の全リソースをリストアップし、定義されたポリシーに対して1つずつチェックします。

以下は、Terraform planを実行し、ドリフトを検出したらアラートを送信するシンプルなbashスクリプトです:

#!/bin/bash
# 定期ドリフト検出スクリプト(cronで1時間ごとに実行)
cd /path/to/terraform/project
terraform init -input=false > /dev/null 2>&1
terraform plan -detailed-exitcode -input=false -no-color > plan_output.txt 2>&1
EXIT_CODE=$?
if [ $EXIT_CODE -eq 2 ]; then
  echo "Drift detected at $(date)" >> drift_alerts.log
  # 通知送信(例:Slack Webhook)
  curl -s -X POST -H 'Content-type: application/json' \
    --data "{\"text\":\"本番インフラでドリフトを検出しました。詳細はplan_output.txtを確認してください。\"}" \
    https://hooks.slack.com/services/YOUR/WEBHOOK/URL
elif [ $EXIT_CODE -eq 1 ]; then
  echo "Terraform plan failed at $(date)" >> drift_alerts.log
fi

例えば、ポリシーで「どのセキュリティグループもポート22を0.0.0.0/0に開放してはならない」と定めているとします。ドリフト検出はすべてのセキュリティグループをスキャンし、違反をフラグ付けします。あるいは、すべてのリソースにownerタグを必須とするポリシーの場合、ドリフト検出は全リソースを棚卸しし、タグがないものをマークします。

結果は、チームが実際に確認する場所に送信する必要があります。ダッシュボード、Slackやメール通知、トラッキングシステムへの自動チケット作成などが有効です。誰も読まないレポートを生成するだけでは意味がありません。フォローアップの責任者がいなければ、ドリフト検出はノイズになってしまいます。

ドリフト検出に役立つツール

いくつかのツールはすでにドリフト検出機能を備えています。Terraformのterraform planは、ステートファイルと実際のインフラの差分を表示できます。ただし、これはTerraformで管理されているリソースにのみ有効です。Terraform外で作成されたリソースには別のアプローチが必要です。

クラウドネイティブツールは広範囲をスキャンできます。AWS Config、Azure Policy、Google Cloud Asset Inventoryは、ネイティブAPIを使用してリソースを検査します。これらはクラウドプロバイダーのリソースモデルを深く理解しており、アカウント内のすべてのリソースのコンプライアンスをチェックできます。

オープンソースの選択肢もあります。Open Policy Agent(OPA)は、定期的にインフラに対してトリガーするポリシーエンジンとして実行できます。Regoでポリシーを記述し、OPAが取り込んだリソースデータに対して評価します。

難しい部分:ドリフトを発見した後の対処

ドリフトの発見は作業の半分に過ぎません。より難しいのは、その後の対応です。

優れたドリフトレポートには、問題を修正するための十分な情報が含まれています。どのリソースがどのポリシーに違反したか、理想的にはその修正方法も示します。さらに進んで自動修復を実装するチームもあります—ドリフトが検出されると、システムが自動的にリソースを準拠状態に戻します。

自動修復は一見素晴らしいように思えますが、問題を引き起こす可能性もあります。エンジニアが本番インシデントのデバッグ中に一時的にポートを開放していた場合、自動修復がそのポートを閉じてしまうと、作業中の調査を妨害することになります。ツールはポリシー違反を修正する一方で、進行中の調査を妨害するのです。偶発的なドリフトと、短期的な理由で意図的に行われたドリフトを区別する必要があります。

実践的なアプローチは、アラートと手動修復から始めることです。ドリフトが発生したらチームに通知し、詳細を伝え、すぐに修正するか、一時的な例外として文書化するかを判断させます。環境内のドリフトのパターンを理解したら、常に誤りであり正当化されないケースについて自動化を検討できます。

3層の保護

ドリフト検出は、ポリシー適用サイクルを完成させます。これは、互いの死角をカバーする3つの層として考えてください:

以下の図は、これら3つの層がどのように相互作用するかを示しています:

flowchart TD A[コード変更] --> B[Plan時のポリシーチェック] B -->|合格| C[Apply時のポリシーチェック] B -->|不合格| D[デプロイをブロック] C -->|合格| E[本番環境にデプロイ] C -->|不合格| D E --> F[稼働中のインフラ] F --> G[手動変更 / インシデント修正] G --> H[ドリフト検出スキャン] H -->|準拠| I[対応不要] H -->|ドリフト発見| J[チームにアラート] J --> K[手動修復] K --> F J --> L[自動修復] L --> F
  1. Plan時のポリシーチェック - 本番環境に到達する前に違反を防止
  2. Apply時のポリシーチェック - 計画時にすり抜けたものを捕捉
  3. 定期的なドリフト検出 - パイプライン外で発生した変更を発見

どの層も単独では不十分です。パイプラインチェックは手動のコンソール変更を捕捉できません。ドリフト検出は違反の発生自体を防ぐことはできません。これらが連携することで、自動化されたワークフローの外で変更が発生しても、インフラをポリシーに準拠させ続けるためのカバレッジが得られます。

実践的なチェックリスト

  • インフラの変更頻度に基づいて、ドリフトスキャンの間隔を設定する
  • 管理対象リソースと非管理対象リソースの両方をカバーするツールを選択する
  • ドリフトレポートをチームが実際に監視するチャネルに送信する
  • ドリフト発見時のフォローアップの責任者を割り当てる
  • 自動化を検討する前に、まず手動修復から始める
  • 一時的な例外を文書化し、チームがどのドリフトが意図的かを把握できるようにする

チームにとっての意味

ポリシーの強度は、インフラが変更されるすべての場所でそれを適用できるかどうかにかかっています。パイプラインチェックは制御可能な変更を処理します。ドリフト検出は制御できない変更を処理します。両方がなければ、ポリシーは「あるべき姿」を記述した文書に過ぎず、それを維持する仕組みにはなりません。ドリフト検出を運用に組み込めば、予期せぬことが発生してもインフラは準拠し続けます。