验证kubernetes的pod安全策略

最近有客户提出需要对业务集群的pod做安全限制,不允许使用pod拥有privileged的权限,研究一番,刚好k8s的Pod Security Policies可以实现该需求。

什么是pod安全策略

Pod Security Policy(简称psp)是集群级别的资源,该资源控制pod的spec中安全相关的方面,具体的方面参考下表:

Control Aspect Field Names
Running of privileged containers privileged
Usage of host namespaces hostPID, hostIPC
Usage of host networking and ports hostNetwork, hostPorts
Usage of volume types volumes
Usage of the host filesystem allowedHostPaths
White list of Flexvolume drivers allowedFlexVolumes
Allocating an FSGroup that owns the pod’s volumes fsGroup
Requiring the use of a read only root file system readOnlyRootFilesystem
The user and group IDs of the container runAsUser, runAsGroup, supplementalGroups
The SELinux context of the container seLinux

如何开启Pod Security Policy

  • Enable API extensions

For Kubernetes < 1.6.0, the API Server must enable the extensions/v1beta1/podsecuritypolicy API extensions group (–runtime-config=extensions/v1beta1/podsecuritypolicy=true).

  • Enable PodSecurityPolicy admission control policy

The following parameter needs to be added to the API server startup argument: –admission-control=PodSecurityPolicy

默认psp是不开启的,若要开启,需要配置上述apiserver启动参数,默认的apiserver的yaml文件为/etc/kubernetes/manifests/kube-apiserver.yaml

...
spec:
  containers:
  - command:
    - kube-apiserver
    - --authorization-mode=Node,RBAC
    - --enable-admission-plugins=Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,ResourceQuota,PodSecurityPolicy
    - --runtime-config=extensions/v1beta1/podsecuritypolicy=true
    ...

配置默认的psp

由于已有打开了pod创建的安全策略,但此时还未创建任何的policy,所以任何pod都无法被创建,此时如果尝试去创建一个pod,会发现pod无法进行调度,

[root@build-master apiserver]# kubectl run --image=nginx yxli
deployment.apps/yxli created
[root@build-master apiserver]# kubectl get deploy yxli
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
yxli      1         0         0            0           4m

查看controller的日志会发现报错forbidden: no providers available to validate pod request

[root@build-master ~]# kubectl logs --tail=10 -f kube-controller-manager-build-master -n kube-system
E0318 03:01:17.321184       1 replica_set.go:450] Sync "default/yxli-6978dddc9d" failed with pods "yxli-6978dddc9d-" is forbidden: no providers available to validate pod request
I0318 03:01:17.321302       1 event.go:221] Event(v1.ObjectReference{Kind:"ReplicaSet", Namespace:"default", Name:"yxli-6978dddc9d", UID:"0d11050f-492a-11e9-8ef0-00163e004fd2", APIVersion:"apps/v1", ResourceVersion:"764927", FieldPath:""}): type: 'Warning' reason: 'FailedCreate' Error creating: pods "yxli-6978dddc9d-" is forbidden: no providers available to validate pod request
E0318 03:01:37.807060       1 replica_set.go:450] Sync "default/yxli-6978dddc9d" failed with pods "yxli-6978dddc9d-" is forbidden: no providers available to validate pod request
I0318 03:01:37.807046       1 event.go:221] Event(v1.ObjectReference{Kind:"ReplicaSet", Namespace:"default", Name:"yxli-6978dddc9d", UID:"0d11050f-492a-11e9-8ef0-00163e004fd2", APIVersion:"apps/v1", ResourceVersion:"764927", FieldPath:""}): type: 'Warning' reason: 'FailedCreate' Error creating: pods "yxli-6978dddc9d-" is forbidden: no providers available to validate pod request
E0318 03:02:18.773092       1 replica_set.go:450] Sync "default/yxli-6978dddc9d" failed with pods "yxli-6978dddc9d-" is forbidden: no providers available to validate pod request

而由于我们修改了apiserver的参数,pod受kubelet管理,自动触发了重建,此时apiserver的pod也是因为缺少权限没法创建出来

