这篇文章已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已失效。
使用新的 v1.22 alpha 功能为所有工作负载启用 seccomp
这篇博文介绍的是 Kubernetes v1.22 中引入的一个新特性,该特性在现有 seccomp 支持之上增加了一个额外的安全层。Seccomp 是 Linux 进程的一种安全机制,用于根据一组预定义规则过滤系统调用 (syscalls)。将 seccomp profile 应用于容器化工作负载是增强应用程序部署安全性的关键任务之一。开发者、站点可靠性工程师和基础设施管理员需要紧密合作,在应用程序的生命周期内创建、分发和维护这些 profile。
您可以使用 Pod 的 securityContext
字段,及其容器可以用于调整工作负载的安全相关配置。Kubernetes 在 v1.19.0 版本中将 seccomp 正式发布 (GA),并在 SecurityContext
中引入了专用的 seccomp 相关 API 字段。这一增强功能使得更容易指定整个 Pod 或特定容器是否应以以下方式运行:
Unconfined
:seccomp 将不被启用RuntimeDefault
:将使用容器运行时的默认 profileLocalhost
:将应用一个节点本地 profile,该 profile 通过相对路径引用到 kubelet 的 seccomp profile 根目录 (<kubelet-root-dir>/seccomp
)
随着 seccomp 的正式发布,从整体安全性角度来看并没有发生任何变化,因为 Unconfined
仍然是默认设置。从 Kubernetes 版本的升级路径和向后兼容性角度来看,这是完全可以接受的。但这也意味着工作负载更有可能在完全没有 seccomp 的情况下运行,这应该在长期内得到解决。
SeccompDefault
来救援
Kubernetes v1.22.0 引入了一个新的 kubelet feature gate SeccompDefault
,该特性与其他所有新特性一样,以 alpha
状态添加。这意味着它默认是禁用的,可以为每个独立的 Kubernetes 节点手动启用。
这个特性有什么作用?它只是将默认的 seccomp profile 从 Unconfined
更改为 RuntimeDefault
。如果在 Pod manifest 中没有另外指定,那么该特性将通过使用容器运行时的默认 profile 来增加一组更高的安全约束。这些 profile 在不同的运行时(如 CRI-O 或 containerd)之间可能有所不同。它们也因所使用的硬件架构而异。但总的来说,这些默认 profile 允许进行常见的系统调用,同时阻止那些更危险的、不太可能或不安全在容器化应用程序中使用的系统调用。
启用该特性
需要进行两次 kubelet 配置更改才能启用该特性
- 启用该 feature gate,通过命令行 (
--feature-gates
) 或 kubelet 配置文件设置SeccompDefault=true
。 - 开启该特性功能,通过添加
--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 profile。
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-O 和 runc)成功应用了默认的 seccomp profile。该 profile 默认拒绝所有系统调用,同时允许常用的系统调用,如 accept
或 write
。
请注意,目前该特性不会影响任何 Kubernetes API。因此,如果在 SecurityContext
中未设置 SeccompProfile
字段,则无法通过 kubectl
get
或 describe
获取使用的 seccomp profile。
该特性在使用 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 profile 的情况下运行
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 本身,它也使用默认 profile 运行
sudo crictl inspectp (sudo crictl pods -q --name test-pod) |
jq '.info.runtimeSpec.linux.seccomp != null'
true
升级策略
建议分多步启用该特性,每一步都存在不同的风险和缓解措施。
启用 Feature Gate
在 kubelet 级别启用 feature gate 并不会立即开启该特性功能,但通过使用 SeccompDefault
kubelet 配置或 --seccomp-default
CLI 标志,可以使其得以启用。管理员可以为整个集群或仅一组节点执行此操作。
测试应用程序
如果您在专用的测试环境中尝试此操作,在节点上启用该特性之前,您必须确保应用程序代码不会触发被 RuntimeDefault
profile 阻止的系统调用。这可以通过以下方式完成:
建议:分析代码(手动或通过使用 strace 运行应用程序),查找可能被默认 profile 阻止的任何执行的系统调用。如果存在这种情况,您可以通过显式地将 Pod 或容器设置为以
Unconfined
方式运行来覆盖默认设置。或者,您可以创建一个自定义 seccomp profile(参见下面的可选步骤),在默认 profile 的基础上,将额外的系统调用添加到"action": "SCMP_ACT_ALLOW"
部分。建议:手动将 profile 设置到目标工作负载,并使用滚动升级将其部署到生产环境。如果应用程序未能按预期工作,则回滚部署。
可选:针对端到端测试套件运行应用程序,以在启用
RuntimeDefault
的情况下触发所有相关的代码路径。如果某个测试失败,使用与上述相同的缓解措施。可选:基于默认 profile 创建一个自定义 seccomp profile,并将其默认操作从
SCMP_ACT_ERRNO
更改为SCMP_ACT_LOG
。这意味着 seccomp 对未知系统调用的过滤将对应用程序完全没有影响,但系统日志现在将指示哪些系统调用可能被阻止。这至少需要 Kernel 版本 4.14 以及最近的 runc 发布版本。监控应用程序主机的审计日志(默认为/var/log/audit/audit.log
)或 syslog 条目(默认为/var/log/syslog
),查找通过type=SECCOMP
(用于审计)或type=1326
(用于 syslog)报告的系统调用。将系统调用 ID 与Linux Kernel 源代码中列出的进行比较,并将它们添加到自定义 profile 中。请注意,自定义审计策略可能会导致缺少系统调用,具体取决于 auditd 的配置。可选:使用集群附加组件,例如 Security Profiles Operator,通过其日志丰富功能对应用程序进行分析,或者使用其记录功能记录 profile。这使得上述手动日志调查变得过时。
部署修改后的应用程序
根据应用程序测试的结果,可能需要修改应用程序部署,指定 Unconfined
或自定义 seccomp profile。如果应用程序使用 RuntimeDefault
正常运行,则不需要这样做。
启用 kubelet 配置
如果一切顺利,那么该特性已准备好通过 kubelet 配置或其对应的 CLI 标志启用。这应在每个节点的基础上进行,以减少在运行应用程序测试期间调查时遗漏系统调用的总体风险。如果可以在集群内监控审计日志,则建议这样做以捕获可能遗漏的 seccomp 事件。如果应用程序按预期工作,则可以在集群内的其他节点上启用该特性。
结论
感谢您阅读这篇博文!我希望您和我一样,高兴地看到了 seccomp profile 在过去几个版本的 Kubernetes 中的演变。在您自己的集群上,将默认的 seccomp profile 更改为 RuntimeDefault
(使用这个新特性),并查看带来的安全收益。当然,欢迎随时联系我们提供反馈或提出问题。
编者按:如果您对这篇博文有任何问题或反馈,欢迎通过 Kubernetes slack 中的 #sig-node 联系我们。