6.6 Pod的安全策略配置
为了更精细的控制Pod对资源的使用方式,kubernetes 从1.4 版本引入了PodSecurityPolicy资源对象对Pod的安全策略进行管理。并在 1.10 版本升级了Beta 版,到 1.14 时趋于成熟。
6.6.1 PodSecurityPolicy 的工作机制。
若想启用 PodSecurityPolicy机制,则需要在 Kube-apiserver 服务的启动参数 --enable-admission-plugins
中进行设置
--enable-admission-plugins=PodSecurityPolicy
在开启PodSecurityPolicy准入控制器后,Kubernetes 默认不允许创建任何Pod,需要创建PodSecurityPolicy 策略和相应的RBAC授权策略(Authorizing Policies),Pod 才能创建成功。
如:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
使用 kubectl 命令创建时,系统将提示 "禁止创建" 的报错信息:
[root@master k8s]## kubectl create -f pods.yaml
Error from server (Forbidden): error when creating "pods.yaml": pods "nginx" is forbidden: PodSecurityPolicy: no providers available to validate pod request
接下来,创建一个PodSecurityPolicy ,配置文件psp-non-provileged.yaml
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: psp-non-privileged
spec:
privileged: false
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
runAsUser:
rule: RunAsAny
fsGroup:
rule: RunAsAny
volumes:
- '*'
使用kubectl create 命令创建该PodSecurityPolicy:
[root@master k8s]## kubectl create -f psp-non-privileged.yaml
podsecuritypolicy.policy/psp-non-privileged created
查看PodSecurityPolicy
[root@master k8s]## kubectl get psp
NAME PRIV CAPS SELINUX RUNASUSER FSGROUP SUPGROUP READONLYROOTFS VOLUMES
psp-non-privileged false RunAsAny RunAsAny RunAsAny RunAsAny false *
[root@master k8s]##
再次创建Pod 就能成功:
[root@master k8s]## kubectl create -f pods.yaml
pod/nginx created
[root@master k8s]##
上面的PodSecurityPolicy 'psp-non-privileged.yaml' 设置了 privelieged: false ,表示 不允许创建特权模式的Pod ,在下面的yaml 配置文件pod-privileged.yaml 中为Pod 设置了特权模式:
apiVersion: v1
kind: Pod
metadata:
name: nginx-priveliged
spec:
containers:
- name: nginx
image: nginx
securityContext:
privileged: true
创建Pod 时,系统将提示 : "禁止创建特权模式的Pod" 报错信息:
[root@master k8s]## kubectl create -f nginx-privileged.yaml
Error from server (Forbidden): error when creating "nginx-privileged.yaml": pods "nginx-priveliged" is forbidden: PodSecurityPolicy: unable to admit pod: [spec.containers[0].securityContext.privileged: Invalid value: true: Privileged containers are not allowed]
6.6.2PodSecurityPolicy 配置详解
在PodSecurityPolicy 对象中,可以设置下列字段来控制Pod运行时的各种安全策略。
1. 特权模式相关
privileged: 是否允许Pod 以特权模式运行。
2. 宿主机资源相关配置
(1) hostPID: 是否允许Pod共享宿主机的进程空间。
(2) hostIPC: 是否允许Pod 共享宿主机的IPC命令空间。
(3) hostNetwork: 是否允许 Pod 使用宿主机网络的命名空间。
(4) hostPorts: 是否允许Pod 使用宿主机的端口号,可以通过 hostPortRange 字段设置允许使用的端口范围,以[min,max]设置最小端口号和最大端口号。
(5) Volumes: 允许Pod 使用的存储卷Volume 类型,设置为"*"
表示允许使用任意类型,建议至少允许Pod 使用下列的Volume 类型。
- configMap
- downwardAPI
- emptyDir
- persistentVolumeClaim
- secret
- projected
(6) AllowedHostPaths: 允许Pod使用宿主机的hostPath 路径名称,可以通过pathPrefix 字段设置路径的前缀,并可以设置是否为只读属性,例子如下:
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: allow-hostpath-volumes
spec:
volumes:
- hostPath
allowedHostPaths:
- pathPrefix: "/foo"
readOnly: true
结果为允许Pod访问宿主机上以 "/foo"
为前缀的路径,包括 "/foo"
,"/foo/"
,"foo/bar"
等,但不能访问"/fool"
,"/etc/foo"
等路径,也不允许通过 "/foo/../"
表达式访问 /foo 的上层目录。
(7) FSGroup: 设置允许说某些Volume的GroupID范围,可以将规则(rule字段)设置为 MustRunAs、MayRunAs、RunAsAny。
- MustRunAs:需要设置GroupID 的范围,例如 1 ~ 65535,要求Pod的securityContext.fsGroup设置的值必须属于该GroupID的范围。
- MayRunAs:需要设置 GroupID的范围,例如 1 ~ 65535,不强制要求Pod设置securityContext.fsGroup。
- RunAsAny:不限制 GroupID的范围,任何Group都可以访问Volume。
(8) ReadOnlyFilesystem: 要求容器运行的根文件系统(root filesystem) 必须是只读的。
(9) allowFlexVolumes:对于类型为flexVolume的存储卷,设置允许使用的驱动类型,例子如下:
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: allow-flex-volumes
spec:
volumes:
- flexVolume
allowedFlexVolumes:
- driver: example/lvm
- driver: example/cifs
3. 用户和组相关配置
(1) RunAsUser: 设置运行容器的用户ID(User ID)范围,规则字段(rule)的值可以补设置为MustRunAs、MustRunAsNonRoot或RunAsAny。
- MustRunAs:需要设置User ID 的范围,要求Pod的securityContext.runAsUser设置的值必须属于该User ID的范围。
- MustRunAsNonRoot:必须以非 root 用户运行容器,要求Pod的securityContext.runAsUser设置一个非 0 的用户ID,或者镜像中的 USER 字段设置了用户ID,建议同时设置 allowPrivilegeEscalation=false ,以避免不必要的提升权限操作。
- RunAsAny:不限制 User ID的范围,任何User 都可以运行。
(2) RunAsGroup:设置运行容器的 Group ID范围,规则字段的值可以被设置为 MustRunAs、MustRunAsNonRoot或RunAsAny。
- MustRunAs:需要设置Group ID 的范围,要求Pod的securityContext.runAsGroup设置的值必须属于该Group ID的范围。
- MayRunAs: 不要求设置 RunAsGroup。 不过,如果指定了 RunAsGroup 被设置,所设置值必须处于所定义的范围内
- RunAsAny:不限制 Group ID的范围,任何Group的用户都可以运行。
(3) SupplementalGroups: 设置窗口可以额外添加的 Group ID 范围,可以将规则(rule字段) 设置为 MustRunAs 、MayRunAs或 RunAsAny。
- MustRunAs: 需要设置 Group ID的范围,要求Pod的 securityContext.supplementalGroups 设置的值必须属于该 Group ID范围。
- MayRunAs:需要设置 Group ID 的范围,不强制要求Pod设置 securityContext.supplementalGroups 。
- RunAsAny:不限制 Group ID的范围,任何 supplementalGroups 的用户都可以运行。
4.提升权限相关设置
(1) AllowPrivilegeEscalation: 设置容器内的子进程是否可以提升权限,通常在设置非root用户(MustRunasNonRoot)时进行设置。
(2) DefaultAllowPrivilegeEsalation: 设置AllowPrivilegeEscalation 的默认值,设置为disallow时,管理员还可以显式设置AllowPrivilegeEscalation来指定是否允许提升权限
5. Linux能力相关配置
(1) AllowCapabilities:设置容器可以使用的Linux能力列表,设置"*"
表示允许使用Linux的所有能力(如 NET_ADMIN、SYS_TIME等)。
(2) RequiredDropCapabilities: 设置不允许容器使用的Linux能力列表。
(3) DefaultAddCapabilities:设置默认为容器添加的Linux能力列表,例如 SYS_TIME等,Docker 建议默认设置的Linux能力列表查看 https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
6. SELinux 相关配置
seLinux : 设置 SELinux 参数,可以将规则字段 (rule) 的值设置为 MustRunAs 或 RunAsAny。
- MustRunAs:要求设置seLinuxOptions ,系统将对Pod的seurityContext.seLinuxOptions 设置的值进行校验。
- RunAsAny: 不限制seLinuxOptions的设置。
7. 其他Linux相关配置
(1) AllowedProcMountTypes: 设置允许的ProcMountTypes类型列表,可以设置allowedProcMountTypes或DefaultProcMount。
(2) AppArmor: 设置对容器可执行程序的访问控制权限,详情请参考 https://kubernetes.io/docs/tutorials/security/apparmor/#podsecuritypolicyannotations
(3) Seccomp: 设置允许容器使用的系统调用(System Calls)的profile。
(4) Sysctl: 设置允许调整的内核参数,详情参考 https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/#podsecuritypolicy
下面列举常用的PodSecurityPolicy安全策略配置
例1: 基本没有限制的安全策略,允许创建任意安全设置的Pod。
apiVersion: policy/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'
例2:要求Pod运行用户为非特权用户;禁止提升权限;不允许使用宿主机网络、端口号、IPC等资源;限制可以使用的Volume类型,等等。
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default'
apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
seccomp.security.alpha.kubernetes.io/defaultProfileName: 'docker/default'
apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default'
spec:
privileged: false
allowPrivilegeEscalation: false
requiredDropCapabilities:
- ALL
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
- 'persistentVolumeClaim'
hostNetwork: false
hostIPC: false
hostPID: false
runAsUser:
rule: 'MustRunAsNonRoot'
seLinux:
rule: 'RunAsAny'
supplementalGroups:
rule: 'MustRunAs'
ranges:
- min: 1
max: 65535
fsGroup:
rule: 'MustRunAs'
ranges:
- min: 1
max: 65535
readOnlyRootFilesystem: false
此外,Kubernetes 建议使用RBAC 授权机制来设置针对Pod安全策略的授权,通常应该对Pod的SevicesAccount进行授权。
例如: 可以创建如下ClusterRole(也可以创建Role) ,并将其设置为允许使用PodSecurityPolicy:
kind: ClousterRole
apiVersio: rbac.authorization.k8s.io/v1
metadata:
name: <role name>
rules:
- apiGroups: ['policy']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames:
- <list of policies to authorize> #允许使用的PodSecurityPolicy列表
然后创建一个ClusterRoleBinding与用户和ServiceAccount进行绑定
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: <binding name>
roleRef:
kind: ClusterRole
name: <role name> #之前创建的ClusterRole
apiGroup: rbac.authorization.k8s.io
subjects:
#对特定Namespace中的ServiceAccount进行授权
- kind: ServiceAccount
name: <authorized pod namespace> ## ServiceAccount的名称
namespace: <authorized pod namespace> ## Namespace的名称
#对特定用户授权(不推荐)
- kind: User
apiGroup: rbac.authorization.k8s.io
name: <authorized user name> ## 用户名
也可以创建RoleBinding对与该RoleBinding相同的Namespace中的Pod进行授权,通常可以与某个系统级别的Group关联配置,例如:
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: <binding name>
namespace: <binding namespace> ## 访RoleBinding所属的Namespace
roleRef:
kind: Role
name: <role name>
apiGroup: rbac.authorization.k8s.io
subjects:
#授权该Namespace 中的全部ServiceAccount
- kind: Group
apiGroup: rbac.authorization.k8s.io
name: system:serviceaccounts
- kind: Group
apiGroup: rbac.authorization.k8s.io
name: system:authenticated
6.6.3 Pod的安全设置详解
在系统管理员对Kubernetes集群中设置了PodSecurityPolicy策略之后 ,系统将对Pod和Container级别的安全设置进行校验,对于不满足PodSecurityPolicy安全策略的Pod,系统将拒绝创建。
Pod和容器的安全策略可以在Pod或Container的securityContext字段中进行设置,如果在Pod和Container级别都设置相同的安全类型字段,容器将使用Container级别的设置。
在Pod级别可以设置的安全策略类型如下:
- runAsUser:容器内运行程序的用户ID。
- runAsGroup:容器内运行程序的用户组ID。
- runAsNonRoot: 是否必须以非root用户运行运行程序。
- fsGroup:设置允许说某些Volume的GroupID范围。
- seLinuxOptions:SELinux 相关设置。
- supplementalGroups:允许容器使用的其他用户组ID。
- sysctls:设置允许调整的内核参数。
在Container级别可以设置的安全策略类型如下:
- runAsUser:容器内运行程序的用户ID。
- runAsGroup:容器内运行程序的用户组ID。
- runAsNonRoot: 是否必须以非root用户运行运行程序。
- privileged:是否以特权模式运行。
- allowPrivilegeEscalation:是否允许提升权限。
- readOnlyRootFilesystem:根文件系统是否为只读属性。
- capabilities:Linux能力列表。
- seLinuxOptions:SELinux 相关设置。
下面通过几个例子对Pod的安全设置进行说明:
例1:Pod级别的安全设置,作用于该Pod内的全部容器。
apiVersion: v1
kind: Pod
metadata:
name: security-context-demo
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
volumes:
- name: sec-ctx-demo
emptyDir: {}
containers:
- name: sec-ctx-demo
image: gcr.io/google-samples/node-hello:1.0
volumeMounts:
- name: sec-ctx-demo
mountPath: /data/demo
securityContext:
allowPrivilegeEscalation: false
TIPS
如果上面的镜像无法下载,可以使用busybox 镜像测试
containers:
- name: sec-ctx-demo
image: busybox
command:
- tail
- -f
- /dev/null
在spec.securityContext中设置了如下参数:
- runAsUser=1000 : 所有容器都将以 UserID 1000运行程序,所有新生成文件的UserID也被设置为1000
- runAsGroup=1000 : 所有容器都将以 GroupID 3000运行程序,所有新生成文件的GroupID也被设置为3000
- fsGroup=2000: 挂载卷 "/data/demo" 及其中创建的文件都将属于GroupID 2000。
创建该Pod之后 ,进行容器环境,查看到运行进程用户的ID为 1000 :
/ $ ps aux
PID USER TIME COMMAND
1 1000 0:00 tail -f /dev/null
11 1000 0:00 sh
16 1000 0:00 ps aux
查看从Volume 挂载到容器的/data/demo 目录,其GroupID为 2000:
/ $ ls -l /data
total 0
drwxrwsrwx 2 root 2000 6 Mar 22 10:17 demo
在该目录下创建一个新文件,可见其用户ID 为1000,组ID 为2000 :
/ $ cd /data/demo/
/data/demo $ ls
/data/demo $ touch hello
/data/demo $ ls -l
total 0
-rw-r--r-- 1 1000 2000 0 Mar 22 10:20 hello
例2:Container级别的安全设置,作用于特定的容器。
apiVersion: v1
kind: Pod
metadata:
name: security-context-demo-2
spec:
securityContext:
runAsUser: 1000
containers:
- name: sec-ctx-demo-2
image: busybox
command:
- tail
- -f
- /dev/null
securityContext:
runAsUser: 2000
allowPrivilegeEscalation: false
创建该Pod之后 ,进入容器环境,查看到运行进程的用户ID 为2000:
/ $ ps aux
PID USER TIME COMMAND
1 2000 0:00 tail -f /dev/null
6 2000 0:00 sh
11 2000 0:00 ps aux
例3 :为Container设置可用的Linux能力,为容器设置允许使用Linux能力包括NET_ADMIN
和SYS_TIME
apiVersion: v1
kind: Pod
metadata:
name: security-context-demo-3
spec:
containers:
- name: sec-ctx-3
image: busybox
command:
- ping
- 127.0.0.1
securityContext:
capabilities:
add: ['NET_ADMIN','SYS_TIME']
创建该Pod容器之后进入容器环境,查看1号进程的Linux 能力设置:
~ ## cd /proc/1
/proc/1 ## cat status | grep Cap
CapInh: 00000000aa0435fb
CapPrm: 00000000aa0435fb
CapEff: 00000000aa0435fb
CapBnd: 00000000aa0435fb
CapAmb: 0000000000000000
未设置这两个能力的配置为:
/ $ cd /proc/1
/proc/1 $ cat status | grep Cap
CapInh: 00000000a80425fb
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 00000000a80425fb
CapAmb: 0000000000000000
可以看到系统在第12位和第25位添加了CAP_NET_ADMIN和CAP_SYS_TIME这两个Linux 能力。
补充知识:
使用capsh 去解码,更多知识查看 Linux Capabilities 简介
[root@master ~]## capsh --decode=00000000aa0435fb
0x00000000aa0435fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_admin,cap_net_raw,cap_sys_chroot,cap_sys_time,cap_mknod,cap_audit_write,cap_setfcap