検索
連載

Kubernetes 1.25からのスタンダード「Pod Security Admission」でPod/コンテナのセキュリティを強化しようCloud Nativeチートシート(20)

Kubernetesやクラウドネイティブをより便利に利用する技術やツールの概要、使い方を凝縮して紹介する連載。2022年8月23日にKubernetes v1.25がリリースされました。このバージョンでは「Pod Security Policy」が削除され、「Pod Security Admission」がGAになり、Podセキュリティのデファクトスタンダートがバトンタッチされました。今回は、このPod Security Admissionについて詳解します(Kubernetes v1.31に合うように更新)。

Share
Tweet
LINE
Hatena

 Kubernetesは今やコンテナ実行基盤のデファクトスタンダードですが、そこにデプロイするコンテナ(Pod)の設定について、どの程度意識できているでしょうか。KubernetesにPodをデプロイする際には、マニフェストでコンテナに対するさまざまな設定ができます。しかし、その設定次第では、デプロイしたPod、ひいてはKubernetesクラスタ全体に致命的な脆弱(ぜいじゃく)性をもたらすことにつながります。

 Kubernetesでは、このようなPodに対する脆弱な設定を未然に防ぐ機能として、これまでは「Pod Security Policy」がβ版として提供されていましたが、本機能は正式リリース(GA)で日の目を見ることなくKubernetes v1.25で削除されました。それに代わる機能としてKubernetes v1.25以降では、「Pod Security Admission」がGAとなり、正式に利用できるようになりました。

 Kubernetesやクラウドネイティブをより便利に利用する技術やツールの概要、使い方を凝縮して紹介する本連載「Cloud Nativeチートシート」。今回は、脆弱な設定によるセキュリティリスクを踏まえ、Pod Security Admissionを用いてどのようにセキュリティリスクを回避するかについて解説します。

コンテナのリスクとは?

 脆弱なコンテナ設定を含むPodをKubernetesにデプロイすると、以下のようなリスクにつながる場合があります。

外部の攻撃者に基盤を乗っ取られるリスク

 脆弱なコンテナの設定を含んだ状態でWebサーバPodをデプロイし、外部に公開しているケースを想定します。

 万一、外部の攻撃者が何らかの手段によってWebサーバPodに侵入できた場合、攻撃者はコンテナの脆弱性を悪用してWebサーバPodのみならず、そのPodがデプロイされているホストや、そのホスト上で動作している他のPodなど、攻撃の範囲を特定のPodだけでなく基盤全体に拡大できることになります。

アプリ開発者が与えられた権限を越えてクラスタを操作できるリスク

 Kubernetesクラスタをコンテナ基盤としてアプリ開発者に提供しているケースを想定します。アプリ開発者にはKubernetesにPodをデプロイするといった基本的な操作は許可したいですが、Worker Nodeなどのクラスタ構成要素にはアクセスさせたくありません。

 ところが、悪意のあるアプリ開発者が脆弱なコンテナ設定を含むPodをKubernetesにデプロイした場合、悪意のある開発者は自身のデプロイしたPodを経由して本来権限の与えられていないWorker Nodeに不正にアクセスし、攻撃できてしまうケースがあります。

脆弱なコンテナ設定によるリスクの具体例

 脆弱なコンテナの設定に伴うリスクの例を2点挙げましたが、説明だけ聞いても具体的にぴんとこない方も多いと思います。そこで具体的なリスクを実感するために、ここでは実験として脆弱なコンテナの設定を含むPodマニフェストを用いてKubernetesにPodをデプロイし、コンテナの設定が脆弱だとどのようなことができてしまうのかを見てみましょう。

※注意※

 この実験は必ず検証環境など他のサービスに影響のない環境で実施してください。

 本内容は決してサイバー攻撃を肯定するものではありませんので悪用はしないでください。


 脆弱なコンテナの設定を含むPodマニフェストを用意します。

apiVersion: v1
kind: Pod
metadata:
  name: exploit-pod
