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

KCSA — Kubernetesセキュリティアソシエイト

監査人が来たのに「有効になっている」証拠がない

TT Labで続きを見る

目標

複数のセキュリティ統制項目(匿名アクセスの遮断、Pod Securityの強制、ServiceAccountトークンの自動マウントの解除、リソースクォータ)を クラスターで直接確認し、その結果を、統制IDごとのpass/failレポートと、ギャップの一覧として残します。

なぜ重要なのか

CIS Kubernetes Benchmark、NSA/CISAの強化ガイド、SOC 2・ISO 27001のようなフレームワークは、結局のところ 「この統制は有効になっているか」を問う質問の一覧です。監査で問題になるのは、統制がない場合よりも、 有効になっていると書いたのに、実際には無効になっている場合です。そのためレポートの1行1行は、設定ファイルや 記憶ではなく、いまクラスターが返す答え(auth can-i、オブジェクトのフィールド、ラベル)から出てくるものでなければなりません。

また、強化した場所だけを見て終わりにしてはいけません。誰も手を付けていないdefaultネームスペースのように、統制の外に残された 場所を探してギャップとして記録することが、コンプライアンス点検の半分です。

ステップ

  1. 点検対象のネームスペースkcsa-compを作成します。
  2. system:anonymousがシークレットをlistできるかを尋ね、anon.txtに記録します。
  3. kcsa-compにpod-security.kubernetes.io/enforce=restrictedラベルを付けます。
  4. restrictedの要件をすべて備えたPodhardenedを作成します。
  5. defaultServiceAccountのトークンの自動マウントを無効にします。
  6. ResourceQuotaquotaで、ネームスペースのリソースの上限を設定します。
  7. 4つの統制項目の実際の結果を、compliance.csvレポートとして書きます。
  8. enforceラベルのないネームスペースを探し、gaps.txtにギャップとして記録します。

参考

監査対象のネームスペースを立てる

コンプライアンス点検の対象になるネームスペースkcsa-compを作成してください。

監査は、範囲を決めるところから始まります。ネームスペースは、createを--dry-run=clientで生成してapplyすれば、何度実行しても安全です。

ログインしていない誰かがシークレットを見られるか

認証されていないユーザー(system:anonymous)が、ネームスペースkcsa-compでシークレットをlistできるかをクラスターに直接尋ね、その答えを/root/kcsa-comp/anon.txtにanonymous-list-secrets=<yes|no>の1行で書いてください。

まずkubectl auth can-iに--as=system:anonymousを付けてみてください。答えの代わりに拒否が返ります。can-iはそのユーザー自身としてSelfSubjectAccessReviewを作成しますが、匿名ユーザーはそれすらできないからです。そこで、管理者が代わりに尋ねるSubjectAccessReview(authorization.k8s.io/v1)を作成して、spec.userとgroups(system:unauthenticated)、resourceAttributesを埋め、status.allowedを読みます。falseならnoです。

ネームスペースにrestrictedのゲートキーパーを置く

ネームスペースkcsa-compに、Pod Security Admissionのラベルpod-security.kubernetes.io/enforce=restrictedを付けてください(必要ならwarn・auditのラベルも一緒に)。

Pod Security Standardsは、ネームスペースのラベル1つで有効になります。ラベルのキーはpod-security.kubernetes.io/<모드>で、値はprivileged・baseline・restrictedのどれかです(プレースホルダーはモードです)。kubectl labelに--overwriteを付ければ、もう一度実行しても安全です。

restrictedの基準を通過するPodを受け入れる

ネームスペースkcsa-compにPodhardened(イメージはnginx:1.27-alpine)を作成してください。PodレベルにrunAsNonRoot: trueとseccompProfile.type: RuntimeDefault、コンテナレベルにallowPrivilegeEscalation: false、capabilities.drop: [ALL]、runAsNonRoot: trueを宣言して、restrictedプロファイルの要件をすべて備えます。

restrictedプロファイルは、rootで動かないこと、権限昇格を防ぐこと、すべてのcapabilityを捨てること、seccompプロファイルを指定することを要求します。securityContextにはPodレベル(spec.securityContext)とコンテナレベル(containers[].securityContext)の2か所があり、フィールドごとに置ける場所が違います。要件が1つでも欠けると、ステップ3のラベルにより、作成が拒否されることがあります。

使いもしないトークンがすべてのPodに差し込まれる

ネームスペースkcsa-compのdefaultServiceAccountにautomountServiceAccountToken: falseを設定して、別途要求していないPodにはAPIトークンが自動でマウントされないようにしてください。

ネームスペースが作成されると、コントローラーがdefaultServiceAccountを作成してくれます。まだなければ、少し待ってから確認してください。このフィールドはServiceAccountオブジェクトのトップレベルのフィールドなので、patchで変更できます。

1つのチームがクラスターを食い尽くさないように

ネームスペースkcsa-compにResourceQuotaquotaを作成して、pods=10、requests.cpu=2、requests.memory=2Giを上限として設定してください。

ResourceQuotaは、ネームスペース全体の合計を制限するオブジェクトで、spec.hardの下にリソース名と上限を書きます。kubectl create quotaでは、--hardで複数の項目をカンマでつないで指定できます。

監査担当者に提出する統制項目のレポート

/root/kcsa-comp/compliance.csvを作成してください。1行目はヘッダーcontrol,statusで、その下に4つの統制項目anonymous-access、psa-enforce、sa-token-automount、resource-quotaを1行ずつ書き、statusには実際に確認した結果(passまたはfail)を書きます。4つの項目すべてが、通過の状態である必要があります。

レポートの値は、宣言ではなく証拠です。採点ツールは行ごとに該当する統制をクラスターでもう一度確認し、書かれたstatusと実際の状態が違えば失敗にします。ステップ2・3・5・6で見た結果を、それぞれどの統制項目に対応させるかを考えて、書く前にもう一度確認してください。

デフォルトのネームスペースは誰が守っているのか

PSAのenforceラベルがないネームスペースは、コンプライアンス上のギャップ(gap)です。クラスターのdefaultネームスペースにenforceラベルがあるかを確認し、ギャップのあるネームスペースの名前を/root/kcsa-comp/gaps.txtにpsa-missing-namespace=<네임스페이스>の1行で書いてください(プレースホルダーはネームスペース名です)。(ラベルを付けて直さないでください。このステップは、発見を記録するステップです。)

すべてのネームスペースのラベルを一度に見るには、kubectl get nsにラベルの列を追加するオプションを使ってください。いま強化したネームスペースと、誰も手を付けていないネームスペースを並べて比較すれば、ギャップが見えます。