Pod Security AdmissionとsecurityContext
目標
ネームスペースのラベルだけでワークロードのセキュリティの下限を決める方法を身に付け、restrictedプロファイルを実際に通過するPodスペックを手で完成させます。
なぜ重要なのか
PodSecurityPolicyは1.25で削除されました。使うにはServiceAccountに「このPSPをuseする権限」を与える必要があり、その構造そのものが権限昇格の経路でしたし、複数のPSPのうちどれが適用されるかを予測するのも困難だったためです。代替のPSAは、ネームスペースのラベル数行で済み、enforce・audit・warnの3つのモードを別々に有効にできるため、段階的な導入が可能です。既存のクラスターにいきなりenforceを設定するとワークロードが大量にブロックされるので、warnとauditを先に有効にして観察するのが標準的な手順です。
restrictedプロファイルが要求する4つ(runAsNonRoot、allowPrivilegeEscalation false、capabilities drop ALL、seccompProfile)は、CKSで最もよく出る組み合わせです。暗記するよりも、自分で1つずつ外してみて、どんなエラーが出るかを確認したほうが長く記憶に残ります。
この環境のAPIサーバーではPodSecurityアドミッションが有効になっているため、違反したPodは実際に拒否されます。
ステップ
- ネームスペース
cks-psaを作成し、ラベルpod-security.kubernetes.io/enforce=baselineを付けてください。 - ネームスペース
cks-psa-strictを作成し、enforce・audit・warnの3つのラベルをすべてrestrictedにし、さらにenforce-version・audit-version・warn-versionの3つのラベルをすべてlatestにして付けてください。 cks-psa-strictにPodhardened(コンテナ名app、イメージnginx:1.27-alpine)を作成してください。restrictedを通過する必要があるため、PodレベルにrunAsNonRoot: trueとseccompProfile.type: RuntimeDefault、コンテナレベルにallowPrivilegeEscalation: falseとcapabilities.drop: [ALL]が必要です。cks-psa-strictに、privileged: trueのコンテナを持つPodbad-podをapplyしてみてください。その出力(標準エラー出力を含む)を/root/cks-securitycontext/denied.txtに保存します。Podが作成されてはいけません。cks-psaにPodnonroot-app(コンテナ名app)を作成してください。PodレベルのsecurityContextにrunAsNonRoot: true、runAsUser: 10001、runAsGroup: 10001、fsGroup: 20001を入れます。cks-psa-strictにPoddropped(コンテナ名app)を作成してください。PodレベルのseccompProfile.typeはRuntimeDefault、runAsNonRootはtrueとし、コンテナレベルにallowPrivilegeEscalation: false、capabilities.drop: [ALL]、readOnlyRootFilesystem: trueを入れます。- RuntimeClass
cks-sandboxを作成してください。handlerはrunsc、overhead.podFixedはメモリ160MiとCPU250mです。 cks-psa-strictにDeploymentpaymentsを作成してください。replicasは2、セレクターとPodラベルはapp=payments、PodスペックのruntimeClassNameはcks-sandboxとし、Podテンプレートは、restrictedを通過するようにステップ3と同じ4つの条件をすべて備えます。
参考
kubectl label ns cks-psa-strict pod-security.kubernetes.io/enforce=restricted pod-security.kubernetes.io/enforce-version=latest ...- ステップ4の実行例:
kubectl apply -f bad-pod.yaml 2>&1 | tee /root/cks-securitycontext/denied.txt kubectl explain pod.spec.securityContextとkubectl explain pod.spec.containers.securityContextは、それぞれ異なるフィールドの一覧を表示します。- よくある間違い1:
capabilitiesをPodレベルに入れてしまいます。コンテナレベル専用です。 - よくある間違い2:
-versionラベルを抜かすと、クラスターのアップグレード時にプロファイルの定義が変わり、問題なく動いていたPodが拒否されることがあります。
baselineプロファイルを適用する
ネームスペースcks-psaを作成し、ラベルpod-security.kubernetes.io/enforce=baselineを付けてください。
PSAはネームスペースのラベルで動作します。ラベルのキーはpod-security.kubernetes.io/で始まります。
3つのモードとバージョンラベルをすべて設定する
ネームスペースcks-psa-strictを作成し、enforce・audit・warnの3つのラベルをすべてrestrictedにし、さらにenforce-version・audit-version・warn-versionの3つのラベルをすべてlatestにして付けてください。
enforce・audit・warnのそれぞれに対応する-versionラベルが別にあります。バージョンを固定すると、クラスターのアップグレードがそのままポリシーの強化につながることがありません。
restrictedを通過するPod
cks-psa-strictにPodhardened(コンテナ名app、イメージnginx:1.27-alpine)を作成してください。restrictedを通過する必要があるため、PodレベルにrunAsNonRoot: trueとseccompProfile.type: RuntimeDefault、コンテナレベルにallowPrivilegeEscalation: falseとcapabilities.drop: [ALL]が必要です。
restrictedは4つを必須として要求します。1つでも欠けると拒否され、エラーメッセージがどのフィールドが引っかかったかを教えてくれます。
もう1つ知っておいてください。runAsNonRoot: trueはUIDを指定しません。「rootで動かすな」と言っているだけなので、イメージがUIDを指定していないと、本物のノードではkubeletがPodを起動できず、CreateContainerConfigErrorが出ます。実際のクラスターでは、runAsUserを一緒に指定するか、非rootで作られたイメージを使ってください。このラボ環境はPodを実際には実行しないため、その違いは現れません。
違反したPodが拒否されることを確認する
cks-psa-strictに、privileged: trueのコンテナを持つPodbad-podをapplyしてみてください。その出力(標準エラー出力を含む)を/root/cks-securitycontext/denied.txtに保存します。Podが作成されてはいけません。
applyの標準出力と標準エラー出力を、一緒にファイルに残す必要があります。2>&1 | teeを使ってください。Podが作成されたなら、ポリシーが効いていないということです。
実行ユーザーとグループを指定する
cks-psaにPodnonroot-app(コンテナ名app)を作成してください。PodレベルのsecurityContextにrunAsNonRoot: true、runAsUser: 10001、runAsGroup: 10001、fsGroup: 20001を入れます。
runAsUser/runAsGroup/fsGroupはPodレベルのsecurityContextに、runAsNonRootは両方に書けます。fsGroupは、ボリューム内のファイルのグループ所有権を変更します。
seccompとcapabilityの組み合わせ
cks-psa-strictにPoddropped(コンテナ名app)を作成してください。PodレベルのseccompProfile.typeはRuntimeDefault、runAsNonRootはtrueとし、コンテナレベルにallowPrivilegeEscalation: false、capabilities.drop: [ALL]、readOnlyRootFilesystem: trueを入れます。
restrictedのネームスペースなので、このPodも4つの必須条件をすべて満たさないと作成されません。
RuntimeClassオブジェクト
RuntimeClasscks-sandboxを作成してください。handlerはrunsc、overhead.podFixedはメモリ160MiとCPU250mです。
RuntimeClassはクラスタースコープのリソースです。handlerの値は、ノードのコンテナランタイムの設定にあるランタイム名と、一文字ずつ同じでなければなりません。
総合: サンドボックスランタイムでデプロイする
cks-psa-strictにDeploymentpaymentsを作成してください。replicasは2、セレクターとPodラベルはapp=payments、PodスペックのruntimeClassNameはcks-sandboxとし、Podテンプレートは、restrictedを通過するようにステップ3と同じ4つの条件をすべて備えます。
DeploymentのPodテンプレートもPSAの検査を受けます。先に作成したRuntimeClassの名前を、PodスペックのruntimeClassNameに書きます。