[root@build-master apiserver]# journalctl -fu kubelet
-- Logs begin at Tue 2019-03-12 20:33:59 CST. --
Mar 18 11:25:29 build-master kubelet[4013]: E0318 11:25:29.016416    4013 kubelet.go:1594] Failed creating a mirror pod for "kube-apiserver-build-master_kube-system(77a7d13c654e7233306d4e2948aaaa78)": pods "kube-apiserver-build-master" is forbidden: no providers available to validate pod request
Mar 18 11:25:30 build-master kubelet[4013]: W0318 11:25:30.113587    4013 kubelet.go:1579] Deleting mirror pod "kube-apiserver-build-master_kube-system(e48d84cd-46f0-11e9-8ef0-00163e004fd2)" because it is outdated
Mar 18 11:25:30 build-master kubelet[4013]: E0318 11:25:30.117242    4013 kubelet.go:1594] Failed creating a mirror pod for "kube-apiserver-build-master_kube-system(77a7d13c654e7233306d4e2948aaaa78)": pods "kube-apiserver-build-master" is forbidden: no providers available to validate pod request

所以我们需要创建一个policy,为我们需要的受kubelet管理的静态pod以及kube-system命名空间下的pod提供权限,否则pod一旦发生重建,都将因为缺少权限导致无法正常创建出来。

首先新建文件 privileged.policy.yaml,权限不受限制,

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: privileged
spec:
  allowedCapabilities:
  - '*'
  allowPrivilegeEscalation: true
  fsGroup:
    rule: 'RunAsAny'
  hostIPC: true
  hostNetwork: true
  hostPID: true
  hostPorts:
  - min: 0
    max: 65535
  privileged: true
  readOnlyRootFilesystem: false
  runAsUser:
    rule: 'RunAsAny'
  seLinux:
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'RunAsAny'
  volumes:
  - '*'

创建并查看该policy

[root@build-master apiserver]# kubectl create -f privileged.policy.yaml
podsecuritypolicy.policy/privileged created
[root@build-master apiserver]# kubectl get psp
NAME         PRIV      CAPS      SELINUX    RUNASUSER   FSGROUP    SUPGROUP   READONLYROOTFS   VOLUMES
privileged   true      *         RunAsAny   RunAsAny    RunAsAny   RunAsAny   false            *

然后还需要创建一个clusterrole,并且赋予该role对上述psp对使用权

# Cluster role which grants access to the privileged pod security policy
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: privileged-psp
rules:
- apiGroups:
  - policy
  resourceNames:
  - privileged
  resources:
  - podsecuritypolicies
  verbs:
  - use

然后把clusterrole赋予serviceaccount或者对应的user、group,针对kube-system命名空间下的pod来说,都使用了kube-system下的serviceaccount或者由kubelet管理,kubelet是使用system:nodes这个组来管理的pod,所以只需要做如下binding即可为kube-system下的所有pod提供权限,

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: kube-system-psp
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: privileged-psp
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:nodes
  namespace: kube-system
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:serviceaccounts:kube-system

都创建好之后,查看kube-system下的pod,发现apiserver已经创建成功

[root@build-master apiserver]# kubectl get po -n kube-system
NAME                                   READY     STATUS    RESTARTS   AGE
coredns-68f5b48ccb-9hjvr               1/1       Running   0          5d
coredns-68f5b48ccb-qrjnr               1/1       Running   0          5d
etcd-build-master                      1/1       Running   0          5d
kube-apiserver-build-master            1/1       Running   0          18s

上面的binding只是为kube-system空间赋予了权限,若想要为别的命名空间赋予权限,可以使用ClusterRoleBinding的方式为多个namespace绑定privileged-psp的clusterrole,或者像如下方式,为所有合法的serviceaccounts和user绑定权限,当然前提是pod使用了namespace下的serviceAccount

# Authorize all service accounts in a namespace:
- kind: Group
  apiGroup: rbac.authorization.k8s.io
  name: system:serviceaccounts
