kubernetes

Kubernetes: Pod Security Policy によるセキュリティの設定

この記事は Pod Security Policy (PodSecurityPolicy)によるセキュリティの設定について Kubernetes v1.9 で確認した内容になります。v1.9 未満では RBAC 周りで大きな違いがあるのでご注意ください。

PodSecurityPolicy とは

PodSecurityPolicy とはクラスタ全体のセキュリティ上のポリシーを定義する機能です。ホストに影響を与える可能性がある 特権 (privileged) や HostIPC などの機能を制限し Pod に脆弱性があった場合にクラスタを守ることができます。ポリシーの制限は Admission Control として実装されており、ポリシーを満たしていない Pod の実行を拒否することができるようになります。Design Proposal を見るとマルチテナントでの利用を想定した機能のようです。v1.9 時点では beta の機能になります。

ポリシーは RBAC (Role-Based Access Control) を使ってユーザごとに分けることができます。これにより柔軟なポリシーの設定が可能です。

なお PodSecurityPolicy も含め、Kubernetes の Pod に関するセキュリティに関連する設定には以下の 3 つがあります。(名前が似通っていて若干わかりづらいですね)

  • PodSecurityContext v1 core Pod 単位 のセキュリティ定義
    • pod.spec.securityContext で Pod 単位で設定
  • SecurityContext v1 core コンテナ単位 のセキュリティ定義
    • pod.spec.containers.securityContext でコンテナ単位に設定
  • PodSecurityPolicy v1beta1 extensions クラスタ単位のセキュリティポリシーの定義
    • podsecuritypolicy というオブジェクトでクラスタ管理者がクラスタ全体のポリシーとして定義する

PodSecurityPolicy は他の 2 つと比べてクラスタ全体のポリシーである点が大きく異なっています。

PodSecurityPolicy で設定できるポリシー

PodSecurityPolicy は下記のようなセキュリティのポリシーを定義することができます。どれもがホストに影響を与える可能性のある設定のポリシーになります。ポリシーは複数の定義が可能で RBAC でユーザごとにポリシーを分けることができます。

フィールド名 内容
allowPrivilegeEscalation bool Pod に allowPrivilegeEscalation の設定を許可するかどうか
allowedCapabilities string array Pod に設定を許可する Linux Capabilities のリスト
allowedHostPaths AllowedHostPath array 利用を許可する hostPath のホワイトリスト
defaultAddCapabilities string array デフォルトで追加する Linux Capabilities のリスト
defaultAllowPrivilegeEscalation boolean デフォルトで allowPrivilegeEscalation を設定するかどうか
fsGroup FSGroupStrategyOptions Pod の fsGroup での設定を制限するかどうか。MustRunAs, RunAsAny が設定でき、MustRunAs の場合は GID の範囲を指定する。
hostIPC boolean Pod に HostIPC の利用を許可するかどうか
hostNetwork boolean Pod に HostNetwork の利用を許可するかどうか
hostPID boolean Pod に HostPID の利用を許可するかどうか
hostPorts HostPortRange array Pod が hostPort として利用可能なポートのレンジを設定する
privileged boolean Pod に privileged の設定を許可するかどうか
readOnlyRootFilesystem boolean true を設定した場合、readOnlyRootFilesystem が true に設定されていないものを拒否する
requiredDropCapabilities string array Pod に設定を必須とする drop する Linux Capabilities のリスト
runAsUser RunAsUserStrategyOptions Pod の runAsUser での設定を制限するかどうか。MustRunAs, MustRunAsNonRoot, RunAsAny が設定でき、MustRunAs の場合は UID の範囲を指定する。
seLinux SELinuxStrategyOptions Pod の seLinux での設定を制限するかどうか。MustRunAs, , RunAsAny が設定でき、MustRunAs の場合は seLinux のラベルを設定する。
supplementalGroups SupplementalGroupsStrategyOptions Pod の SupplementalGroup での設定を制限するかどうか。MustRunAs, RunAsAny が設定でき、MustRunAs の場合は GID の範囲を指定する。
volumes string array ボリュームとして利用を許可する volume plugins のホワイトリスト

PodSecurityPolicy を利用する

PodSecurityPolicy を利用するには下記の設定が必要になります。

  1. Admisson Control で PodSecurityPolicy を有効にする
  2. PodSecurityPolicy を定義する
  3. RBAC で PodSecurityPolicy をユーザに紐付ける

Admisson Control で PodSecurityPolicy を有効にする

API サーバーの --admission-control 引数に PodSecurityPolicy を追加することで、PodSecurityPolicy の Admission Control を有効にすることができます。

:warning: Admission Control を設定しない場合は PodSecurityPolicy を定義してもポリシーは有効になりません。また Admisson Control で PodSecurityPolicy を有効にしポリシーをなにも設定しない状態では全ての Pod が作成できない状態になっています。

# Admisson Control で PodSecurityPolicy を有効にし、何も設定していない状態
$ kubectl run nginx --image nginx:1.13 --restart=Never
Error from server (Forbidden): pods "nginx" is forbidden: no providers available to validate pod request