spec:
  hostPID: true
  containers:
  - name: ubuntu
    image: ubuntu:22.04
    command: ["/bin/sh", "-c", "while :; do sleep 10; done"]
    securityContext:
      privileged: true
exploit-pod.yaml

 続いて、用意したPodマニフェストを用いてKubernetesにPodをデプロイしてみます。

$ kubectl apply -f exploit-pod.yaml
pod/exploit-pod created
 
$ kubectl get po
NAME                 READY   STATUS    RESTARTS      AGE
exploit-pod          1/1     Running   0             16s

 デプロイが完了したら、次のコマンドでPod内のコンテナに接続します。

$ kubectl exec -it exploit-pod -- /bin/bash
root@exploit-pod:/# hostname
exploit-pod

 コンテナに接続できたらコンテナ内で次のようなコマンドを実行してみます。

root@exploit-pod:/# nsenter -t 1 -a /bin/bash
 
root@k8s-cluster-worker01:/# hostname
k8s-cluster-worker01
root@k8s-cluster-worker01:/# shutdown now

 KubernetesにデプロイしたPod内のコンテナを経由してPodが起動しているホスト(KubernetesのWorker Node)に侵入し、ホスト上での任意のコマンドを実行したり、ホストのシャットダウンができたりしてしまいました。

 詳細な解説は省略しますが、今回の例では「hostPID: true」「privileged: true」の部分が脆弱なコンテナの設定に該当し、そのような設定を持つPodがKubernetesにデプロイ可能な状態だったことで上記の操作が可能になっていたといえます。

コンテナセキュリティを確保するためのKubernetes公式ガイドライン「Pod Security Standards」

 上記のようなセキュリティリスクを発生させないためには、Kubernetesに脆弱な設定のコンテナを含むPodをデプロイさせないことが重要ですが、そもそもどのようなコンテナ設定なら「安全」といえるのでしょうか。

 Kubernetesの公式ドキュメントでは「Pod Security Standards」というガイドラインを提供しています。このガイドラインでは下記の3種類のポリシーに応じたコンテナの設定項目が用意されており、それらに準拠することで所定のセキュリティレベルを確保できます。

ポリシー 概要 設定項目の例 セキュリティレベル
Privileged コンテナの設定項目に規定を設けないポリシー -
Baseline コンテナへの特権付与などリスクが明確な設定項目について最低限規定したポリシー ・コンテナへの特権付与を禁止
・コンテナとホスト間でのNamespaceやprocの共有を禁止
・HostPathの利用を禁止
・HostPortの利用を禁止
・Sysctlsによって変更可能なカーネルパラメーターの制限
・コンテナへのCapability付与の制限
・セキュリティ機能(AppArmor、SELinux、Seccomp)についてセキュリティレベルを低下させる設定の禁止
Restricted コンテナの実行ユーザーなど詳細な設定項目までを規定したベストプラクティスに該当するポリシー ・rootユーザーでのコンテナ起動の禁止
・コンテナ内での特権昇格の禁止
・コンテナへのCapability付与の禁止(NET_BIND_SERVICEを除く)
・セキュリティ機能(Seccomp)の明示的な有効化
・利用可能なVolumeTypesの制限

※Pod Security Standardsの「Privileged」はポリシーのプロファイル名を示すもので、先ほどの例で出てきたコンテナ設定の「privileged」という項目(コンテナに対する特権付与)とは別物なのでご注意ください。

 これらのポリシーについて、ベストプラクティスの「Restricted」ポリシーに準拠することが望ましいですが、それが難しい場合でも特別な理由がない限りは最低限「Baseline」ポリシーに準拠するといいでしょう。システムコンポーネントなどRestricted/Baselineポリシーに準拠することが難しい場合はPrivilegedポリシーを適用することになります。

 各ポリシーで具体的にどのような設定が定義されているかについてはPod Security Standardsを確認してください。

