本文发表已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已变得不正确。

使用新的 v1.22 alpha 功能为所有工作负载启用 seccomp

这篇博文是关于 v1.22 中引入的 Kubernetes 新功能,它在现有的 seccomp 支持之上添加了一个额外的安全层。Seccomp 是一种 Linux 进程安全机制,用于根据一组定义的规则过滤系统调用 (syscall)。将 seccomp 配置文件应用于容器化工作负载是增强应用程序部署安全性的关键任务之一。开发人员、站点可靠性工程师和基础设施管理员必须携手合作,在应用程序的整个生命周期中创建、分发和维护配置文件。

您可以使用 Pod 及其容器的 securityContext 字段来调整工作负载的安全相关配置。Kubernetes 在此 SecurityContext 中引入了专用的 seccomp 相关 API 字段,并随着 seccomp 在 v1.19.0 中正式发布。此增强功能允许更轻松地指定整个 Pod 或特定容器是否应以以下方式运行:

  • Unconfined:不启用 seccomp
  • RuntimeDefault:将使用容器运行时的默认配置文件
  • Localhost:将应用节点本地配置文件,该文件通过 kubelet seccomp 配置文件根目录(<kubelet-root-dir>/seccomp)的相对路径引用

随着 seccomp 的正式发布,从整体安全角度来看,没有任何变化,因为 Unconfined 仍然是默认值。如果您从 Kubernetes 版本的升级路径和向后兼容性的角度考虑,这完全没问题。但这也意味着,工作负载更有可能在完全没有 seccomp 的情况下运行,这应该从长远来看得到修复。

SeccompDefault 来救援

Kubernetes v1.22.0 引入了一个新的 kubelet 功能门 SeccompDefault,它像所有其他新功能一样以 alpha 状态添加。这意味着默认情况下它处于禁用状态,可以为每个 Kubernetes 节点手动启用。

此功能有什么作用?好吧,它只是将默认 seccomp 配置文件从 Unconfined 更改为 RuntimeDefault。如果未在 Pod 清单中另行指定,则该功能将通过使用容器运行时的默认配置文件来添加更高的一组安全约束。这些配置文件可能因运行时而异,例如 CRI-Ocontainerd。它们也因其使用的硬件架构而异。但总的来说,这些默认配置文件允许常用数量的系统调用,同时阻止更危险的系统调用,这些系统调用不太可能或不安全在容器化应用程序中使用。

启用该功能

必须进行两个 kubelet 配置更改才能启用该功能

  1. 通过命令行 (--feature-gates) 或 kubelet 配置文件SeccompDefault=true启用功能门
  2. 通过添加 --seccomp-default 命令行标志或通过 kubelet 配置文件 (seccompDefault: true) 来启用该功能

如果只完成上述步骤中的一个,kubelet 将在启动时出错。

试用一下

如果在节点上启用了该功能,则可以创建如下所示的新工作负载

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
    - name: test-container
      image: nginx:1.21

现在可以使用 crictl 检查使用的 seccomp 配置文件,同时检查容器的 运行时规范

CONTAINER_ID=$(sudo crictl ps -q --name=test-container)
sudo crictl inspect $CONTAINER_ID | jq .info.runtimeSpec.linux.seccomp
{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": ["SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32"],
  "syscalls": [
    {
      "names": ["_llseek", "_newselect", "accept", …, "write", "writev"],
      "action": "SCMP_ACT_ALLOW"
    },
    
  ]
}

您可以看到较低级别的容器运行时(在本例中为 CRI-Orunc)成功应用了默认的 seccomp 配置文件。此配置文件默认拒绝所有系统调用,同时允许常用的系统调用,如 acceptwrite

请注意,该功能目前不会影响任何 Kubernetes API。因此,如果 SeccompProfile 字段在 SecurityContext 中未设置,则无法通过 kubectl getdescribe 检索使用的 seccomp 配置文件。

该功能在使用 Pod 内的多个容器时也有效,例如,如果您创建如下所示的 Pod

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
    - name: test-container-nginx
      image: nginx:1.21
      securityContext:
        seccompProfile:
          type: Unconfined
    - name: test-container-redis
      image: redis:6.2

