K8S Security Context
概述
Security Context(安全上下文)用来限制容器对宿主节点的可访问范围,以避免容器非法操作宿主节点的系统级别的内容,使得节点的系统或者节点上其他容器组受到影响。
Security Context可以按照如下几种方式设定:
- 访问权限控制:是否可以访问某个对象(例如文件)是基于 userID(UID)和 groupID(GID) 的
- Security Enhanced Linux (SELinux):为对象分配Security标签
- 以 privileged(特权)模式运行
- Linux Capabilities:为容器组(或容器)分配一部分特权,而不是 root 用户的所有特权
- AppArmor:自 Kubernetes v1.4 以来,一直处于 beta 状态
- Seccomp:过滤容器中进程的系统调用(system call)
- AllowPrivilegeEscalation(允许特权扩大):此项配置是一个布尔值,定义了一个进程是否可以比其父进程获得更多的特权,直接效果是,容器的进程上是否被设置 no_new_privs 标记。当出现如下情况时,AllowPrivilegeEscalation 的值始终为 true:
- 容器以 privileged 模式运行
- 容器拥有 CAP_SYS_ADMIN 的 Linux Capability
如需要了解更多关于 Linux 安全机制方面的信息,请参考 Overview Of Linux Kernel Security Features
本文从以下几个方面介绍如何在 Kubernetes 中配置 Security Context
为Pod设置Security Context
在 Pod 的定义中增加 securityContext
字段,即可为 Pod 指定 Security 相关的设定。 securityContext
字段是一个 PodSecurityContext 对象。通过该字段指定的内容将对该 Pod 中所有的容器生效。
Pod示例
以下面的 Pod 为例:
apiVersion: v1
kind: Pod
metadata:
name: security-context-demo
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
volumes:
- name: sec-ctx-vol
emptyDir: {}
containers:
- name: sec-ctx-demo
image: busybox
command: [ "sh", "-c", "sleep 1h" ]
volumeMounts:
- name: sec-ctx-vol
mountPath: /data/demo
securityContext:
allowPrivilegeEscalation: false
在上面的例子中:
spec.securityContext.runAsUser
字段指定了该 Pod 中所有容器的进程都以UserID 1000 的身份运行
spec.securityContext.runAsGroup
字段指定了该 Pod 中所有容器的进程都以GroupID 3000 的身份运行,如果该字段被省略,容器进程的GroupID为 root(0)
容器中创建的文件,其所有者为 userID 1000,groupID 3000
spec.securityContext.fsGroup
字段指定了该 Pod 的 fsGroup 为 2000
数据卷 (本例中,对应挂载点 /data/demo
的数据卷为 sec-ctx-demo
) 的所有者以及在该数据卷下创建的任何文件,其 GroupID 为 2000
执行Pod示例
-
创建 Pod
kubectl apply -f https://kuboard.cn/statics/learning/sec-ctx/security-context-1.yaml
-
验证 Pod 已运行
kubectl get pod security-context-demo
-
进入容器的命令行界面
kubectl exec -it security-context-demo -- sh
-
在该命令行界面中,查看正在运行的进程
ps
请注意,所有的进程都以 user 1000 的身份运行(由 runAsUser 指定),输出结果如下所示:
PID USER TIME COMMAND 1 1000 0:00 sleep 1h 6 1000 0:00 sh ...
-
在命令行界面中,切换到目录
/data
,并查看目录中的文件列表cd /data ls -l
请注意,
/data/demo
目录的 groupID 为 2000(由 fsGroup 指定),输出结果如下所示:drwxrwsrwx 2 root 2000 4096 Oct 4 05:08 demo
-
在命令行界面中,切换到目录
/data/demo
,并创建一个文件cd /data/demo echo hello > testfile ls -l
请注意,
testfile
的 groupID 为 2000 (由 FSGroup 指定),输出结果如下所示:-rw-r--r-- 1 1000 2000 6 Oct 4 05:09 testfile
-
在命令行界面中执行
id
命令,输出结果如下所示:$ id uid=1000 gid=3000 groups=2000
请注意:
- gid 为 3000,与
runAsGroup
字段所指定的一致 - 如果
runAsGroup
字段被省略,则 gid 取值为 0(即 root),此时容器中的进程将可以操作 root Group 的文件
- gid 为 3000,与
为容器设置Security Context
容器的定义中包含 securityContext
字段,该字段接受 SecurityContext 对象。通过指定该字段,可以为容器设定安全相关的配置,当该字段的配置与 Pod 级别的 securityContext
配置相冲突时,容器级别的配置将覆盖 Pod 级别的配置。容器级别的 securityContext
不影响 Pod 中的数据卷。
下面的示例中的 Pod 包含一个 Container,且 Pod 和 Container 都有定义 securityContext
字段:
apiVersion: v1
kind: Pod
metadata:
name: security-context-demo-2
spec:
securityContext:
runAsUser: 1000
containers:
- name: sec-ctx-demo-2
image: gcr.io/google-samples/node-hello:1.0
securityContext:
runAsUser: 2000
allowPrivilegeEscalation: false
-
执行命令以创建 Pod
kubectl apply -f https://kuboard.cn/statics/learning/sec-ctx/security-context-2.yaml
-
执行命令以验证容器已运行
kubectl get pod security-context-demo-2
-
执行命令进入容器的命令行界面:
kubectl exec -it security-context-demo-2 -- sh
-
在命令行界面中查看所有的进程
ps aux
请注意,容器的进程以 userID 2000 的身份运行。该取值由
spec.containers[*].securityContext.runAsUser
容器组中的字段定义。Pod 中定义的spec.securityContext.runAsUser
取值 1000 被覆盖。输出结果如下所示:PID USER TIME COMMAND 1 2000 0:00 sleep 1h 6 2000 0:00 sh 11 2000 0:00 ps aux ...
为容器设置Linux Capabilities
使用 Linux Capabilities 可以为容器内的进程授予某些特定的权限(而不是 root 用户的所有权限)。在容器定义的 securityContext 中添加 capabilities 字段,可以向容器添加或删除 Linux Capability。
本文后续章节中,先运行一个不包含 capabilities 字段的容器,观察容器内进程的 linux capabilities 位图的情况;然后在运行一个包含 capabilities 字段的容器,比较其 linux capabilities 位图与前者的不同。
无capabilities字段时
我们先确认在没有 capabilities 字段时,容器的行为是怎样的。下面的例子中包含一个容器,我们没有为其添加或删除任何 Linux capability。
apiVersion: v1
kind: Pod
metadata:
name: security-context-demo-3
spec:
containers:
- name: sec-ctx-3
image: gcr.io/google-samples/node-hello:1.0
-
执行命令以创建 Pod
kubectl apply -f https://kuboard.cn/statics/learning/sec-ctx/security-context-3.yaml
-
执行命令以验证容器正在运行
kubectl get pod security-context-demo-3
-
执行命令以进入容器的命令行界面
kubectl exec -it security-context-demo-3 -- sh
-
在容器的命令行界面中查看正在运行的进程
ps aux
输出结果中展示了容器中进程的 process ID(PID),如下所示:
PID USER TIME COMMAND 1 root 0:00 sleep 1h 6 root 0:00 sh 11 root 0:00 ps aux
-
在容器的命令行界面中查看 process 1 的状态
cd /proc/1 cat status
输出结果中展示了该进程 Linux Capabilities 的位图,如下所示:
... CapPrm: 00000000a80425fb CapEff: 00000000a80425fb ...
有capabilities字段时
接下来,我们运行同样的一个容器,不同的是,这次为其设置了 capabilities 字段。下面是 yaml 配置文件,该配置中为进程添加了两个 Linux Capability: CAP_NET_ADMIN 和 CAP_SYS_TIME:
apiVersion: v1
kind: Pod
metadata:
name: security-context-demo-4
spec:
containers:
- name: sec-ctx-4
image: gcr.io/google-samples/node-hello:1.0
securityContext:
capabilities:
add: ["NET_ADMIN", "SYS_TIME"]
-
执行命令以创建 Pod
kubectl apply -f https://kuboard.cn/statics/learning/sec-ctx/security-context-4.yaml
-
执行命令以验证容器正在运行
kubectl get pod security-context-demo-4
-
执行命令以进入容器的命令行界面
kubectl exec -it security-context-demo-4 -- sh
-
在容器的命令行界面中查看正在运行的进程
ps aux
输出结果中展示了容器中进程的 process ID(PID),如下所示:
PID USER TIME COMMAND 1 root 0:00 sleep 1h 6 root 0:00 sh 11 root 0:00 ps aux
-
在容器的命令行界面中查看 process 1 的状态
cd /proc/1 cat status
输出结果中展示了该进程 Linux Capabilities 的位图,如下所示:
... CapPrm: 00000000aa0435fb CapEff: 00000000aa0435fb ...
-
比较两次运行,进程的 Linux Capabilities 位图的差异:
第一次运行:00000000a80425fb 第二次运行:00000000aa0435fb
-
第一次运行时,位图的 12 位和 25 为是 0。第二次运行时,12 位和 25 位是 1.查看 Linux Capabilities 的常量定义文件 capability.h 可知:12 位代表
CAP_NET_ADMIN
,25 位代表CAP_SYS_TIME
。
Linux Capability 常量
Linux Capabilities 常量格式为CAP_XXX
。然而,在容器定义中添加或删除 Linux Capabilities 时,必须去除常量的前缀CAP_
。例如:向容器中添加CAP_SYS_TIME
时,只需要填写SYS_TIME
。
为容器设置SELinux标签
Pod 或容器定义的 securityContext
中 seLinuxOptions
字段是一个 SELinuxOptions 对象,该字段可用于为容器指定 SELinux 标签。如下所示:
...
securityContext:
seLinuxOptions:
level: "s0:c123,c456"
为容器指定 SELinux 标签时,宿主节点的 SELinux 模块必须加载。
关于数据卷
Pod 的 securityContext 作用于 Pod 中所有的容器,同时对 Pod 的数据卷也同样生效。具体来说,fsGroup
和 seLinuxOptions
将被按照如下方式应用到 Pod 中的数据卷:
fsGroup
:对于支持 ownership 管理的数据卷,通过fsGroup
指定的 GID 将被设置为该数据卷的 owner,并且可被fsGroup
写入。更多细节请参考 Ownership Management design documentseLinuxOptions
:对于支持 SELinux 标签的数据卷,将按照seLinuxOptions
的设定重新打标签,以使 Pod 可以访问数据卷内容。通常您只需要设置seLinuxOptions
中level
这一部分内容。该设定为 Pod 中所有容器及数据卷设置 Multi-Category Security (MCS) 标签。