Kubernetesにおけるポリシー制御

 「脆弱なコンテナの設定を含むPodがKubernetesにデプロイされるのをどのような方法で防ぐか」について説明します。

 KubernetesにPodをデプロイする際、その設定をPod Security Standardsのような特定のポリシーに準拠させる仕組みを一般的に「ポリシー制御」といいます(厳密にはコンテナの設定に対する制御は狭義のポリシー制御に該当し、広義のポリシー制御はコンテナの設定以外に各種Kubernetes上のリソースや命名規則などについても制御することを指します)。

 ポリシー制御の中でも設定を検査した結果、ポリシーに準拠していないPodのデプロイを禁止することを「Validation」、ポリシーに準拠していないPodの設定を上書きして強制的にポリシーに準拠させることを「Mutation」といいます。

 Kubernetesではv1.3から「Pod Security Policy」というBuilt-inのポリシー制御の仕組みが存在していましたが、v1.21で非推奨という扱いになり、今回のv1.25リリースをもって正式にKubernetesの機能から削除されました

 これに対してKubernetes公式ではPod Security Policyに代わるポリシー制御の実現方式として、「OPA Gatekeeper」「Kyverno」に代表されるサードパーティーツールや、今回v1.25で正式にGAとなったKubernetesにビルトインされたポリシー制御の仕組み「Pod Security Admission」の利用を推奨しています。さらにv1.30では、任意のValidationポリシーを定義して適用できる「Validating Admission Policy」がビルトイン機能として追加されました。

コラム Pod Security Policy廃止の背景

 Pod Security Policyでは以前から主に以下の点が問題視されており、コミュニティーでの議論の結果、廃止という結論に至っています。

  • Pod Security PolicyではUserまたはServiceAccountに対してポリシーを付与できるが、この仕様によってポリシー制御が複雑化し、意図した制御ができない場合がある(例えばUserに特定のポリシー(「ポリシーA」とする)が付与されていない場合でも、デプロイするPodにポリシーAが付与されたServiceAccountがバインドされていれば、結果としてUserはポリシーAに準じてPodをデプロイできてしまう)
  • ポリシーを適用する前に影響確認(Dry-runやAudit)ができないので、ポリシー適用に伴って予期せぬ影響を生む可能性がある
  • UserまたはServiceAccountに複数のポリシーが付与されている場合に優先順位が設定できない

 詳細な廃止理由についてはKEP-2579やKubernetes Blogに掲載されているこちらの記事を確認してください。

 既にPod Security Policyによるポリシー制御を行っている場合については、Pod Security Admissionへの移行ガイドがKubernetes公式ドキュメントで提供されているので併せて確認してください。詳細はリンク先にありますが、移行時の主なポイントは次の通りです。

  • Pod Security Policyへの機能依存の観点からPod Security Admissionに移行可能かどうかを検討する
    Pod Security Admissionには現時点でPod Security Policyのようにポリシーを詳細にカスタマイズしたり、Mutationによって設定を書き換えたりする機能が含まれていないので、これらを考慮して移行可能かどうかを検討し、移行不可と判断される場合はサードパーティーツールの利用を検討する
  • Pod Security Policyで行っているポリシー制御の内容を見直す
    Pod Security AdmissionではPod Security Standardに沿って制御(Validation)することになるので、Pod Security Policyで同等のポリシー制御にし、影響を確認した上で移行する
  • Namespaceに対する権限を見直す
    Pod Security Admissionではポリシーの適用対象が原則Namespaceになるので、Namespaceに対して適切に権限を制御しないと誰でもポリシーを変更できてしまうことになる

Pod Security Admissionによるポリシー制御

 今回は、v1.25で正式にGAとなった、Kubernetesにビルトインされたポリシー制御の仕組み「Pod Security Admission」の利用を推奨しています。さらにv1.30では、任意のValidationポリシーを定義して適用できる「Validating Admission Policy」がビルトイン機能として追加されました。また、Mutation機能を提供する「Mutating Admission Policy」はKubernetes v1.32からalpha版として提供されています。

 ポリシーの適用対象はNamespaceおよびクラスタ全体となっており、適用前の事前確認(Dry-run)も可能です。

 Pod Security Admissionでは下記表の3つのモードが提供されており、各モードに対してPod Security Standardsのいずれかのポリシーを指定します。各モードに対してポリシーを指定しなかった場合、そのモードには暗黙的にPrivileged(Podのデプロイを制限しない)が設定されることになります。