那么您应该看到 test-container-nginx 在没有 seccomp 配置文件的情况下运行

sudo crictl inspect $(sudo crictl ps -q --name=test-container-nginx) |
    jq '.info.runtimeSpec.linux.seccomp == null'
true

而容器 test-container-redisRuntimeDefault 运行

sudo crictl inspect $(sudo crictl ps -q --name=test-container-redis) |
    jq '.info.runtimeSpec.linux.seccomp != null'
true

这同样适用于 Pod 本身,它也以默认配置文件运行

sudo crictl inspectp (sudo crictl pods -q --name test-pod) |
    jq '.info.runtimeSpec.linux.seccomp != null'
true

升级策略

建议分多个步骤启用该功能,每个步骤都存在不同的风险和缓解措施。

启用功能门

在 kubelet 级别启用功能门不会启用该功能,但可以通过使用 SeccompDefault kubelet 配置或 --seccomp-default CLI 标志使其成为可能。管理员可以为整个集群或仅为一组节点执行此操作。

测试应用程序

如果您在专用测试环境中尝试此操作,则必须确保应用程序代码在节点上启用该功能之前不会触发 RuntimeDefault 配置文件阻止的系统调用。这可以通过以下方式完成:

  • 推荐:分析代码(手动或通过使用 strace 运行应用程序)查找可能被默认配置文件阻止的任何已执行系统调用。如果是这种情况,则可以通过显式设置 Pod 或容器以 Unconfined 方式运行来覆盖默认值。或者,您可以基于默认值创建自定义 seccomp 配置文件(请参阅下面的可选步骤),方法是将其他系统调用添加到 "action": "SCMP_ACT_ALLOW" 部分。

  • 推荐:手动将配置文件设置为目标工作负载,并使用滚动升级部署到生产环境。如果应用程序无法按预期工作,则回滚部署。

  • 可选:针对端到端测试套件运行应用程序,以触发启用 RuntimeDefault 的所有相关代码路径。如果测试失败,请使用上述相同的缓解措施。

  • 可选:基于默认值创建自定义 seccomp 配置文件,并将其默认操作从 SCMP_ACT_ERRNO 更改为 SCMP_ACT_LOG。这意味着未知系统调用的 seccomp 过滤器将不会对应用程序产生任何影响,但系统日志现在将指示可能被阻止的系统调用。这需要至少 4.14 的内核版本以及最新的 runc 版本。通过 type=SECCOMP(用于审计)或 type=1326(用于 syslog)监控应用程序主机的审计日志(默认为 /var/log/audit/audit.log)或 syslog 条目(默认为 /var/log/syslog)查找系统调用。将系统调用 ID 与 Linux 内核源代码中列出的 进行比较,并将其添加到自定义配置文件。请注意,自定义审计策略可能会导致缺少系统调用,具体取决于 auditd 的配置。

  • 可选:使用集群附加组件(如 安全配置文件运算符)通过其 日志增强 功能分析应用程序,或通过使用其 记录功能 记录配置文件。这使得上述手动日志调查变得过时。

部署修改后的应用程序

根据应用程序测试的结果,可能需要通过指定 Unconfined 或自定义 seccomp 配置文件来更改应用程序部署。如果应用程序按预期使用 RuntimeDefault 工作,则不会出现这种情况。

启用 kubelet 配置

如果一切顺利,则可以通过 kubelet 配置或其相应的 CLI 标志启用该功能。应在每个节点的基础上执行此操作,以降低在运行应用程序测试期间错过调查中的系统调用的总体风险。如果可以在集群内监视审计日志,则建议这样做以查找最终错过的 seccomp 事件。如果应用程序按预期工作,则可以为集群内的其他节点启用该功能。

结论

感谢您阅读这篇博文!我希望您喜欢看到 seccomp 配置文件在过去几个版本中如何在 Kubernetes 中演变,就像我一样。在您自己的集群上,将默认 seccomp 配置文件更改为 RuntimeDefault(使用此新功能)并查看安全优势,当然,随时可以联系我们以获取反馈或问题。


编者注:如果您对这篇博文有任何疑问或反馈,请随时通过 #sig-node 中的 Kubernetes slack 联系我们。