Compartir a través de

使用内置 Linux 安全功能保护对 Azure Kubernetes 服务(AKS)工作负荷资源的容器访问

本文介绍如何使用 用户命名空间AppArmorseccomp 内置 Linux 安全功能保护对 Azure Kubernetes 服务(AKS)工作负荷资源的容器访问。

容器访问安全性概述

与应该向用户或组授予所需最少权限的方式一样,也应将容器限制为只能访问它们所需的操作和进程。 为了尽量减少攻击风险,避免配置需要提升的权限或 root 访问权限的应用程序和容器。

可以使用内置的 Kubernetes Pod 安全上下文来定义更多权限,例如要作为哪个用户或组运行、要公开的 Linux 功能,或在 Pod 清单中设置 allowPrivilegeEscalation: false 有关更多最佳做法,请参阅保护 Pod 对资源的访问

若要改进主机隔离并减少 Linux 上的横向移动,可以使用 用户命名空间。 若要更精确地控制容器操作,可以使用内置 Linux 安全功能,例如 AppArmor 和 seccomp。 这些功能帮助你通过在节点级别定义 Linux 安全特性,并通过 Pod 规范实施这些特性,来限制容器可执行的操作。

内置的 Linux 安全功能仅在 Linux 节点和 Pod 上提供。

注意

目前,Kubernetes 环境并不安全,因为可能存在恶意多租户使用情况。 其他安全功能,例如 Microsoft Defender for ContainersAppArmorseccomp用户命名空间Pod 安全允许Kubernetes Role-Based 访问控制(RBAC)用于节点,有效阻止攻击。

若要在运行恶意多租户工作负荷时获得真正的安全性,应只信任虚拟机监控程序。 Kubernetes 的安全域成为整个群集,而不是单个节点。

对于这些类型的恶意多租户工作负荷,应使用物理隔离的群集。

用户命名空间的先决条件

  • 现有的 AKS 群集。 如果没有群集,请使用 Azure CLIAzure PowerShellAzure 门户创建群集。
  • 控制平面和工作节点的最低 Kubernetes 版本 1.33。 如果不使用 Kubernetes 版本 1.33 或更高版本,则需要 升级 Kubernetes 版本
  • 运行 Azure Linux 3.0 或 Ubuntu 24.04 的工作器节点,以确保满足启用用户命名空间的最低 堆栈要求 。 如果需要升级作系统(OS)版本,请参阅 升级 OS 版本

用户命名空间的限制

用户命名空间概述

默认情况下,Linux Pod 使用多个命名空间运行:用于隔离网络标识的网络命名空间和用于隔离进程的 PID 命名空间。 用户命名空间(user_namespace)将容器中的用户与主机上的用户隔离开来。 它还会限制功能的范围和 Pod 与系统的其余部分的交互。

容器内的 UID 和 GID 映射到主机上的未特权用户,因此与主机其余部分的所有交互都发生在那些不受特权的 UID 和 GID 中。 例如,容器(UID 0)内的根可以映射到主机上的用户 65536。 Kubernetes 会创建映射,以确保它不会与系统上使用用户命名空间(user namespaces)的其他 Pod 发生重叠。

Kubernetes 实现具有一些关键优势。 若要了解详细信息,请参阅 Kubernetes 用户命名空间文档

启用用户命名空间

  1. 创建名为 mypod.yaml 的文件,并将其复制到以下清单中。 若要使用用户命名空间,YAML 需要具有字段 hostUsers: false

    apiVersion: v1
    kind: Pod
    metadata:
      name: userns
    spec:
      hostUsers: false
      containers:
      - name: shell
        command: ["sleep", "infinity"]
        image: debian
    
  2. 使用 kubectl apply 命令部署应用程序,并指定 YAML 清单的名称。

    kubectl apply -f mypod.yaml
    
  3. 使用 kubectl get pods 命令查看已部署的 Pod 的状态。

    kubectl get pods
    
  4. 使用 kubectl exec 命令将 Exec 执行到 Pod 中。

    kubectl exec -ti userns -- bash
    
  5. 在 pod 内,使用以下命令检查 /proc/self/uid_map

    cat /proc/self/uid_map
    

    输出在最后一列中应有 65536 。 例如:

    0  833617920      65536
    

    此输出表明容器(UID 0)中的根用户映射到主机上的用户 65536。

用户命名空间缓解的常见漏洞和暴露(CVE)

下表概述了使用user_namespaces部分或完全缓解的一些常见漏洞和风险:

CVE 严重性分数 严重级别
CVE-2019-5736 8.6 High
CVE 2024-21262 8.6 High
CVE 2022-0492 7.8 High
CVE-2021-25741 8.1 / 8.8 高/高
CVE-2017-1002101 9.6 / 8.8 关键/高

