この記事は 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 を利用するには下記の設定が必要になります。
- Admisson Control で
PodSecurityPolicy
を有効にする - PodSecurityPolicy を定義する
- RBAC で PodSecurityPolicy をユーザに紐付ける
Admisson Control で PodSecurityPolicy を有効にする
API サーバーの --admission-control
引数に PodSecurityPolicy
を追加することで、PodSecurityPolicy の Admission Control を有効にすることができます。
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 を利用しない状態と同等の制限の無いポリシーを定義してみます。以下の設定は公式ドキュメントの例 に記載されているものになります。
# 制限がないポリシー
# 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 動作などを制限しています。
# 制限したポリシー
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
# ^^^^^^^ 動作した
ポリシーを満たしたコンテナがデプロイできることが確認できました
まとめ
PodSecurityPolicy と RBAC を組み合わせることでユーザごとに異なるセキュリティポリシーが設定できます。複数のポリシーを定義できるため、一部のユーザには特権を与えつつその他のユーザの権限は制限するといったセキュリティ上の設定が可能です。クラスタのマルチテナントを実現する上では必須な機能といえるでしょう。