minikube での Admissin Control の設定

minikube で利用するには --extra-config を利用して、API サーバーの--admission-control 引数を設定します。この記事の記載時点では minikube で v1.9.0 の localkube のイメージが出ていないため、下記のように kubeadm bootstrapper を使って v1.9.0 の環境を作りました。なお、kubeadm を使った場合とデフォルトの localkube では--extra-config の指定の仕方が違うのでご注意ください。

# extra-config を使って API サーバーの引数を設定
$ minikube start --kubernetes-version v1.9.0 --bootstrapper kubeadm \
  --extra-config=apiserver.admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ValidatingAdmissionWebhook,ResourceQuota,DefaultTolerationSeconds,MutatingAdmissionWebhook,PodSecurityPolicy

PodSecurityPolicy を定義する

PodSecurityPolicy は Pod や Deployment と同じく API オブジェクトとして定義をします。ここでは制限のないポリシーと、制限をしたポリシーの 2 つを定義してみます。

制限の無いポリシー

PodSecurityPolicy を利用しない状態と同等の制限の無いポリシーを定義してみます。以下の設定は公式ドキュメントの例 に記載されているものになります。

privileged.yaml
# 制限がないポリシー
# https://kubernetes.io/docs/concepts/policy/pod-security-policy/#example-policies より
apiVersion: extensions/v1beta1
kind: PodSecurityPolicy
metadata:
  name: privileged
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
spec:
  privileged: true
  allowPrivilegeEscalation: true
  allowedCapabilities:
  - '*'
  volumes:
  - '*'
  hostNetwork: true
  hostPorts:
  - min: 0
    max: 65535
  hostIPC: true
  hostPID: true
  runAsUser:
    rule: 'RunAsAny'
  seLinux:
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'RunAsAny'
  fsGroup:
    rule: 'RunAsAny'

制限したポリシー

次に制限したポリシーを定義してみます。この例では特権 (privileged) やホストのネットワーク、IPC, PID、コンテナの root 動作などを制限しています。

restricted.yaml
# 制限したポリシー
apiVersion: extensions/v1beta1
kind: PodSecurityPolicy
metadata:
  name: restricted
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
spec:
  privileged: false  # privileged は利用させない
  allowPrivilegeEscalation: false # 特権昇格は利用させない
  allowedCapabilities:
  - '*'
  volumes:
  - '*'
  hostNetwork: false # ホストのネットワークは利用させない
  hostPorts:
  - min: 0
    max: 65535
  hostIPC: true # ホスト IPC は利用させない
  hostPID: true # ホスト PID は利用させない
  runAsUser:
    rule: 'MustRunAsNonRoot' # コンテナは root で実行させない
  seLinux:
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'RunAsAny'
  fsGroup:
    rule: 'RunAsAny'

RBAC で PodSecurityPolicy をユーザに紐付ける

ここでは公式ドキュメントの例を元に RBAC でユーザに紐付けをしてみます。まずは紐付けるユーザとして新規の ServiceAccount を用意します。 kubectl の操作には cluster-admin 権限を持つ minikube のデフォルトユーザを利用します。なお cluster-admin 権限はすべての PodSecurityPolicy に対して権限を持っているので、全てのポリシーを利用可能です(いずれかのポリシーで許可されていれば許可される)。

# default ネームスペースに psp-user という ServiceAccount を作成
$ kubectl create serviceaccount psp-user
serviceaccount "psp-user" created

# psp-user にクラスタ全体の edit 権限を与える
$ kubectl create clusterrolebinding psp-user --clusterrole edit --serviceaccount default:psp-user
clusterrolebinding "user-edit" created

下記の 2 つにユーザを使って RBAC を使った PodSecurityPolicy の紐付けを確認してきます。

ユーザ クラスタ権限 PodSecurityPolicy
minikube-user (minikube のデフォルトユーザ) cluster-admin cluster-admin なので全てのポリシーが利用できる
default:psp-user (ServiceAccount) edit 制限したポリシーを紐付ける

公式の例を参考にわかりやすい用に以下のエイリアスを設定しておきます。

$ alias kubectl-admin='kubectl'
$ alias kubectl-user='kubectl --as=system:serviceaccount:default:psp-user'

PodSecurityPolicy の適用

まずは定義した PodSecurityPolicy を適用してみます。

# PodSecurityPolicy 定義前は Pod が作成できない
$ kubectl-admin run nginx --image nginx:1.13 --restart=Never
Error from server (Forbidden): pods "nginx" is forbidden: no providers available to validate pod request

# 制限のない PodSecurityPolicy を設定
$ kubectl-admin apply -f privileged-psp.yaml
podsecuritypolicy "privileged" created

$ kubectl-admin get psp
NAME         DATA      CAPS      SELINUX    RUNASUSER   FSGROUP    SUPGROUP   READONLYROOTFS   VOLUMES
privileged   true      [*]       RunAsAny   RunAsAny    RunAsAny   RunAsAny   false            [*]