请记住,此列表并不详尽。 若要了解详细信息,请参阅 Kubernetes v1.33:默认启用的用户命名空间

AppArmor 先决条件

注意

自 2025 年 11 月 7 日 VHD 版本起,Azure Linux 3.0 支持 AppArmor。

AppArmor 概述

若要限制容器操作,可以使用 AppArmor Linux 内核安全模块。 AppArmor 作为基础 AKS 节点作系统(OS)的一部分提供,默认情况下处于启用状态。 可以创建 AppArmor 配置文件,以限制读取、写入或执行操作,或系统功能(如装载文件系统)。 默认 AppArmor 配置文件限制对各种 /proc/sys 位置的访问,并提供一种在逻辑上将容器与基础节点隔离的方法。 AppArmor 适用于 Linux 上运行的任何应用程序,而不仅仅是 Kubernetes Pod。

注意

在 Kubernetes v1.30 之前,AppArmor 是通过注释指定的。 从 v1.30 开始,AppArmor 是通过 Pod 规范中的 securityContext 字段指定的。 有关详细信息,请参阅 Kubernetes AppArmor 文档

AKS 群集中用来限制容器操作的 AppArmor 配置文件

使用 AppArmor 保护 Pod

可以在 Pod 或容器级别指定 AppArmor 配置文件。 容器 AppArmor 配置文件优先于 Pod AppArmor 配置文件。 如果未指定这两个条件,容器将无约束地运行。 有关 AppArmor 配置文件的详细信息,请参阅 通过 AppArmor 保护 Pod 的 Kubernetes 文档

配置自定义 AppArmor 配置文件