モード ポリシーに違反するPodのデプロイを検知した場合の挙動
enforce Podのデプロイを禁止する
audit KubernetesのAudit Logに記録する(Podはデプロイされる)
warn 警告を表示する(Podはデプロイされる)

 ここからは具体的にPod Security Admissionによるポリシー制御の例を見ていきます。

Namespaceに対してポリシーを適用する

 基本的な使い方として、Namespaceに対してポリシーを適用し、ポリシー制御を行ってみます。Namespaceに対してポリシーを適用する際は、Namespaceのlabelsで各モードそれぞれどのレベルのポリシーを適用するかを設定します。

 下記「namespace-a.yaml」はNamespaceに関するマニフェストです。このマニフェストの「labels」を見ると、「enforce」モードとしてBaselineポリシーを、「audit」「warn」モードとしてRestrictedポリシーを指定していることが分かります。

apiVersion: v1
kind: Namespace
metadata:
  name: namespace-a
  labels:
    # Baselineポリシーに違反するPodのデプロイを禁止
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/enforce-version: v1.31
    # Restrictedポリシーに違反するPodのデプロイを検知した場合はAudit Logに記録
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: v1.31
    # Restrictedポリシーに違反するPodのデプロイを検知した場合は警告を表示
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: v1.31
namespace-a.yaml

 このNamespace内では、Kubernetes v1.31におけるPod Security Standardsに準じて、次のようなポリシー制御が行われます。

  • Baselineポリシーに違反するPodのデプロイを禁止する(enforce)
  • Restrictedポリシーに違反するPodのデプロイを検知した場合はAudit Logに記録(audit)および警告を表示(warn)する

 本稿ではKubernetes v1.31時点での内容を紹介していますが、Pod Security Standardsのポリシー内容はKubernetesのバージョンアップによって変更されることがあります(例えばコンテナへのseccompに関する設定はv1.21ではRestrictedポリシーに含まれていましたが、v1.22以降はBaselineに含まれるようになりました)。最新の情報はKubernetesのWebサイトを確認してください。

 上記の例では明示的にv1.31というバージョンを設定していますが、「latest」を設定することで利用可能な最新バージョンのポリシーが使われるようになります。

 現在Kubernetes上には上記マニフェストを用いて作成した「namespace-a」と、特にポリシーを指定せずに作成した「namespace-b」が存在しているとします。これらに対して先の説明で用いた脆弱なコンテナの設定を含むPodマニフェスト「exploit-pod.yaml」を適用し、Podをデプロイしてみましょう。

# kubectl get ns
NAME                    STATUS   AGE
……
namespace-a             Active   69s
namespace-b             Active   60s

 namespace-aに対して、最初に紹介した脆弱性を含むexploit-pod.yamlを適用します。すると、次のようにエラーが表示され、Podのデプロイが禁止されたことが分かります。

# kubectl apply -f exploit-pod.yaml -n namespace-a
Error from server (Forbidden): error when creating "exploit-pod.yaml": pods "exploit-pod" is forbidden: violates PodSecurity "baseline:v1.31": host namespaces (hostPID=true), privileged (container "ubuntu" must not set securityContext.privileged=true)
 
# kubectl get po -n namespace-a
No resources found in namespace-a namespace.

 デプロイが禁止されたのはエラーメッセージにも記載の通り、デプロイしようとしたPodに含まれる脆弱なコンテナの設定(hostPID: trueおよびprivileged: true)がPod Security StandardsのBaselineポリシーに違反したからです。

 どう違反しているのかをAudit Logで確認してみましょう。

