TT Lab
はじめる
学ぶ 学習パス コース

CNPE — クラウドネイティブプラットフォームエンジニア

秘密の本文を残さず、操作の記録を残す監査ポリシー

TT Labで続きを見る

目標

個人VMの本物のk3sで、合成した秘密の露出 → Metadataによる復旧 → Noneによる証拠の欠落 → 最終的な復旧を実施します。

なぜ重要なのか

ポリシーがあるというだけでは、安全な監査証跡は生まれません。実際のリクエストが残っているかと、 本文が複製されていないかを、合わせて検査する必要があります。CNPEの監査証跡の能力の一部を練習するもので、 公式の試験問題を複製したり、セキュリティのドメイン全体を扱ったりするという意味ではありません。 Kubernetesのリソース・RBAC・JSON・jqと、systemdの基礎が必要です。

用意されているもの

Ubuntu 24.04の個人VM、k3s v1.36.4+k3s1、cnpe-audit Namespaceと合成canary、 合成Secretの詳細な監査記録が用意されます。最初の準備には数分かかることがあります。 すべての相対パスは/root/cnpe-audit以下です。drop-inの完全なパスは /etc/rancher/k3s/config.yaml.d/95-cnpe-audit.yamlです。 これはAPIサーバーのポリシーファイルであり、kubectl applyするリソースではありません。

75分のラボなので、デフォルトの60分セッションでは「+時間」で延長してください。セッション終了時に、ファイルとVMは 消えます。必要なレポートは、値が露出していないかを確認したうえで、別に保管してください。 systemctl restartは、このVMの中でだけ使います。ホストクラスターや他の人のセッションには 触れず、実際の認証情報・個人情報を入れません。

ステップ

  1. 自分のVMのAPIと監査環境を確認する: kubectl --kubeconfig=/etc/rancher/k3s/k3s.yaml get nodesで、ホストが1台だけのクラスターを確認してください。Namespace cnpe-auditは、このラボ専用です。環境では、合成Secretの詳細な収集が有効になっています。ほかのクラスターのkubeconfigや、実際の秘密を持ち込まないでください。
  2. 値ではなく露出しているかどうかを報告する: 用意されているunsafe-case.jsonとunsafe-events.jsonlを読んで、unsafe-report.jsonを作成してください。requestsは、該当のSecretのResponseCompleteイベントの数、body_presentは、requestObjectまたはresponseObjectキーの存在の有無、marker_exposedは、caseのmarkerの原文またはbase64の露出の有無、control_presentは、case.control_uriの200の対照記録の存在の有無です。safe_to_acceptは、4つのSecretリクエストが存在し、本文・マーカーがなく、対照記録がある場合だけtrueです。元の秘密の値をレポートにコピーしないでください。
  3. 本文を除きつつ追跡は残すポリシーを書く: safe-policy.jsonに、apiVersion audit.k8s.io/v1、kind Policy、omitStages [RequestReceived]を書いてください。rulesは2つです。最初のルールはlevel Metadata、namespaces [cnpe-audit]、resources [{group: (空文字列), resources: [secrets]}]で、2つ目は条件のないlevel Metadataです。このラボは、この制限されたポリシーの契約を使うもので、汎用のポリシーエディターではありません。
  4. ポリシーファイルと実行中のAPIをつなぐ: 95-cnpe-audit.yamlを、JSON形式で書いてください。唯一のキーkube-apiserver-arg+のリストに、audit-policy-file=/root/cnpe-audit/safe-policy.json、audit-log-path=/root/cnpe-audit/runtime.jsonl、audit-log-mode=blocking、audit-log-maxsize=5、audit-log-maxbackup=1、audit-log-maxage=1を入れてください。個人VM内のk3sを再起動し、/readyzがokであることを確認します。採点は、新しい合成canaryの参照の、実際の監査レベルも検査します。
  5. 作成・参照・拒否・削除を実際に送る: 下の参考の収集コマンドの形式で、ステップの引数としてsafeを選んで実行してください。コレクターは、今回のUUIDの合成Secretの作成・管理者による参照・権限のない代理ユーザーによる参照・削除と、別の/version対照リクエストを実行します。safe-case.json、safe-events.jsonl、safe-origin.jsonlが作られている必要があります。4つのリクエストは201・200・403・200で、Metadataに本文はない必要があります。
  6. 空の記録を通過させない要約を作る: safe-case.jsonとsafe-events.jsonlから、ステップ2と同じ5つのフィールドを計算して、safe-report.jsonに保存してください。収集時点の元の抜粋と分析ファイルを、合わせて保存します。このレポートは、このコレクターの限定されたシナリオの要約であり、任意の監査ログを承認する汎用のセキュリティゲートではありません。
  7. Noneで隠した出来事を失敗として分類する: none-policy.jsonを、ステップ3のポリシーと同じにして、最初のSecretルールだけをNoneに変えてください。drop-inのポリシーパスだけをこのファイルに変え、個人k3sの再起動・準備確認のあと、collect noneを実行します。none-report.jsonに、同じ5つのフィールドを計算してください。Secretの記録は0、対照記録は存在し、safe_to_acceptはfalseである必要があります。監査機能全体が無効になっている場合は、この反例とは異なります。
  8. 安全なポリシーに復旧して新しい証拠を残す: drop-inをsafe-policy.jsonに戻して、個人k3sを再起動してください。collect finalで新しいリクエストを作り、final-report.jsonを計算します。以前のsafeレポートをコピーしないでください。採点は、新しいリクエストと現在のAPIのcanary参照を、合わせて確認します。そのあとは、ほかのラボを始めず、この個人セッションを終了すればよいです。

