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

使用新的 v1.22 Alpha 特性为所有工作负载启用 seccomp

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

您可以使用 Pod 及其容器的 securityContext 字段来调整工作负载的安全相关配置。Kubernetes 在 v1.19.0 中将 seccomp 晋级为通用可用性 (GA) 后,在这个 SecurityContext 中引入了专用的 seccomp 相关 API 字段。这项增强功能使得更容易指定整个 Pod 或特定容器应以下列方式运行:

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

随着 seccomp 的毕业,从整体安全角度来看并没有改变,因为 Unconfined 仍然是默认设置。如果您从 Kubernetes 版本的升级路径和向后兼容性角度考虑,这完全没问题。但这也意味着工作负载很可能根本没有运行 seccomp,这种情况应该长期解决。

SeccompDefault 来救援

Kubernetes v1.22.0 引入了一个新的 kubelet 功能门 (feature gate) SeccompDefault,它像其他所有新功能一样以 alpha 状态添加。这意味着它默认是禁用的,并且可以为每个 Kubernetes 节点手动启用。

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

启用该特性

要启用该功能,需要进行两个 kubelet 配置更改

  1. 启用功能门:通过命令行 (--feature-gates) 或 kubelet 配置文件SeccompDefault=true 设置为 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。因此,如果 SecurityContext 中的 SeccompProfile 字段未设置,则无法通过 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-redis 则使用 RuntimeDefault 运行

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 过滤器对未知系统调用将完全不影响应用程序,但系统日志将指示哪些系统调用可能被阻止。这至少需要 Kernel 版本 4.14 以及最近的 runc 版本。通过 type=SECCOMP(用于审计)或 type=1326(用于 syslog)监控应用程序主机的审计日志(默认为 /var/log/audit/audit.log)或 syslog 条目(默认为 /var/log/syslog)以获取系统调用。将系统调用 ID 与 Linux 内核源代码中列出的 进行比较,并将它们添加到自定义配置文件中。请注意,自定义审计策略可能会导致缺少系统调用,具体取决于 auditd 的配置。

  • 可选:使用集群附加组件,例如 Security Profiles Operator,通过其 日志丰富 功能或使用其 记录功能 来分析应用程序的配置文件。这使得上述手动日志调查变得过时。

部署修改后的应用程序

根据应用程序测试的结果,可能需要通过指定 Unconfined 或自定义 seccomp 配置文件来更改应用程序部署。如果应用程序能够按预期与 RuntimeDefault 配合工作,则不需要这样做。

启用 kubelet 配置

如果一切顺利,那么该功能就可以通过 kubelet 配置或其对应的 CLI 标志来启用。这应该在每个节点的基础上完成,以减少在运行应用程序测试时,调查过程中遗漏系统调用的总体风险。如果可以在集群内监控审计日志,那么建议这样做以防万一错过 seccomp 事件。如果应用程序按预期工作,那么该功能可以在集群内的其他节点上启用。

结论

感谢您阅读这篇博客文章!我希望您和我一样,享受看到 seccomp 配置文件在 Kubernetes 过去版本中的演变。在您自己的集群上,将默认 seccomp 配置文件更改为 RuntimeDefault(使用此新功能),并查看其带来的安全优势,当然,欢迎随时提出反馈或问题。


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