{
    "kind": "Event",
……
    "responseStatus": {
        "metadata": {},
        "status": "Failure",
        "message": "pods \"exploit-pod\" is forbidden: violates PodSecurity \"baseline:v1.31\": host namespaces (hostPID=true), privileged (container \"ubuntu\" must not set securityContext.privileged=true)",
        "reason": "Forbidden",
        "details": {
            "name": "exploit-pod",
            "kind": "pods"
        },
        "code": 403
    },
    "requestReceivedTimestamp": "2024-08-25T14:37:11.624385Z",
    "stageTimestamp": "2024-08-25T14:37:11.634141Z",
    "annotations": {
        "authorization.k8s.io/decision": "allow",
        "authorization.k8s.io/reason": "",
        "pod-security.kubernetes.io/audit-violations": "would violate PodSecurity \"restricted:v1.31\": host namespaces (hostPID=true), privileged (container \"ubuntu\" must not set securityContext.privileged=true), allowPrivilegeEscalation != false (container \"ubuntu\" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container \"ubuntu\" must set securityContext.capabilities.drop=[\"ALL\"]), runAsNonRoot != true (pod or container \"ubuntu\" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container \"ubuntu\" must set securityContext.seccompProfile.type to \"RuntimeDefault\" or \"Localhost\")",
        "pod-security.kubernetes.io/enforce-policy": "baseline:v1.31"
    }
}

 Pod Security StandardsのBaselineポリシーに違反したことによってPodのデプロイが拒否されたことが、「responseStatus」で記録されているのが分かります。「annotations」でRestrictedポリシーに違反している旨も記録されています(Audit log出力結果はJSONフォーマットを整形した状態で記載しています)。

※KubernetesにおいてAudit Logはデフォルトで無効化されているので、有効にしたい場合はKubernetes公式ドキュメントに沿って設定する必要があります。

 続いてexploit-pod.yamlから「hostPID: true」「privileged: true」の設定を除外し、次のようなPodマニフェストを用意します。

apiVersion: v1
kind: Pod
metadata:
  name: baseline-pod
spec:
  containers:
  - name: ubuntu
    image: ubuntu:20.04
    command: ["/bin/sh", "-c", "while :; do sleep 10; done"]
baseline-pod.yaml

 先ほどと同様にnamespace-aに対してbaseline-pod.yamlを適用してみます。