# Or equivalently, all authenticated users in a namespace:
- kind: Group
  apiGroup: rbac.authorization.k8s.io
  name: system:authenticated

验证psp的privileged限制

该章节会创建一个名为psp-namespace的namespace做测试,来验证psp中对privileged的pod的限制

[root@build-master apiserver]# kubectl create namespace psp-example
namespace/psp-example created
[root@build-master apiserver]# kubectl create serviceaccount fake-user -n psp-example
serviceaccount/fake-user created
[root@build-master apiserver]# kubectl run --image=nginx psp-pod-nginx-1 -n psp-example --serviceaccount=fake-user
deployment.apps/psp-pod-nginx-1 created
[root@build-master apiserver]# kubectl get po -n psp-example
No resources found.
[root@build-master apiserver]# kubectl get deploy -n psp-example
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
psp-pod-nginx-1   1         0         0            0           23s

如上所示,新的psp-example命名空间由于没有任何权限,所以无法创建新pod,接下来我们创建一个policy并给psp-example做authorize,但是policy中会限制无法创建privileged的pod

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: example
spec:
  privileged: false  # Don't allow privileged pods!
  # The rest fills in some required fields.
  seLinux:
    rule: RunAsAny
  supplementalGroups:
    rule: RunAsAny
  runAsUser:
    rule: RunAsAny
  fsGroup:
    rule: RunAsAny
  volumes:
  - '*'

然后创建role和rolebinding,并验证fake-user是否有权限使用example的policy

[root@build-master psp-example]# kubectl -n psp-example create role psp:unprivileged --verb=use --resource=podsecuritypolicy --resource-name=example
role.rbac.authorization.k8s.io/psp:unprivileged created
[root@build-master psp-example]# kubectl -n psp-example create rolebinding fake-user:psp:unprivileged --role=psp:unprivileged --serviceaccount=psp-example:fake-user
rolebinding.rbac.authorization.k8s.io/fake-user:psp:unprivileged created
[root@build-master psp-example]#  kubectl --as=system:serviceaccount:psp-example:fake-user -n psp-example auth can-i use podsecuritypolicy/example
yes

等待片刻,再次查看刚才创建的deploy,发现已经调度成功

[root@build-master psp-example]# kubectl get deploy -n psp-example psp-pod-nginx-1
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
psp-pod-nginx-1   1         1         1            1           22m

此时我们尝试在psp-example下创建一个privileged的特权容器,

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    run: psp-pod-nginx-privileged
  name: psp-pod-nginx-privileged
  namespace: psp-example
spec:
  selector:
    matchLabels:
      run: psp-pod-nginx-privileged
  template:
    metadata:
      labels:
        run: psp-pod-nginx-privileged
    spec:
      containers:
      - image: nginx
        imagePullPolicy: Always
        name: psp-pod-nginx-privileged
      securityContext:
        privileged: true
      serviceAccount: fake-user

创建并查看结果

[root@build-master psp-example]# kubectl create -f privileged-pod.yaml
deployment.extensions/psp-pod-nginx-privileged created
[root@build-master psp-example]# kubectl get deploy -n psp-example psp-pod-nginx-privileged
psp-pod-nginx-privileged   1         0         0            0           24s
[root@build-master ~]# kubectl logs --tail=1 -f kube-controller-manager-build-master -n kube-system
I0318 04:32:42.777200       1 event.go:221] Event(v1.ObjectReference{Kind:"ReplicaSet", Namespace:"psp-example", Name:"psp-pod-nginx-privileged-d7f8dbd75", UID:"c66fb10f-4936-11e9-9c58-00163e004fd2", APIVersion:"apps/v1", ResourceVersion:"773292", FieldPath:""}): type: 'Warning' reason: 'FailedCreate' Error creating: pods "psp-pod-nginx-privileged-d7f8dbd75-" is forbidden: unable to validate against any pod security policy: [spec.containers[0].securityContext.privileged: Invalid value: true: Privileged containers are not allowed]

查看controller-manager的日志,发现Privileged containers are not allowed