参考

収集コマンドの形式はpython3 /opt/fixtures/cnpe-audit-lab.py collect <단계>です(プレースホルダーはステップ名です)。 <단계>の部分を、課題で指定したsafe、none、finalのいずれかに置き換えてください。 collectは、明示的に合成Secretを作成・削除する収集コマンドです。checkは、ファイルと 現在のAPIを読み、canaryを変更しません。収集に失敗したときに、以前の証拠が新しいリクエストと 誤認されないよう、リクエスト識別子を先に更新します。originの抜粋を別に保存するため、元の ログがローテーションしても、以前のステップを再採点できます。受講生のrootによる改ざん防止や、 中央収集・不変ストレージ・停電・すべての機密リソースの保護の証拠ではありません。 最後の安全なポリシーに復旧したあとで、全体の採点をしてください。ステップ7のNoneが有効な間は、 現在の安全な状態を求めるステップ4は、失敗するのが正常です。

公式ドキュメント

自分のVMのAPIと監査環境を確認する

kubectl --kubeconfig=/etc/rancher/k3s/k3s.yaml get nodesで、ホストが1台だけのクラスターを確認してください。Namespace cnpe-auditは、このラボ専用です。環境では、合成Secretの詳細な収集が有効になっています。ほかのクラスターのkubeconfigや、実際の秘密を持ち込まないでください。

アプリケーションPodのログと、APIサーバーの監査記録は別です。

値ではなく露出しているかどうかを報告する

用意されているunsafe-case.jsonとunsafe-events.jsonlを読んで、unsafe-report.jsonを作成してください。requestsは、該当のSecretのResponseCompleteイベントの数、body_presentは、requestObjectまたはresponseObjectキーの存在の有無、marker_exposedは、caseのmarkerの原文またはbase64の露出の有無、control_presentは、case.control_uriの200の対照記録の存在の有無です。safe_to_acceptは、4つのSecretリクエストが存在し、本文・マーカーがなく、対照記録がある場合だけtrueです。元の秘密の値をレポートにコピーしないでください。

unsafeの詳細なイベントが存在するということと、安全であるということは違います。Falseを文字列で保存しないでください。

本文を除きつつ追跡は残すポリシーを書く

safe-policy.jsonに、apiVersion audit.k8s.io/v1、kind Policy、omitStages [RequestReceived]を書いてください。rulesは2つです。最初のルールはlevel Metadata、namespaces [cnpe-audit]、resources [{group: (空文字列), resources: [secrets]}]で、2つ目は条件のないlevel Metadataです。このラボは、この制限されたポリシーの契約を使うもので、汎用のポリシーエディターではありません。

最初に一致するルールが、収集レベルを決めます。すべての記録をNoneに変えると、誰が読んだかも失われます。

ポリシーファイルと実行中のAPIをつなぐ

95-cnpe-audit.yamlを、JSON形式で書いてください。唯一のキーkube-apiserver-arg+のリストに、audit-policy-file=/root/cnpe-audit/safe-policy.json、audit-log-path=/root/cnpe-audit/runtime.jsonl、audit-log-mode=blocking、audit-log-maxsize=5、audit-log-maxbackup=1、audit-log-maxage=1を入れてください。個人VM内のk3sを再起動し、/readyzがokであることを確認します。採点は、新しい合成canaryの参照の、実際の監査レベルも検査します。

+がないと、別のdrop-inの既存のリストを上書きすることがあります。ポリシーのYAMLを、Kubernetesのリソースのようにapplyしないでください。

作成・参照・拒否・削除を実際に送る

下の参考の収集コマンドの形式で、ステップの引数としてsafeを選んで実行してください。コレクターは、今回のUUIDの合成Secretの作成・管理者による参照・権限のない代理ユーザーによる参照・削除と、別の/version対照リクエストを実行します。safe-case.json、safe-events.jsonl、safe-origin.jsonlが作られている必要があります。4つのリクエストは201・200・403・200で、Metadataに本文はない必要があります。

ファイルだけを安全なポリシーに変えて再起動しなければ、実際の収集レベルはそのままなので、失敗します。

空の記録を通過させない要約を作る

safe-case.jsonとsafe-events.jsonlから、ステップ2と同じ5つのフィールドを計算して、safe-report.jsonに保存してください。収集時点の元の抜粋と分析ファイルを、合わせて保存します。このレポートは、このコレクターの限定されたシナリオの要約であり、任意の監査ログを承認する汎用のセキュリティゲートではありません。

4つのリクエストの存在が先です。本文がないという条件1つだけを検査しないでください。

Noneで隠した出来事を失敗として分類する

none-policy.jsonを、ステップ3のポリシーと同じにして、最初のSecretルールだけをNoneに変えてください。drop-inのポリシーパスだけをこのファイルに変え、個人k3sの再起動・準備確認のあと、collect noneを実行します。none-report.jsonに、同じ5つのフィールドを計算してください。Secretの記録は0、対照記録は存在し、safe_to_acceptはfalseである必要があります。監査機能全体が無効になっている場合は、この反例とは異なります。

Noneポリシーが正常に適用されたことは、セキュリティ監査の要件の成功ではありません。

安全なポリシーに復旧して新しい証拠を残す

drop-inをsafe-policy.jsonに戻して、個人k3sを再起動してください。collect finalで新しいリクエストを作り、final-report.jsonを計算します。以前のsafeレポートをコピーしないでください。採点は、新しいリクエストと現在のAPIのcanary参照を、合わせて確認します。そのあとは、ほかのラボを始めず、この個人セッションを終了すればよいです。

過去に安全だったログだけでは、今実行中の設定を証明できません。