Kubernetes での Pod のセキュリティ対策についてまとめる。
ここでは次の3点について書く。
securityContext
で Pod をセキュアにする方法SecurityContextDeny
でsecurityContext
を設定させない方法PodSecurityPolicy
を用いてクラスタ側で Pod をセキュアにする方法
いずれも securityContext / security policy による AttackSurface の制御方法について書いている。
securityContext
Pod の Manifest で securityContext を指定することで、その Pod の Attack Surface を制御できる。
詳しくはドキュメントを見れば良いので割愛するが、「privileged なコンテナを許容するか」「どのユーザーがコンテナでプロセスを動かすか」「root ファイルシステムを read only とするか」といった設定が可能。
securityContext を適用する
次のような Pod を作成する。
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
terminationGracePeriodSeconds: 0
securityContext:
runAsNonRoot: true # root での動作を禁止
$ kubectl apply -f nginx.yaml
pod/nginx/created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 0/1 CreateContainerConfigError 0 3s
$ kubectl describe pods nginx
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 12s default-scheduler Successfully assigned default/nginx to k8s-node-1
Normal Pulled 9s (x3 over 11s) kubelet, k8s-node-1 Container image "nginx:alpine" already present on machine
Warning Failed 9s (x3 over 11s) kubelet, k8s-node-1 Error: container has runAsNonRoot and image will run as root
nginx:alpine は root で動作するため、ポリシー違反で Pod の作成に失敗する。
SecurityContextDeny
SecurityContextDeny をとは Admission Controller Plugin の1つで、これを設定することで Pod の securityContext を設定できなくなる。
ただし、securityContext の中でも runAsUser や FsGroup などのみに対して有効である。
詳しくは https://github.com/kubernetes/kubernetes/blob/master/plugin/pkg/admission/securitycontext/scdeny/admission.go を参照。
SecurityContextDeny を有効化する
デフォルトで有効になっていない場合もあるので有効化されているか確認する。
有効化されているかの確認は kubeapi-server の引数に enable-admission-plugins=SecurityContextDeny
が含まれているかを確認する。
$ cat /etc/kubernetes/manifests/kube-apiserver.yaml
...
spec:
containers:
- command:
- kube-apiserver
- --advertise-address=172.16.20.100
...
- --enable-admission-plugins=NodeRestriction,SecurityContextDeny # SecurityContextDeny を追加
...
securityContext が設定できないことを確認する
SecurityContextDeny が有効でない場合、 runAsUser: 0
は適用され、root なプロセスを作ることができる。
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginxinc/nginx-unprivileged
securityContext:
runAsUser: 0
$ kubectl apply -f nginx.yaml
pod/nginx created
$ kubectl exec -it nginx sh
# id
uid=0(root) gid=0(root) groups=0(root)
次に SecurityContextDeny を適用した状態で試す。
$ kubectl apply -f nginx.yaml
Error from server (Forbidden): error when creating "nginx.yaml": pods "nginx" is forbidden: SecurityContext.RunAsUser is forbidden
このように Pod が作成できないことが確認できる。
PodSecurityPolicy
PodSecurityPolicy とはクラスタから Pod の Attack Surface を制御するリソースである。
securityContext では Pod で制御したが、こちらはクラスタ側から制御する方法。
PodSecurityPolicy を有効にする
デフォルトで有効になっていない場合もあるので有効化されているか確認する。
有効化されているかの確認は kubeapi-server の引数に enable-admission-plugins=PodSecurityPolicy
が含まれているかを確認する。
これにより Admission Controller で PodSecurityPolicy プラグインが有効化される。
$ cat /etc/kubernetes/manifests/kube-apiserver.yaml
...
spec:
containers:
- command:
- kube-apiserver
- --advertise-address=172.16.20.100
...
- --enable-admission-plugins=NodeRestriction,PodSecurityPolicy # PodSecurityPolicy を追加
...
PodSecurityPolicy を適用する
次のような PodSecurityPolicy を作る。
apiVersion: extensions/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted
annotations:
apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default'
seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default'
seccomp.security.alpha.kubernetes.io/defaultProfileName: 'docker/default'
spec:
privileged: false
allowPrivilegeEscalation: false
allowedCapabilities: []
volumes:
- '*'
hostNetwork: false
hostPorts:
- min: 0
max: 65535
hostIPC: true
hostPID: true
runAsUser:
rule: 'MustRunAsNonRoot'
seLinux:
rule: 'RunAsAny'
supplementalGroups:
rule: 'RunAsAny'
fsGroup:
rule: 'RunAsAny'
runAsUser
が MustRunAsNonRoot
となっているため root でプロセスを動かすことができない。
次にこの PodSecurityPolicy を ServiceAccount に紐づける。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: member
rules:
- apiGroups: ["policy"]
resourceNames: ["restricted"]
resources: ["podsecuritypolicies"]
verbs: ["use"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: member
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: member
subjects:
- apiGroup: ""
kind: ServiceAccount
name: mrtc0
namespace: "default"
この状態で pod を作成してみる。
$ kubectl run test --image nginx:latest --restart=Never
pod/test created
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
test 0/1 CreateContainerConfigError 0 18s
$ kubectl describe pod test
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 34s default-scheduler Successfully assigned default/test to k8s-node-1
Normal Pulled 17s (x2 over 20s) kubelet, k8s-node-1 Successfully pulled image "nginx:latest"
Warning Failed 17s (x2 over 20s) kubelet, k8s-node-1 Error: container has runAsNonRoot and image will run as root
nginx:latest
は root でプロセスが動くため、ポリシー違反で Pod 作成に失敗する。
では non root なプロセスで動かすようにしてみる。
$ kubectl run test --image nginxinc/nginx-unprivileged:latest --restart=Never
pod/test created
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
test 1/1 Running 0 56s
今度は Pod が作成できた。