以下示例创建一个自定义配置文件,该配置文件阻止从容器内部向文件写入。

  1. 通过 SSH 连接到 AKS 节点。

  2. 创建名为 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,
    }
    
  3. 将 AppArmor 配置文件加载到节点上。

    # This example assumes that node names match host names, and are reachable via SSH.
    NODES=($( kubectl get node -o jsonpath='{.items[*].status.addresses[?(.type == "Hostname")].address}' ))
    
    for NODE in ${NODES[*]}; do ssh $NODE 'sudo apparmor_parser -q <<EOF
    #include <tunables/global>
    
    profile k8s-apparmor-example-deny-write flags=(attach_disconnected) {
      #include <abstractions/base>
    
      file,
    
      # Deny all file writes.
      deny /** w,
    }
    EOF'
    done
    

使用自定义 AppArmor 配置文件部署 Pod

  1. 使用拒绝写入配置部署 “Hello AppArmor” Pod。

    apiVersion: v1
    kind: Pod
    metadata:
      name: hello-apparmor
    spec:
      securityContext:
        appArmorProfile:
          type: Localhost
          localhostProfile: k8s-apparmor-example-deny-write
      containers:
      - name: hello
        image: busybox:1.28
        command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]
    
  2. 使用 kubectl apply 命令应用 Pod 配置。

    kubectl apply -f hello-apparmor.yaml
    
  3. 在 Pod 中执行并验证容器是否使用 AppArmor 配置文件运行。

    kubectl exec hello-apparmor -- cat /proc/1/attr/current
    

    输出应显示正在使用的 AppArmor 配置文件。 例如:

    k8s-apparmor-example-deny-write (enforce)
    

Seccomp 先决条件

注册 KubeletDefaultSeccompProfilePreview 功能标志

重要

AKS 预览功能是可选择启用的自助功能。 预览功能是“按现状”和“按可用”提供的,不包括在服务级别协议和有限保证中。 AKS 预览功能是由客户支持尽最大努力部分覆盖。 因此,这些功能并不适合用于生产。 有关详细信息,请参阅以下支持文章:

  1. 使用 KubeletDefaultSeccompProfilePreview 命令注册 az feature register 功能标志。

    az feature register --namespace "Microsoft.ContainerService" --name "KubeletDefaultSeccompProfilePreview"
    

    状态显示为“已注册”需要几分钟时间

  2. 使用 az feature show 命令验证注册状态。

    az feature show --namespace "Microsoft.ContainerService" --name "KubeletDefaultSeccompProfilePreview"
    
  3. 当状态反映为已注册时,使用 命令刷新 az provider register 资源提供程序的注册。

    az provider register --namespace Microsoft.ContainerService
    

Seccomp 限制

  • AKS 仅支持默认 seccomp 配置文件(RuntimeDefaultUnconfined)。 不支持自定义 seccomp 配置文件。
  • SeccompDefault 不是 Windows 节点池支持的参数。

默认 seccomp 配置文件概述(预览版)

虽然 AppArmor 适用于任何 Linux 程序,但 seccomp(或安全计算)在进程级工作。 Seccomp 也是 Linux 内核安全模块。 containerd AKS 节点使用的运行时为 seccomp 提供本机支持。 借助 seccomp,你可以限制容器的系统调用。 Seccomp 建立了一个额外的保护层来防范恶意参与者利用的常见系统调用漏洞,并使你能够为节点中的所有工作负载指定一个默认的配置文件。

创建新的 Linux 节点池时,可以使用自定义节点配置来应用默认的 seccomp 配置文件。 AKS 支持 RuntimeDefaultUnconfined 值。 某些工作负载需要的 syscall 限制可能比其他工作负载要少。 这意味着在运行时使用 RuntimeDefault 配置文件时,它们可能会失败。 若要缓解此类失败,可以指定 Unconfined 配置文件。 如果工作负载需要自定义配置文件,请参阅配置自定义 seccomp 配置文件

使用 seccomp 限制容器系统调用

  1. 按照步骤通过在 kubelet 配置中指定来应用 seccomp 配置文件"seccompDefault": "RuntimeDefault"
  2. 连接到主机
  3. 验证配置是否已应用于节点。

使用 seccomp 解决工作负荷故障

启用SeccompDefault后,节点上计划的所有工作负荷都将使用容器运行时的默认 seccomp 配置文件,这可能会导致工作负荷因被阻止的系统调用而失败。 如果发生负载失败,您可能会看到如下错误:

  • 启用该功能后,工作负荷会意外退出,并出现“权限被拒绝”错误。
  • 通过在默认配置文件中将 SCMP_ACT_ERRNO 替换为 SCMP_ACT_LOG,也可以在 auditd 或 syslog 中看到 Seccomp 错误消息。

如果遇到这些错误,建议将 seccomp 配置文件更改为 UnconfinedUnconfined 对 syscalls 没有限制,允许执行所有系统调用。

自定义 seccomp 配置文件概述

使用自定义 seccomp 配置文件,您可以更细致地控制容器中的受限系统调用。 可以通过以下方式创建自己的 seccomp 配置文件:

  • 使用筛选器指定要允许或拒绝的动作。
  • 在 pod YAML 清单内批注以与 seccomp 筛选器相关联。

注意

有关对 seccomp 配置文件进行故障排除的帮助,请参阅 Azure Kubernetes 服务(AKS)中的 seccomp 配置文件配置疑难解答

配置自定义 seccomp 配置文件

为了通过实际操作了解 seccomp,请创建一个筛选器来阻止更改文件权限。

  1. 通过 SSH 连接到 AKS 节点。

  2. 创建名为 /var/lib/kubelet/seccomp/prevent-chmod 的 seccomp 筛选器。

  3. 复制并粘贴以下内容:

    {
      "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"
        }
      ]
    }
    
  4. 在本地计算机上,创建一个名为 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
    
  5. 使用 kubectl apply 以下命令部署示例 Pod:

    kubectl apply -f ./aks-seccomp.yaml
    
  6. 使用 kubectl get pods 命令查看 Pod 状态。

    kubectl get pods
    

    在输出中,您应该会看到 Pod 报告错误。 seccomp 筛选器阻止 chmod 命令运行,如示例输出所示:

    NAME                      READY     STATUS    RESTARTS   AGE
    chmod-prevented           0/1       Error     0          7s
    

seccomp 安全配置文件选项

seccomp 安全配置文件是一组定义的被允许或受到限制的 syscall。 大多数容器运行时都有一个默认的 seccomp 配置文件,与 Docker 使用的配置文件相似,如果不是相同的话。 有关可用配置文件的详细信息,请参阅 Dockercontainerd 的默认 seccomp 配置文件。

AKS 使用containerd默认的 seccomp 配置文件,当您使用RuntimeDefault配置 seccomp 时。

默认配置文件阻止的重要 syscall

Dockercontainerd 维护安全的 syscalls 允许列表。 对 Docker容器进行更改时,AKS 会更新默认配置以匹配。 此列表的更新可能会导致工作负载崩溃。 有关发布更新,请参阅 AKS 发行说明

以下表格列出了由于不在允许列表中而被阻止的重要系统调用。 此列表并不详尽。 如果您的工作负载需要被阻止的任何 syscalls,请不要使用 RuntimeDefault seccomp 配置文件。

被阻止的 syscall 说明
acct 记账系统调用,这可以使容器禁用其自身的资源限制或进程记账。 也受到 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 配额系统调用,允许容器禁用其自身的资源限制或进程会计。 也受到 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 群集,请参阅以下文章: