本文已超过一年。较旧的文章可能包含过时内容。请检查页面中的信息自发布以来是否仍然正确。
提升 Ingress-NGINX v1.2.0 的安全标准
Ingress 可能是 Kubernetes 中最常被攻击的组件之一。Ingress 通常定义一个暴露给互联网的 HTTP 反向代理,包含多个网站,并具有对 Kubernetes API 的一些特权访问(例如读取与 TLS 证书及其私钥相关的 Secrets)。
虽然它是您的架构中的一个高风险组件,但它仍然是正确暴露服务的最流行方式。
Ingress-NGINX 曾参与过安全评估,结果发现我们存在一个大问题:在将配置转换为 nginx.conf
文件之前,我们没有进行所有适当的清理,这可能导致信息泄露风险。
虽然我们理解这个风险以及解决它的实际需求,但这并非易事,因此我们在当前 (v1.2.0) 版本中采取了另一种方法来降低(但未消除!)此风险。
认识 Ingress NGINX v1.2.0 和 chroot 后的 NGINX 进程
主要挑战之一是 Ingress-NGINX 将 Web 代理服务器 (NGINX) 与 Ingress 控制器(具有访问 Kubernetes API 并创建 nginx.conf
文件的组件)一起运行。
因此,NGINX 具有与控制器(以及 Kubernetes 服务账户令牌和容器中的其他配置)相同的 文件系统访问权限。虽然将这些组件分离是我们的最终目标,但项目需要快速响应;这促使我们考虑使用 chroot()
。
让我们看看此更改之前 Ingress-NGINX 容器是什么样的
正如我们所见,提供 HTTP 代理服务的同一个容器(不是 Pod,是容器!)也是监视 Ingress 对象并写入容器卷的那个。
现在,认识新的架构
这一切意味着什么?一个基本的总结是:我们将 NGINX 服务隔离为控制器容器内的另一个容器。
虽然这并非严格意义上的事实,但要理解此处所做的工作,最好了解 Linux 容器(及底层机制如内核命名空间)的工作原理。您可以在 Kubernetes 词汇表中阅读有关 cgroups 的内容:cgroup
,并在 NGINX 项目文章 什么是命名空间和 cgroups,以及它们如何工作?中了解更多关于 cgroups 如何与命名空间交互的内容。(阅读时请记住,Linux 内核命名空间与Kubernetes 命名空间是不同的东西)。
跳过理论,我需要做些什么来使用这种新方法?
虽然这提高了安全性,但我们在本次发布中将此功能设为选择加入 (opt-in),以便您有时间在您的环境 (environment(s)) 中进行适当的调整。此新功能仅从 Ingress-NGINX 控制器 v1.2.0 版本开始提供。
要使用此功能,您需要在部署中进行两项必需的更改
- 在容器镜像名称后添加后缀 "-chroot"。例如:
gcr.io/k8s-staging-ingress-nginx/controller-chroot:v1.2.0
- 在您的 Ingress 控制器的 Pod 模板中,找到添加能力
NET_BIND_SERVICE
的位置,并添加能力SYS_CHROOT
。编辑清单后,您将看到如下片段
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
- SYS_CHROOT
如果您使用官方 Helm Chart 部署控制器,则更改 values.yaml
中的以下设置
controller:
image:
chroot: true
Ingress 控制器通常在集群范围内设置 (IngressClass API 是集群范围的)。如果您管理 Ingress-NGINX 控制器但并非整体集群操作员,则在您的部署中启用 SYS_CHROOT
能力**之前**,请与您的集群管理员确认您是否可以使用此能力。
好的,但这如何提高我的 Ingress 控制器的安全性?
假设出于某种原因,以下配置片段被添加到您的 nginx.conf
中
location /randomthing/ {
alias /;
autoindex on;
}
如果您部署此配置,有人可以调用 http://website.example/randomthing
并获取 Ingress 控制器整个文件系统的列表(和访问权限)。
现在,您能看出 chroot 后和未 chroot 的 Nginx 在以下列表中有什么区别吗?
无额外 chroot() | 有额外 chroot() |
---|---|
bin | bin |
dev | dev |
etc | etc |
home | |
lib | lib |
media | |
mnt | |
opt | opt |
proc | proc |
root | |
run | run |
sbin | |
srv | |
sys | |
tmp | tmp |
usr | usr |
var | var |
dbg | |
nginx-ingress-controller | |
wait-shutdown |
左侧的列表未进行 chroot。因此 NGINX 对文件系统具有完全访问权限。右侧的列表进行了 chroot,因此创建了一个只包含 NGINX 工作所需文件的新文件系统。
本次发布还有哪些其他安全改进?
我们知道新的 chroot()
机制有助于解决一部分风险,但仍然有人可能尝试注入命令来读取(例如)nginx.conf
文件并提取敏感信息。
因此,本次发布的另一项更改(这是选择退出 (opt-out) 功能!)是 *深度检查器 (deep inspector)*。我们知道某些指令或正则表达式可能对 NGINX 有危险,因此深度检查器会检查 Ingress 对象的所有字段(在其协调过程中,以及通过验证准入 Webhook)以验证是否存在包含这些危险指令的字段。
Ingress 控制器已经对注解进行了此类检查,我们的目标是在未来的版本中将此现有验证移至深度检查作为其一部分。
您可以在https://github.com/kubernetes/ingress-nginx/blob/main/internal/ingress/inspector/rules.go 中查看现有规则。
由于需要检查和匹配相关 Ingress 对象中的所有字符串,此新功能可能会消耗更多的 CPU。您可以通过运行带有命令行参数 --deep-inspect=false
的 ingress 控制器来禁用它。
下一步是什么?
这并非我们的最终目标。我们的最终目标是分离控制平面和数据平面进程。事实上,这样做也将有助于我们实现 Gateway API 的实现,因为一旦数据平面“知道”需要提供什么,我们就可以拥有一个不同的控制器(这里我们需要一些帮助!!)
Kubernetes 中的其他一些项目(例如 kube-proxy
的拟议替代品 KPNG)已经采取了这种方法,我们计划与他们保持一致,并在 Ingress-NGINX 中获得相同的体验。
延伸阅读
如果您想了解 Ingress NGINX 中如何实现 chrooting,请参阅https://github.com/kubernetes/ingress-nginx/pull/8337 包含所有更改的 v1.2.0 版本可以在https://github.com/kubernetes/ingress-nginx/releases/tag/controller-v1.2.0 找到