# kubectl apply -f baseline-pod.yaml -n namespace-a
Warning: would violate PodSecurity "restricted:v1.31": allowPrivilegeEscalation != false (container "ubuntu" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "ubuntu" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "ubuntu" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "ubuntu" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
 
# kubectl get po -n namespace-a
NAME           READY   STATUS    RESTARTS   AGE
baseline-pod   1/1     Running   0          35s

 すると、今度はエラーではなく警告が表示され、Pod自体は正常にデプロイすることができました。Podが正常にデプロイできたのは、「hostPID: true」「privileged: true」をコンテナの設定から除外したことでBaselineポリシーに準拠するようになったからです。一方、依然Restrictedポリシーには準拠していないので、結果として警告が表示されました。

 Audit Logについても確認してみると、下記のようにRestrictedポリシーに違反している旨がannotationsに記録されていることが分かります(Audit log出力結果はJSONフォーマットを整形した状態で記載しています)。

{
    "kind": "Event",
……
    "responseStatus": {
        "metadata": {},
        "code": 201
    },
    "requestReceivedTimestamp": "2024-08-25T14:47:23.225779Z",
    "stageTimestamp": "2024-08-25T14:47:23.239878Z",
    "annotations": {
        "authorization.k8s.io/decision": "allow",
        "authorization.k8s.io/reason": "",
        "pod-security.kubernetes.io/audit-violations": "would violate PodSecurity \"restricted:v1.31\": allowPrivilegeEscalation != false (container \"ubuntu\" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container \"ubuntu\" must set securityContext.capabilities.drop=[\"ALL\"]), runAsNonRoot != true (pod or container \"ubuntu\" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container \"ubuntu\" must set securityContext.seccompProfile.type to \"RuntimeDefault\" or \"Localhost\")",
        "pod-security.kubernetes.io/enforce-policy": "baseline:v1.31"
    }
}

 当然ですが、ポリシーが何も適用されていないnamespace-bに対してexploit-pod.yamlを適用して見ると、正常にPodをデプロイすることができます。

# kubectl apply -f exploit-pod.yaml -n namespace-b
pod/exploit-pod created
 
# kubectl get po -n namespace-b
NAME          READY   STATUS    RESTARTS   AGE
exploit-pod   1/1     Running   0          7s

Dry-runによるポリシー適用の影響確認

 運用で想定されるケースとして、現在のnamespace-bのように「既にPodが起動しているNamespaceに対してポリシーを適用したい」場合も考えられます。Pod Security AdmissionによるValidationはPod作成時にのみ機能するので、この状態でnamespace-bにlabelsを付与してポリシーを有効にしても、ポリシーに違反しているexploit-podが即座に停止されることはありません。

 当然ですが、ポリシー適用後にこのPodを再作成しようとすれば、先の例同様にPodの作成が禁止されます。トラブルを避ける意味でも、これから適用しようとしているポリシーに違反しているPodがNamespace内に存在しない状態にしておくべきです。

 その際役に立つのがDry-run機能です。下記コマンドを実行すると、namespace-bにlabelsを付与せずに「ポリシーを適用した場合に、どのようなポリシー違反が発生し得るか」を事前に確認できます。

# kubectl label --dry-run=server --overwrite ns namespace-b pod-security.kubernetes.io/enforce=baseline pod-security.kubernetes.io/enforce-version=v1.31
Warning: existing pods in namespace "namespace-b" violate the new PodSecurity enforce level "baseline:v1.31"
Warning: exploit-pod: host namespaces, privileged
namespace/namespace-b labeled
 
# kubectl get ns namespace-b --show-labels
NAME          STATUS   AGE    LABELS
namespace-b   Active   116m   kubernetes.io/metadata.name=namespace-b

 既にPodが起動しているNamespaceにポリシーを適用する場合、この結果を踏まえて、Podに含まれるコンテナの設定を見直した上で行うのがいいでしょう。

Kubernetesクラスタにデフォルトポリシーを定義する

 1つ目の例では特定のNamespaceに対してポリシーを適用しましたが、逐一Namespaceごとにポリシーを適用するのではなく、「各NamespaceにKubernetesクラスタとして定義したデフォルトポリシーが適用されるようにしたい」ケースもあるでしょう。2つ目の例としては、Kubernetesクラスタにデフォルトポリシーを定義します。

 Kubernetesクラスタにデフォルトポリシーを定義する際は、「AdmissionConfiguration」としてポリシーを定義します。

 下記「pod-security.yaml」はKubernetesクラスタのデフォルトポリシーを定義したAdmissionConfigurationの設定ファイルです。ポリシーの内容としては、1つ目の例と同じく、enforceモードとしてBaselineポリシーを、auditおよびwarnモードとしてRestrictedポリシーを指定しています。デフォルトポリシーの適用を除外するNamespaceとしてkube-systemとnamespace-cを指定しています。

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
  configuration:
    apiVersion: pod-security.admission.config.k8s.io/v1beta1
    kind: PodSecurityConfiguration
    defaults:
      enforce: "baseline"
      enforce-version: "v1.31"
      audit: "restricted"
      audit-version: "v1.31"
      warn: "restricted"
      warn-version: "v1.31"
    exemptions:
      # デフォルトポリシーを適用しないユーザーを指定する
      usernames: []
      # デフォルトポリシーを適用しないRuntimeClassを指定する
      runtimeClasses: []
      # デフォルトポリシーを適用しないNamespaceを指定する
      namespaces:
        - kube-system
        - namespace-c
pod-security.yaml

 AdmissionConfigurationでデフォルトポリシーを定義する際は、ポリシーの適用除外対象としてUser、Namespace、RuntimeClassを指定できます。

※なお、「RuntimeClass」はPod起動時に使用するOCIランタイム(低レベルコンテナランタイム)を選択するKubernetesの機能です。デフォルトのOCIランタイム(runcなど)以外のOCIランタイム(「gVisor」「Kata Container」など)を利用する際に指定します。

 「kube-proxy」などKubernetesを構成するシステムコンポーネントは、その仕様上Pod Security Standardsのポリシーに準拠していないケースがあるので、上記の例では「kube-system Namespace」をデフォルトポリシーの適用対象から除外しています。他にも、デフォルトポリシーが全てのPodに適用されると、監視コンポーネントやCNI(Container Network Interface)、CSI(Container Storage Interface)のDaemonSetなど、強い権限が必要なPodが動作しなくなる場合があります。そのため、デフォルトポリシーを定義する際は先ほど紹介したDry-runの機能を用いて事前に影響を確認し、必要に応じてデフォルトポリシーの適用除外を設定するといいでしょう。

 AdmissionConfigurationをKubernetesクラスタに適用するには、「kube-apiserver」の起動オプション「--admission-control-config-file」で、上記のpod-security.yamlを指定します。これによって、Kubernetesクラスタにデフォルトポリシーが設定されます。

 下記「/etc/kubernetes/manifests/kube-apiserver.yaml」はkubeadmで構築したKubernetesクラスタのkube-apiserverに対して設定する例です。

kubeadmではkube-apiserverはControl Plane Node上に「Static Pod」としてデプロイされるので、Control Plane Node上に存在するkube-apiserverのPodマニフェストに次のような設定を追加することで自動的にPodが再作成され、AdmissionConfigurationが読み込まれます。

apiVersion: v1
kind: Pod
metadata:
……
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - command:
    - kube-apiserver
……
    - --admission-control-config-file=/etc/kubernetes/policies/pod-security.yaml
……
    volumeMounts:
    - mountPath: /etc/kubernetes/policies
      name: policies
      readOnly: true
……
  volumes:
  - name: policies
    hostPath:
      path: /etc/kubernetes/policies
      type: DirectoryOrCreate
……
/etc/kubernetes/manifests/kube-apiserver.yaml

※Pod Security Admissionはkube-apiserverの「Admission Controller」に含まれる「PodSecurity Plugin」によって実現されています。そのため、Admission Controllerを設定するAdmissionConfigurationファイルでPodSecurity Pluginを設定します。

 設定が完了したら、Kubernetesクラスタにデフォルトポリシーが定義されたかどうかを確認します。1つ目の例でnamespace-bには個別でポリシーを適用していないのでexploit-pod.yamlを用いて正常にPodをデプロイできましたが、Kubernetesクラスタにデフォルトポリシーを定義した状態ではどうでしょうか。

 namespace-bに対してexploit-pod.yamlを適用してみると、次のようにエラーが表示されてPodのデプロイが禁止されたことが分かります(※既に1つ目の例でnamespace-bにPodが作成されている場合は一度削除してから実施してください)。

# kubectl apply -f exploit-pod.yaml -n namespace-b
Error from server (Forbidden): error when creating "exploit-pod.yaml": pods "exploit-pod" is forbidden: violates PodSecurity "baseline:v1.31": host namespaces (hostPID=true), privileged (container "ubuntu" must not set securityContext.privileged=true)
kubectl get po -n namespace-b
No resources found in namespace-b namespace.

 Audit Logについては省略しますが、1つ目の例と同様の内容が出力されているはずです。

 続いてデフォルトポリシーで適用除外対象として指定した「namespace-c」を作成し、同様にexploit-pod.yamlを適用してみます。namespace-cはデフォルトポリシーが適用されず、個別でポリシーも適用していないので、次の通り、正常にPodをデプロイできました。

# kubectl create ns namespace-c
namespace/namespace-c created
 
# kubectl apply -f exploit-pod.yaml -n namespace-c
pod/exploit-pod created
 
# kubectl get po -n namespace-c
NAME          READY   STATUS    RESTARTS   AGE
exploit-pod   1/1     Running   0          20s

 最後に、下記「namespace-d」に示すようなデフォルトポリシーよりも緩いポリシーを適用した場合を見てみます。デフォルトポリシーとnamespace-dに適用したポリシーではどちらが優先されるのでしょうか。

apiVersion: v1
kind: Namespace
metadata:
  name: namespace-d
  labels:
    # Privilegedポリシー(Podのデプロイを制限しない)
    pod-security.kubernetes.io/enforce: privileged
    pod-security.kubernetes.io/enforce-version: v1.31
    # Baselineポリシーに違反するPodのデプロイを検知した場合はAudit Logに記録
    pod-security.kubernetes.io/audit: baseline
    pod-security.kubernetes.io/audit-version: v1.31
    # Baselineポリシーに違反するPodのデプロイを検知した場合は警告を表示
    pod-security.kubernetes.io/warn: baseline
    pod-security.kubernetes.io/warn-version: v1.31
namespace-d.yaml

 namespace-dに対してexploit-pod.yamlを適用すると、次のように、namespace-dに適用したポリシーに従って「Podは正常にデプロイされつつも、Baselineポリシーに準拠していない」旨が警告として表示されるのが分かります。

kubectl apply -f exploit-pod.yaml -n namespace-d
Warning: would violate PodSecurity "baseline:v1.31": host namespaces (hostPID=true), privileged (container "ubuntu" must not set securityContext.privileged=true)
pod/exploit-pod created
 
kubectl get po -n namespace-d
NAME          READY   STATUS    RESTARTS   AGE
exploit-pod   1/1     Running   0          9s

 このように、デフォルトポリシーが定義されている状態でNamespaceに個別のポリシーを適用した場合は、Namespaceのポリシーが優先されます。

コラム サードパーティーツールの利用

 ポリシー制御を実施するに当たって、組織によっては次のような要件を満たしたいケースが発生することもあると思います。

  1. Kubernetesのマニフェストがポリシーに違反している場合、強制的にポリシーに準拠するように内容を変更してデプロイしたい(Mutation)
  2. Pod Security Standardにない独自のポリシーを定義してポリシーを強制したい

 こういったケースに対応する必要がある場合は、先述のOPA GatekeeperやKyvernoに代表されるサードパーティーツールや、Validation Admission Policy/Mutation Admission Policyの利用を検討するとよいでしょう(※Mutation Admission Policyはv1.32時点で開発中(alpha)のステータスであり、今後変更されれる可能性があるので注意が必要です)。

 ポリシー制御とは直接関係がありませんが、Kubernetesクラスタに対してマニフェストを適用する以前に、そのマニフェストに含まれる脆弱な設定をチェックしたいケースがあると思います。

 そういった場合は本連載第17回で紹介した「Trivy」をはじめとした、チェックツールを用いるのがいいでしょう。


まとめ

 Pod Security AdmissionはOPA Gatekeeperなど一般的なサードパーティーツールには柔軟性の面では劣るものの、Kubernetes Built-inの仕組みとして手軽にポリシー制御を実現できるという観点から有用だと思います。従来のPod Security Policyと比較しても使い方が比較的シンプルだったり、利用者がポリシーを定義する必要がなかったりといった点から、ポリシー制御をKubernetesに導入するハードルもかなり低くなったといえます。

 現在運用しているKubernetesクラスタへのセキュリティ対策として「まず何から実施しよう」と迷っている場合は、この機会にPod Security Admissionの利用を検討してみてはいかがでしょうか。

■更新履歴

【2024/12/17】Kubernetes v1.31に合うように更新しました。


Copyright © ITmedia, Inc. All Rights Reserved.

[an error occurred while processing this directive]
ページトップに戻る