使用内置 Linux 安全功能保护容器对资源的访问
本文介绍如何保护容器对 Azure Kubernetes 服务 (AKS) 工作负载资源的访问。
概述
与应该向用户或组授予所需最少权限的方式一样,也应将容器限制为只能访问它们所需的操作和进程。 为了尽量减少攻击风险,避免配置需要提升的权限或 root 访问权限的应用程序和容器。
可以使用内置的 Kubernetes Pod 安全上下文来定义更多权限,例如要作为哪个用户或组运行、要公开的 Linux 功能,或在 Pod 清单中设置 allowPrivilegeEscalation: false
。 有关更多最佳做法,请参阅保护 Pod 对资源的访问。
若要更精确地控制容器操作,可以使用内置 Linux 安全功能,例如 AppArmor 和 seccomp。
- 在节点级别定义 Linux 安全功能。
- 通过 pod 清单实现功能。
内置的 Linux 安全功能仅在 Linux 节点和 Pod 上提供。
注意
目前,Kubernetes 环境并不完全安全,因为可能存在恶意的多租户使用情况。 其他安全功能(例如 Microsoft Defender for Containers、AppArmor、seccomp、Pod 安全准入 或用于节点的 Kubernetes RBAC)可有效阻止攻击。
若要在运行恶意多租户工作负荷时获得真正的安全性,应只信任虚拟机监控程序。 Kubernetes 的安全域成为整个群集,而不是单个节点。
对于这些类型的恶意多租户工作负荷,应使用物理隔离的群集。
App Armor
若要限制容器操作,可以使用 AppArmor Linux 内核安全模块。 AppArmor 作为基础 AKS 节点 OS 的一部分提供,默认情况下处于启用状态。 可以创建 AppArmor 配置文件来限制读取、写入或执行等操作或者装载文件系统等系统功能。 默认 AppArmor 配置文件限制对各种 /proc
和 /sys
位置的访问,并提供一种在逻辑上将容器与基础节点隔离的方法。 AppArmor 适用于 Linux 上运行的任何应用程序,而不仅仅是 Kubernetes Pod。
为了通过实际操作了解 AppArmor,以下示例将创建一个阻止写入文件的配置文件。
通过 SSH 连接到 AKS 节点。
创建一个名为 deny-write.profile 的文件。
复制并粘贴以下内容:
#include <tunables/global> profile k8s-apparmor-example-deny-write flags=(attach_disconnected) { #include <abstractions/base> file, # Deny all file writes. deny /** w, }
使用 apparmor_parser
命令添加 AppArmor 配置文件。
将配置文件添加到 AppArmor。
指定在上一步中创建的配置文件的名称:
sudo apparmor_parser deny-write.profile
如果已正确分析配置文件并将其应用于 AppArmor,则不会显示任何输出,并且你将回到命令提示符。
在本地计算机上,创建一个名为 aks-apparmor.yaml 的 Pod 清单。 此清单:
- 为
container.apparmor.security.beta.kubernetes
定义一个注释。 - 引用在前面的步骤中创建的 deny-write 配置文件。
apiVersion: v1 kind: Pod metadata: name: hello-apparmor annotations: container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-deny-write spec: containers: - name: hello image: mcr.azk8s.cn/dotnet/runtime-deps:6.0 command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
- 为
部署 Pod 后,运行以下命令并验证 hello-apparmor Pod 是否显示“正在运行”状态:
kubectl get pods NAME READY STATUS RESTARTS AGE aks-ssh 1/1 Running 0 4m2s hello-apparmor 0/1 Running 0 50s
有关 AppArmor 的详细信息,请参阅 Kubernetes 中的 AppArmor 配置文件。
安全计算 (seccomp)
AppArmor 适用于任何 Linux 应用程序,seccomp(安全计算)则在进程级别运行。 Seccomp 也是一个 Linux 内核安全模块,并由 AKS 节点所用的 containerd
运行时提供本机支持。 借助 seccomp,你可以限制容器的系统调用。 Seccomp 建立了一个额外的保护层来防范恶意参与者利用的常见系统调用漏洞,并使你能够为节点中的所有工作负载指定一个默认的配置文件。
配置默认 seccomp 配置文件(预览)
创建新的 Linux 节点池时,可以使用自定义节点配置来应用默认的 seccomp 配置文件。 AKS 支持两个值:RuntimeDefault
和 Unconfined
。 某些工作负载需要的 syscall 限制可能比其他工作负载要少。 这意味着它们在使用“RuntimeDefault”配置文件的运行时期间可能会失败。 若要缓解此类失败,可以指定 Unconfined
配置文件。 如果工作负载需要自定义配置文件,请参阅配置自定义 seccomp 配置文件。
限制
- SeccompDefault 不是 Windows 节点池支持的参数。
- SeccompDefault 从 2024-09-02-preview API 开始可用。
重要
AKS 预览功能是可选择启用的自助功能。 预览功能是“按现状”和“按可用”提供的,不包括在服务级别协议和有限保证中。 AKS 预览功能是由客户支持尽最大努力部分覆盖。 因此,这些功能并不适合用于生产。 有关详细信息,请参阅以下支持文章:
注册 KubeletDefaultSeccompProfilePreview
功能标志
使用
az feature register
命令注册KubeletDefaultSeccompProfilePreview
功能标志。az feature register --namespace "Microsoft.ContainerService" --name "KubeletDefaultSeccompProfilePreview"
状态显示为“已注册”需要几分钟时间。
使用
az feature show
命令验证注册状态。az feature show --namespace "Microsoft.ContainerService" --name "KubeletDefaultSeccompProfilePreview"
当状态反映为已注册时,使用
az provider register
命令刷新 Microsoft.ContainerService 资源提供程序的注册。az provider register --namespace Microsoft.ContainerService
使用 seccomp 限制容器的系统调用
1.按照步骤,通过指定 "seccompDefualt": "RuntimeDefault"
在 kubelet 配置中应用 seccomp 配置文件。
RuntimeDefault
使用容器的默认 seccomp 配置文件,限制某些系统调用来提高安全性。 受限制的 syscall 将失败。 有关详细信息,请参阅 containerD 默认 seccomp 配置文件。
2.检查是否已应用配置。
可以通过连接到主机并验证是否已在文件系统上进行了配置更改,确认设置已应用到节点。
3.排查工作负载失败情况。
启用 SeccompDefault 后,默认情况下,容器运行时默认 seccomp 配置文件用于节点上计划的所有工作负载。 这可能会导致工作负载由于 syscall 被阻止而失败。 如果工作负载失败,可能会看到如下错误:
- 启用该功能后,工作负载意外存在,出现“权限被拒绝”错误。
- 通过在默认配置文件中将 SCMP_ACT_ERRNO 替换为 SCMP_ACT_LOG,也可以在 auditd 或 syslog 中看到 Seccomp 错误消息。
如果遇到上述错误,建议将 seccomp 配置文件更改为 Unconfined
。 Unconfined
对 syscall 没有限制,允许所有系统调用,这降低了安全性。
配置自定义 seccomp 配置文件
使用自定义 seccomp 配置文件,可以更精细地控制受限制的 syscall。 通过以下方式,遵循授予容器运行所需要的最低权限的最佳做法:
- 定义时采用筛选器,以筛选允许或拒绝的操作。
- 在 pod YAML 清单内批注以与 seccomp 筛选器相关联。
为了通过实际操作了解 seccomp,请创建一个筛选器来阻止更改文件权限。
通过 SSH 连接到 AKS 节点。
创建名为 /var/lib/kubelet/seccomp/prevent-chmod 的 seccomp 筛选器。
复制并粘贴以下内容:
{ "defaultAction": "SCMP_ACT_ALLOW", "syscalls": [ { "name": "chmod", "action": "SCMP_ACT_ERRNO" }, { "name": "fchmodat", "action": "SCMP_ACT_ERRNO" }, { "name": "chmodat", "action": "SCMP_ACT_ERRNO" } ] }
在版本 1.19 及更高版本中,需要配置以下各项:
{ "defaultAction": "SCMP_ACT_ALLOW", "syscalls": [ { "names": ["chmod","fchmodat","chmodat"], "action": "SCMP_ACT_ERRNO" } ] }
在本地计算机上,创建一个名为 aks-seccomp.yaml 的 Pod 清单并粘贴以下内容。 此清单:
- 为
seccomp.security.alpha.kubernetes.io
定义一个注释。 - 引用在上一步中创建的 prevent-chmod 筛选器。
apiVersion: v1 kind: Pod metadata: name: chmod-prevented annotations: seccomp.security.alpha.kubernetes.io/pod: localhost/prevent-chmod spec: containers: - name: chmod image: mcr.azk8s.cn/dotnet/runtime-deps:6.0 command: - "chmod" args: - "777" - /etc/hostname restartPolicy: Never
在版本 1.19 及更高版本中,需要配置以下各项:
apiVersion: v1 kind: Pod metadata: name: chmod-prevented spec: securityContext: seccompProfile: type: Localhost localhostProfile: prevent-chmod containers: - name: chmod image: mcr.azk8s.cn/dotnet/runtime-deps:6.0 command: - "chmod" args: - "777" - /etc/hostname restartPolicy: Never
- 为
使用 kubectl apply 命令部署示例 Pod:
kubectl apply -f ./aks-seccomp.yaml
使用 kubectl get pods 命令查看 Pod 状态。
- 该 Pod 报告一个错误。
- seccomp 筛选器阻止
chmod
命令运行,如示例输出所示:
kubectl get pods NAME READY STATUS RESTARTS AGE chmod-prevented 0/1 Error 0 7s
seccomp 安全配置文件选项
seccomp 安全配置文件是一组定义的被允许或受到限制的 syscall。 大多数容器运行时都有一个默认的 seccomp 配置文件,该文件如果与 Docker 使用的不相同,则与之相似。 有关可用配置文件的详细信息,请参阅 Docker 或 containerD 默认 seccomp 配置文件。
使用自定义节点配置配置 seccomp 时,AKS 使用 RuntimeDefault 的 containerD 默认 seccomp 配置文件。
默认配置文件阻止的重要 syscall
Docker 和 containerD 都维护有安全 syscall 的允许列表。 此表列出了因不在允许列表上而被有效阻止的重要(但并非全部)syscall。 如果工作负载需要任何被阻止的 syscall,请不要使用 RuntimeDefault
seccomp 配置文件。
对 Docker 和 containerD 进行更改时,AKS 会更新其默认配置来进行匹配。 更新此列表可能会导致工作负载失败。 有关发布更新,请参阅 AKS 发行说明。
被阻止的 syscall | 说明 |
---|---|
acct |
可让容器禁用自己的资源限制或进程计帐的计帐 syscall。 也受到 CAP_SYS_PACCT 限制。 |
add_key |
阻止容器使用未加入命名空间的内核 keyring。 |
bpf |
拒绝将潜在的持久性 bpf 程序加载到内核中,已受到 CAP_SYS_ADMIN 限制。 |
clock_adjtime |
时间/日期未加入命名空间。 也受到 CAP_SYS_TIME 限制。 |
clock_settime |
时间/日期未加入命名空间。 也受到 CAP_SYS_TIME 限制。 |
clone |
拒绝克隆新的命名空间。 此外受到 CAP_SYS_ADMIN for CLONE_* 标志限制(CLONE_NEWUSER 除外)。 |
create_module |
拒绝内核模块上的操作和函数。 已过时。 也受到 CAP_SYS_MODULE 限制。 |
delete_module |
拒绝内核模块上的操作和函数。 也受到 CAP_SYS_MODULE 限制。 |
finit_module |
拒绝内核模块上的操作和函数。 也受到 CAP_SYS_MODULE 限制。 |
get_kernel_syms |
拒绝检索导出的内核和模块符号。 已过时。 |
get_mempolicy |
修改内核内存和 NUMA 设置的 syscall。 已受到 CAP_SYS_NICE 限制。 |
init_module |
拒绝内核模块上的操作和函数。 也受到 CAP_SYS_MODULE 限制。 |
ioperm |
防止容器修改内核 I/O 特权级别。 已受到 CAP_SYS_RAWIO 限制。 |
iopl |
防止容器修改内核 I/O 特权级别。 已受到 CAP_SYS_RAWIO 限制。 |
kcmp |
限制进程检查功能,已通过删除 CAP_SYS_PTRACE 来阻止。 |
kexec_file_load |
kexec_load 的同类型 syscall,执行相同的操作,但参数略有不同。 也受到 CAP_SYS_BOOT 限制。 |
kexec_load |
拒绝加载新内核来供稍后执行。 也受到 CAP_SYS_BOOT 限制。 |
keyctl |
阻止容器使用未加入命名空间的内核 keyring。 |
lookup_dcookie |
跟踪/分析 syscall,这可能会泄露主机上的信息。 也受到 CAP_SYS_ADMIN 限制。 |
mbind |
修改内核内存和 NUMA 设置的 syscall。 已受到 CAP_SYS_NICE 限制。 |
mount |
拒绝装载,已受到 CAP_SYS_ADMIN 限制。 |
move_pages |
修改内核内存和 NUMA 设置的 syscall。 |
nfsservctl |
拒绝与内核 nfs 守护程序交互。 自 Linux 3.1 起已过时。 |
open_by_handle_at |
旧容器中断服务的原因。 也受到 CAP_DAC_READ_SEARCH 限制。 |
perf_event_open |
跟踪/分析 syscall,这可能会泄露主机上的信息。 |
personality |
阻止容器启用 BSD 仿真。 本质上并非是危险的,但测试效果不佳,可能存在内核漏洞。 |
pivot_root |
拒绝 pivot_root,应是特权操作。 |
process_vm_readv |
限制进程检查功能,已通过删除 CAP_SYS_PTRACE 来阻止。 |
process_vm_writev |
限制进程检查功能,已通过删除 CAP_SYS_PTRACE 来阻止。 |
ptrace |
跟踪/分析 syscall。 在低于 4.8 的 Linux 内核版本中遭到阻止,以避免绕过 seccomp。 已通过删除 CAP_SYS_PTRACE 来阻止跟踪/分析任意进程,因为它可能会泄露主机上的信息。 |
query_module |
拒绝内核模块上的操作和函数。 已过时。 |
quotactl |
可让容器禁用自己的资源限制或进程计帐的配额 syscall。 也受到 CAP_SYS_ADMIN 限制。 |
reboot |
不让容器重新启动主机。 也受到 CAP_SYS_BOOT 限制。 |
request_key |
阻止容器使用未加入命名空间的内核 keyring。 |
set_mempolicy |
修改内核内存和 NUMA 设置的 syscall。 已受到 CAP_SYS_NICE 限制。 |
setns |
拒绝将线程与命名空间关联。 也受到 CAP_SYS_ADMIN 限制。 |
settimeofday |
时间/日期未加入命名空间。 也受到 CAP_SYS_TIME 限制。 |
stime |
时间/日期未加入命名空间。 也受到 CAP_SYS_TIME 限制。 |
swapon |
拒绝开始/停止交换到文件/设备。 也受到 CAP_SYS_ADMIN 限制。 |
swapoff |
拒绝开始/停止交换到文件/设备。 也受到 CAP_SYS_ADMIN 限制。 |
sysfs |
已过时的 syscall。 |
_sysctl |
已过时,被 /proc/sys 替换。 |
umount |
应为特权操作。 也受到 CAP_SYS_ADMIN 限制。 |
umount2 |
应为特权操作。 也受到 CAP_SYS_ADMIN 限制。 |
unshare |
拒绝为进程克隆新的命名空间。 此外,受到 CAP_SYS_ADMIN 限制,但 unshare --user 除外。 |
uselib |
与共享库相关的较旧的 syscall,长时间未使用。 |
userfaultfd |
用户空间页面错误处理,这在很大程度上是进程迁移所必需的。 |
ustat |
已过时的 syscall。 |
vm86 |
在内核 x86 实模式虚拟机中。 也受到 CAP_SYS_ADMIN 限制。 |
vm86old |
在内核 x86 实模式虚拟机中。 也受到 CAP_SYS_ADMIN 限制。 |
后续步骤
如需相关的最佳做法,请参阅 AKS 中群集安全性和升级的最佳做法和 AKS 中的 Pod 安全的最佳做法。