为 Pod 或容器配置安全上下文
安全上下文定义了 Pod 或容器的权限和访问控制设置。安全上下文设置包括但不限于
自由裁量访问控制:访问对象(如文件)的权限基于 用户 ID (UID) 和组 ID (GID)。
安全增强型 Linux (SELinux):对象被分配安全标签。
以特权或非特权方式运行。
Linux 功能:赋予进程某些权限,但不是 root 用户的所有权限。
AppArmor:使用程序配置文件来限制单个程序的功能。
Seccomp:过滤进程的系统调用。
allowPrivilegeEscalation
:控制进程是否可以获得比其父进程更多的权限。此布尔值直接控制是否在容器进程上设置no_new_privs
标志。当容器- 以特权方式运行,或
- 具有
CAP_SYS_ADMIN
readOnlyRootFilesystem
:将容器的根文件系统安装为只读。
以上要点不是安全上下文设置的完整集合 - 请参阅 SecurityContext 以获取完整列表。
开始之前
您需要拥有一个 Kubernetes 集群,并且 kubectl 命令行工具必须配置为与您的集群通信。建议在至少有两个节点(不充当控制平面主机)的集群上运行本教程。如果您还没有集群,可以使用 minikube 创建一个,或者您可以使用以下 Kubernetes 游乐场之一
要检查版本,请输入kubectl version
。设置 Pod 的安全上下文
要为 Pod 指定安全设置,请在 Pod 规范中包含 securityContext
字段。securityContext
字段是一个 PodSecurityContext 对象。您为 Pod 指定的安全设置适用于 Pod 中的所有容器。以下是一个具有 securityContext
和 emptyDir
卷的 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:1.28
command: [ "sh", "-c", "sleep 1h" ]
volumeMounts:
- name: sec-ctx-vol
mountPath: /data/demo
securityContext:
allowPrivilegeEscalation: false
在配置文件中,runAsUser
字段指定对于 Pod 中的任何容器,所有进程都以用户 ID 1000 运行。runAsGroup
字段指定 Pod 中任何容器内的所有进程的主组 ID 为 3000。如果省略此字段,容器的主组 ID 将为 root(0)。当指定 runAsGroup
时,创建的任何文件也将由用户 1000 和组 3000 拥有。由于指定了 fsGroup
字段,容器的所有进程也属于补充组 ID 2000。卷 /data/demo
以及在该卷中创建的任何文件的拥有者将是组 ID 2000。
创建 Pod
kubectl apply -f https://k8s.io/examples/pods/security/security-context.yaml
验证 Pod 的容器是否正在运行
kubectl get pod security-context-demo
获取到正在运行的容器的 shell
kubectl exec -it security-context-demo -- sh
在您的 shell 中,列出正在运行的进程
ps
输出显示进程以用户 1000 运行,这是 runAsUser
的值
PID USER TIME COMMAND
1 1000 0:00 sleep 1h
6 1000 0:00 sh
...
在您的 shell 中,导航到 /data
,并列出一个目录
cd /data
ls -l
输出显示 /data/demo
目录的组 ID 为 2000,这是 fsGroup
的值。
drwxrwsrwx 2 root 2000 4096 Jun 6 20:08 demo
在您的 shell 中,导航到 /data/demo
,并创建一个文件
cd demo
echo hello > testfile
列出 /data/demo
目录中的文件
ls -l
输出显示 testfile
的组 ID 为 2000,这是 fsGroup
的值。
-rw-r--r-- 1 1000 2000 6 Jun 6 20:08 testfile
运行以下命令
id
输出类似于此
uid=1000 gid=3000 groups=2000
从输出中,您可以看到 gid
为 3000,与 runAsGroup
字段相同。如果省略 runAsGroup
,gid
将保持为 0(root),并且进程将能够与由 root(0) 组拥有的文件以及对 root (0) 组具有所需组权限的组进行交互。
退出您的 shell
exit
配置 Pod 的卷权限和所有权更改策略
Kubernetes v1.23 [稳定]
默认情况下,Kubernetes 会递归地更改每个卷内容的所有权和权限,以匹配 Pod 的 securityContext
中指定的 fsGroup
,当该卷被挂载时。对于大型卷,检查和更改所有权和权限可能需要很长时间,从而减慢 Pod 启动速度。您可以使用 securityContext
内部的 fsGroupChangePolicy
字段来控制 Kubernetes 检查和管理卷的所有权和权限的方式。
fsGroupChangePolicy - fsGroupChangePolicy
定义了在卷被暴露在 Pod 内之前更改其所有权和权限的行为。此字段仅适用于支持 fsGroup
控制的所有权和权限的卷类型。此字段有两个可能的值
- OnRootMismatch:仅当根目录的权限和所有权与卷的预期权限不匹配时才更改权限和所有权。这有助于缩短更改卷的所有权和权限所需的时间。
- Always:在挂载卷时始终更改卷的权限和所有权。
例如
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
fsGroupChangePolicy: "OnRootMismatch"
将卷权限和所有权更改委托给 CSI 驱动程序
Kubernetes v1.26 [稳定]
如果您部署了支持 VOLUME_MOUNT_GROUP
NodeServiceCapability
的 容器存储接口 (CSI) 驱动程序,则基于 securityContext
中指定的 fsGroup
设置文件所有权和权限的过程将由 CSI 驱动程序执行,而不是 Kubernetes。在这种情况下,由于 Kubernetes 不执行任何所有权和权限更改,因此 fsGroupChangePolicy
不会生效,并且如 CSI 所指定,驱动程序预计会使用提供的 fsGroup
挂载卷,从而导致一个可由 fsGroup
读取/写入的卷。
设置容器的安全上下文
要为容器指定安全设置,请在容器清单中包含 securityContext
字段。securityContext
字段是一个 SecurityContext 对象。您为容器指定的安全设置仅适用于单个容器,并且在存在重叠时会覆盖在 Pod 级别进行的设置。容器设置不会影响 Pod 的卷。
以下是一个具有一个容器的 Pod 的配置文件。Pod 和容器都具有 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/hello-app:2.0
securityContext:
runAsUser: 2000
allowPrivilegeEscalation: false
创建 Pod
kubectl apply -f https://k8s.io/examples/pods/security/security-context-2.yaml
验证 Pod 的容器是否正在运行
kubectl get pod security-context-demo-2
获取到正在运行的容器的 shell
kubectl exec -it security-context-demo-2 -- sh
在您的 shell 中,列出正在运行的进程
ps aux
输出显示进程以用户 2000 运行。这是为容器指定的 runAsUser
的值。它覆盖了为 Pod 指定的值 1000。
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
2000 1 0.0 0.0 4336 764 ? Ss 20:36 0:00 /bin/sh -c node server.js
2000 8 0.1 0.5 772124 22604 ? Sl 20:36 0:00 node server.js
...
退出您的 shell
exit
为容器设置功能
使用 Linux 功能,您可以向进程授予某些权限,而无需授予 root 用户的所有权限。要为容器添加或删除 Linux 功能,请在容器清单的 securityContext
部分中包含 capabilities
字段。
首先,请查看不包含 capabilities
字段时会发生什么。以下配置文件没有添加或删除任何容器功能
apiVersion: v1
kind: Pod
metadata:
name: security-context-demo-3
spec:
containers:
- name: sec-ctx-3
image: gcr.io/google-samples/hello-app:2.0
创建 Pod
kubectl apply -f https://k8s.io/examples/pods/security/security-context-3.yaml
验证 Pod 的容器是否正在运行
kubectl get pod security-context-demo-3
获取到正在运行的容器的 shell
kubectl exec -it security-context-demo-3 -- sh
在您的 shell 中,列出正在运行的进程
ps aux
输出显示容器的进程 ID (PID)
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 4336 796 ? Ss 18:17 0:00 /bin/sh -c node server.js
root 5 0.1 0.5 772124 22700 ? Sl 18:17 0:00 node server.js
在您的 shell 中,查看进程 1 的状态
cd /proc/1
cat status
输出显示进程的功能位图
...
CapPrm: 00000000a80425fb
CapEff: 00000000a80425fb
...
记下功能位图,然后退出 shell。
exit
接下来,运行一个与前一个容器相同的容器,只是它设置了额外的功能。
以下是一个运行一个容器的 Pod 的配置文件。该配置添加了 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/hello-app:2.0
securityContext:
capabilities:
add: ["NET_ADMIN", "SYS_TIME"]
创建 Pod
kubectl apply -f https://k8s.io/examples/pods/security/security-context-4.yaml
获取到正在运行的容器的 shell
kubectl exec -it security-context-demo-4 -- sh
在您的 shell 中,查看进程 1 的功能。
cd /proc/1
cat status
输出显示了该进程的功能位图。
...
CapPrm: 00000000aa0435fb
CapEff: 00000000aa0435fb
...
比较两个容器的功能。
00000000a80425fb
00000000aa0435fb
在第一个容器的功能位图中,位 12 和 25 是清除的。在第二个容器中,位 12 和 25 是设置的。位 12 是 CAP_NET_ADMIN
,位 25 是 CAP_SYS_TIME
。有关功能常量的定义,请参见 capability.h。
注意
Linux 功能常量采用CAP_XXX
的形式。但是,当您在容器清单中列出功能时,必须省略常量的 CAP_
部分。例如,要添加 CAP_SYS_TIME
,请在您的功能列表中包含 SYS_TIME
。为容器设置 Seccomp 配置文件
要为容器设置 Seccomp 配置文件,请在 Pod 或容器清单的 securityContext
部分中包含 seccompProfile
字段。seccompProfile
字段是一个 SeccompProfile 对象,包含 type
和 localhostProfile
。type
的有效选项包括 RuntimeDefault
、Unconfined
和 Localhost
。localhostProfile
仅在 type: Localhost
时才必须设置。它指示节点上预配置配置文件的路径,相对于 kubelet 配置的 Seccomp 配置文件位置(使用 --root-dir
标志配置)。
以下示例将 Seccomp 配置文件设置为节点的容器运行时默认配置文件。
...
securityContext:
seccompProfile:
type: RuntimeDefault
以下示例将 Seccomp 配置文件设置为 <kubelet-root-dir>/seccomp/my-profiles/profile-allow.json
中的预配置文件。
...
securityContext:
seccompProfile:
type: Localhost
localhostProfile: my-profiles/profile-allow.json
将 SELinux 标签分配给容器
要将 SELinux 标签分配给容器,请在 Pod 或容器清单的 securityContext
部分中包含 seLinuxOptions
字段。seLinuxOptions
字段是一个 SELinuxOptions 对象。以下示例应用 SELinux 级别。
...
securityContext:
seLinuxOptions:
level: "s0:c123,c456"
注意
要分配 SELinux 标签,必须在主机操作系统上加载 SELinux 安全模块。高效的 SELinux 卷重标记
Kubernetes v1.28 [beta]
注意
Kubernetes v1.27 引入了这种行为的早期有限形式,该形式仅适用于使用 ReadWriteOncePod
访问模式的卷(和持久卷声明)。
作为 alpha 功能,您可以启用 SELinuxMount
功能网关 以将性能改进扩展到其他类型的持久卷声明,如下文详细解释。
默认情况下,容器运行时会递归地将 SELinux 标签分配给所有 Pod 卷上的所有文件。为了加快此过程,Kubernetes 可以通过使用挂载选项 -o context=<label>
来立即更改卷的 SELinux 标签。
要从这种加速中获益,必须满足以下所有条件。
- 功能网关
ReadWriteOncePod
和SELinuxMountReadWriteOncePod
必须启用。 - Pod 必须使用具有适用
accessModes
和 功能网关 的持久卷声明。- 卷具有
accessModes: ["ReadWriteOncePod"]
,并且启用了功能网关SELinuxMountReadWriteOncePod
。 - 或者,卷可以使用任何其他访问模式,并且必须启用两个功能网关
SELinuxMountReadWriteOncePod
和SELinuxMount
。
- 卷具有
- Pod(或使用持久卷声明的所有容器)必须设置
seLinuxOptions
。 - 相应的持久卷必须是以下之一:
- 使用传统 in-tree
iscsi
、rbd
或fc
卷类型的卷。 - 或者,使用 CSI 驱动程序的卷。CSI 驱动程序必须通过在其 CSIDriver 实例中设置
spec.seLinuxMount: true
来宣布它支持使用-o context
进行挂载。
- 使用传统 in-tree
对于任何其他卷类型,SELinux 重标记会以另一种方式发生:容器运行时会递归地更改卷中所有 inode(文件和目录)的 SELinux 标签。卷中的文件和目录越多,重标记所需的时间就越长。
管理对 /proc
文件系统的访问
Kubernetes v1.12 [alpha]
对于遵循 OCI 运行时规范的运行时,容器默认以一种模式运行,其中有多个路径被屏蔽且只读。这样做的结果是,容器在其容器的挂载命名空间内存在这些路径,并且它们的功能类似于容器是隔离的主机,但容器进程无法写入它们。屏蔽和只读路径的列表如下所示。
屏蔽的路径
/proc/asound
/proc/acpi
/proc/kcore
/proc/keys
/proc/latency_stats
/proc/timer_list
/proc/timer_stats
/proc/sched_debug
/proc/scsi
/sys/firmware
只读路径
/proc/bus
/proc/fs
/proc/irq
/proc/sys
/proc/sysrq-trigger
对于某些 Pod,您可能希望绕过对路径的默认屏蔽。想要这样做最常见的场景是,如果您尝试在 Kubernetes 容器(在 Pod 内)中运行容器。
securityContext
字段 procMount
允许用户请求容器的 /proc
被 Unmasked
,或者被容器进程以读写方式挂载。这也适用于不在 /proc
中的 /sys/firmware
。
...
securityContext:
procMount: Unmasked
注意
将procMount
设置为 Unmasked 需要 pod 规范中的 spec.hostUsers
值为 false
。换句话说:希望具有 Unmasked /proc
或未屏蔽 /sys
的容器也必须位于 用户命名空间 中。Kubernetes v1.12 到 v1.29 没有强制执行该要求。讨论
Pod 的安全上下文适用于 Pod 的容器,并在适用时也适用于 Pod 的卷。具体来说,fsGroup
和 seLinuxOptions
如下应用于卷。
fsGroup
:支持所有权管理的卷将被修改,以便由fsGroup
中指定的 GID 拥有并可写。有关更多详细信息,请参见 所有权管理设计文档。seLinuxOptions
:支持 SELinux 标记的卷将被重新标记,以便可以通过seLinuxOptions
下指定的标签访问。通常,您只需要设置level
部分。这将设置分配给 Pod 中所有容器以及卷的 多类别安全 (MCS) 标签。
警告
在为 Pod 指定 MCS 标签后,所有具有相同标签的 Pod 都可以访问该卷。如果您需要 Pod 间保护,则必须为每个 Pod 分配唯一的 MCS 标签。清理
删除 Pod。
kubectl delete pod security-context-demo
kubectl delete pod security-context-demo-2
kubectl delete pod security-context-demo-3
kubectl delete pod security-context-demo-4
下一步
- PodSecurityContext
- SecurityContext
- CRI 插件配置指南
- 安全上下文设计文档
- 所有权管理设计文档
- PodSecurity 准入
- AllowPrivilegeEscalation 设计文档
- 有关 Linux 中安全机制的更多信息,请参见 Linux 内核安全功能概述(注意:某些信息已过时)。
- 阅读有关 用户命名空间 的信息,了解 Linux Pod。
- OCI 运行时规范中的屏蔽路径