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

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

PSPはなぜ死に、PSAは何が違うのか

TT Labで続きを見る

一言でいうと

PSPは「ポリシーとユーザーをRBACで結び付ける」構造でしたが、それがあまりにも難しいものでした。 PSAはその結び付けを捨て、ネームスペースのラベル3つに単純化しました。表現力を失う代わりに 運用しやすさを得た、トレードオフです。

なぜ必要なのか

PodSecurityPolicyは次のように動いていました。PSPオブジェクトを作成し、ClusterRoleにそのPSPに対する use権限を入れ、それをPodを作成する主体(人ではなくPodを作成するコントローラーのSA)に バインドします。するとアドミッション時に、「この主体が使えるPSPのうち、このPodを許可するものがあるか」を探します。

問題がいくつもありました。

結果として、多くのクラスターがPSPをそもそも有効にしないか、全員にpermissiveなPSPを与えて終わりにしました。 使われないセキュリティ機能は、セキュリティではありません。PSPはv1.25で完全に削除されました。

どう動くのか

3つのプロファイル

プロファイル 性格 代表的な制限
privileged 無制限 なし。CNI・ストレージドライバーなどのシステムワークロード向け
baseline 既知の権限昇格だけを遮断 hostNetwork/hostPID/hostIPCの禁止、privilegedの禁止、hostPathの禁止、危険なcapabilityの追加の禁止
restricted ベストプラクティスを強制 baseline + runAsNonRootが必須、seccompProfileが必須、capabilitiesをすべてdrop、allowPrivilegeEscalation=false

restrictedを満たすには、Podスペックに最低でも4つが必要です。 runAsNonRoot: true、allowPrivilegeEscalation: false、capabilities.drop: [ALL]、 seccompProfile.type: RuntimeDefaultです。

3つのモード

モード 動作 使うとき
enforce 違反するPodの作成を拒否 実際の適用
audit 監査ログにだけ記録し、作成は許可 影響の把握
warn kubectlユーザーに警告を表示し、作成は許可 移行の準備

ラベルはpod-security.kubernetes.io/<모드>: <프로파일>の形で(プレースホルダーはモードとプロファイルです)、 <모드>-versionでバージョンを固定できます(プレースホルダーはモードです)。

labels:
  pod-security.kubernetes.io/enforce: baseline
  pod-security.kubernetes.io/enforce-version: v1.30
  pod-security.kubernetes.io/audit: restricted
  pod-security.kubernetes.io/warn: restricted

バージョンの固定がなぜ重要なのか。固定しないとlatestになり、クラスターをアップグレードした瞬間に ポリシーの内容が変わる可能性があります。デプロイした覚えがないのにPodが拒否され始める事故は、ここから起きます。

上の例は、実務で最もよくある組み合わせです。enforceはbaselineで実際に遮断しつつ、 audit/warnはrestrictedで設定して、「restrictedに上げると何が壊れるか」をあらかじめ収集します。

必ず知っておくべき性質: アドミッション時にしか動作しない

PSAが判断するのは、Podが作成されるときだけです。すでに動いているPodは、ラベルを上げても追い出されません。 そのためenforceをrestrictedに上げた直後は何も起きていないように見えますが、 次のロールアウトやノードの入れ替えのときにPodが起動せず、問題が表面化します。ポリシーの変更と事故の間に、時間差があります。

適用除外(exemptions)

ネームスペースのラベルでは表現できない例外があるため、AdmissionConfigurationに3つの軸の適用除外を置きます。

適用除外はクラスター全体の設定なのでapiserverを再起動する必要があり、ネームスペースのラベルでは変更できません。 これは安全装置でもあります。ネームスペースの管理者が自分自身を適用除外にできないからです。

PSAでは足りないとき

PSAはプロファイルが3つしかないため、「このレジストリのイメージだけを許可」「すべてのPodにリソース制限を必須にする」 「チームラベルのないネームスペースを禁止」のようなことは表現できません。そのときはポリシーエンジンを重ねます。

著者のブログが下した判断は明確です。「単純なポリシーにはKyverno、複雑なクロスリソース検証には OPA Gatekeeper」、そして推奨される組み合わせはRBAC + PSA + ポリシーエンジン(RBACは基本の認可、PSAはPod Securityの基準、ポリシーエンジンはカスタム)です。

Gatekeeperには、アドミッションWebhookが見逃す穴が1つあるため、Audit Controllerが別にあります。 Webhookが止めるのは新規のリクエストだけなので、ポリシーを後から追加すると、すでにデプロイされた違反リソースはそのまま残ります。 Auditが定期的に全体を走査して、それを見つけ出します。enforcementAction: dryrunで遮断せずに 違反だけを収集するのが、新しいポリシーの影響評価の標準的な手順です。

現場での姿

著者のホームラボ再構築の記録に、PSAとまったく同じ原理の出来事があります。 Cilium設置の直後にhubble-relayとhubble-uiがPendingで、理由は 0/1 nodes are available: 1 node(s) had untolerated taint(s)でした。 コントロールプレーンのNoScheduleテイントを、Deploymentがトレラレーションで許容していなかったのですが、 「正常な動作なのに事故のように見える」タイプだという点で、PSAの移行と性格が同じです。 著者の結論も同じでした。「これはエラーではなく、正常な動作です。」

PSA移行の標準的な手順も、著者がまとめたとおりです。 ① dry-runで現在の状態を監査(kubectl label --dry-run=server --overwrite ns --all ...)、 ② warn/auditを先に適用、③ 違反しているワークロードを修正、④ enforceを有効化、 ⑤ AdmissionConfigurationで新規ネームスペースにデフォルト値を適用。 「段階的なアプローチが核心であり、決して一度にenforceを適用しません。」

続けて読むこと

まずシークレット管理の読み物をもう1本読み、そのあとのラボで、ネームスペースにPSAラベルを自分で付けます。 restrictedに違反するPodが拒否されることとその拒否メッセージを確認し、restrictedを通過するPodを 作ってみて、最後にラベルだけを変えて等級を上げたとき、既存のPodはそのまま生き残り、 新しいPodだけが拒否されることを自分で観察します。