cluster-admin 権限を持つユーザは全ての PodSecurityPolicy に対して権限を持つため、制限のない PodSecurityPolicy を設定した時点で Pod を作成できるようになります。一方 edit 権限では PodSecurityPolicy への権限を持っていないため、まだ Pod を作成することはできません。

# cluster-admin 権限では Pod が作れるようになった
$ kubectl-admin run nginx --image nginx:1.13 --restart=Never
pod "nginx" created

# edit 権限では Pod は作れない
$ kubectl-user run nginx2 --image nginx:1.13 --restart=Never
Error from server (Forbidden): pods "nginx2" is forbidden: unable to validate against any pod security policy: []

次に制限を入れた PodSecurityPolicy を設定します。

$ kubectl-admin apply -f restricted-psp.yaml
podsecuritypolicy "restricted" created

$ kubectl-admin get psp
NAME         DATA      CAPS      SELINUX    RUNASUSER          FSGROUP    SUPGROUP   READONLYROOTFS   VOLUMES
privileged   true      [*]       RunAsAny   RunAsAny           RunAsAny   RunAsAny   false            [*]
restricted   false     [*]       RunAsAny   MustRunAsNonRoot   RunAsAny   RunAsAny   false            [*]

制限されたポリシー (restricted) ではコンテナの root 実行が禁止されているため、root 権限で実行する nginx:1.13 は利用できません。しかし、PodSecurityPolicy では許可されるポリシーが優先されるため、cluster-admin 権限を持つユーザは制限の無いポリシー (privileged)を使って引き続き Pod を作成することができます。

# 引き続き Pod を作成できる
$ kubectl-admin run nginx3 --image nginx:1.13 --restart=Never
pod "nginx3" created

# privileged, restricted 両方の権限を持っている
$ kubectl-admin auth can-i use podsecuritypolicy/privileged
yes
$ kubectl-admin auth can-i use podsecuritypolicy/restricted
yes

RBAC による紐付け

次に制限されたポリシーを ServiceAccount default:psp-user に紐付けます。PodSecurityPolicy の紐付けには use という verb を利用します。

# clusterrole の定義。制限されたポリシーの利用 (use) を許可している
$ kubectl-admin create clusterrole psp-restricted \
    --verb=use \
    --resource=podsecuritypolicy \
    --resource-name=restricted
clusterrole "psp-restricted" created

# clusterrolebinding でロールを紐付ける
$ kubectl-admin create clusterrolebinding psp-restricted \
    --clusterrole=psp-restricted \
    --serviceaccount=default:psp-user
clusterrolebinding "psp-restricted" created

# psp-user で権限を確認する
$ kubectl-user auth can-i use podsecuritypolicy/restricted
yes

制限されたポリシーを紐付けた ServiceAccount を使って nginx をデプロイします。nginx はコンテナが root 権限で実行されるためroot の実行を制限されたポリシーによって実行が拒否されます。

# pod 自体は作成できる
$ kubectl-user run nginx4 --image nginx:1.13 --restart=Never
pod "nginx4" created

# しかし実行に失敗する
$ kubectl-user get pods nginx4
NAME      READY     STATUS                       RESTARTS   AGE
nginx4    0/1       CreateContainerConfigError   0          52s

# 失敗の理由を見てみる
$ kubectl-user describe pods nginx4
# 中略
Events:
  Type     Reason                 Age              From               Message
  ----     ------                 ----             ----               -------
  Normal   Scheduled              1m               default-scheduler  Successfully assigned nginx4 to minikube
  Normal   SuccessfulMountVolume  1m               kubelet, minikube  MountVolume.SetUp succeeded for volume "default-token-mshgx"
  Normal   Pulled                 3s (x8 over 1m)  kubelet, minikube  Container image "nginx:1.13" already present on machine
  Warning  Failed                 3s (x8 over 1m)  kubelet, minikube  Error: container has runAsNonRoot and image will run as root
                                                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

次にポリシーを満たす root で動作しないコンテナをデプロイしてみます。ここでは tomaskral/nonroot-nginx という非 root で動作する nginx コンテナを利用しました。

$ kubectl-user run nonroot-nginx --image tomaskral/nonroot-nginx --restart=Never
pod "nonroot-nginx" created

$ kubectl-user get pods nonroot-nginx
NAME            READY     STATUS    RESTARTS   AGE
nonroot-nginx   1/1       Running   0          1m
#                         ^^^^^^^ 動作した

ポリシーを満たしたコンテナがデプロイできることが確認できました :tada:

まとめ

PodSecurityPolicy と RBAC を組み合わせることでユーザごとに異なるセキュリティポリシーが設定できます。複数のポリシーを定義できるため、一部のユーザには特権を与えつつその他のユーザの権限は制限するといったセキュリティ上の設定が可能です。クラスタのマルチテナントを実現する上では必須な機能といえるでしょう。