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

在 Ingress-NGINX v1.2.0 中提高安全标准

Ingress 可能是 Kubernetes 中最常被攻击的组件之一。Ingress 通常定义了一个暴露给互联网的 HTTP 反向代理,其中包含多个网站,并拥有对 Kubernetes API 的一些特权访问(例如读取与 TLS 证书及其私钥相关的 Secret)。

虽然它在您的架构中是一个有风险的组件,但它仍然是正确暴露服务的最流行方式。

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 容器是什么样子

Ingress NGINX pre chroot

正如我们所看到的,提供 HTTP 代理的容器(不是 Pod,而是容器!)是监视 Ingress 对象并写入容器卷的容器

现在,认识一下新架构

Ingress NGINX post chroot

这一切意味着什么?一个基本的总结是:我们将 NGINX 服务隔离为控制器容器内的容器。

虽然这并非完全准确,但要理解这里所做的工作,最好了解 Linux 容器(以及 cgroup 等底层机制和内核命名空间)的工作原理。您可以在 Kubernetes 词汇表中阅读有关 cgroup 的内容:cgroup,并在 NGINX 项目文章 命名空间和 cgroup 是什么,它们如何工作? 中了解 cgroup 如何与命名空间交互。(在您阅读时,请记住 Linux 内核命名空间与 Kubernetes 命名空间是不同的概念)。

跳过理论,我需要什么才能使用这种新方法?

虽然这会提高安全性,但我们在本次发布中将此功能设置为可选,以便您有时间在环境中进行正确的调整。此新功能仅适用于 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()
binbin
devdev
etcetc
home
liblib
media
mnt
optopt
procproc
root
runrun
sbin
srv
sys
tmptmp
usrusr
varvar
dbg
nginx-ingress-controller
wait-shutdown

左侧的没有 chroot。因此 NGINX 对文件系统拥有完全访问权限。右侧的进行了 chroot,因此创建了一个只包含使 NGINX 工作所需文件的新文件系统。

此版本中还有哪些其他安全改进?

我们知道新的 chroot() 机制有助于解决部分风险,但是,仍然有人可以尝试注入命令来读取,例如,nginx.conf 文件并提取敏感信息。

因此,此版本中的另一项更改(这是可选退出!)是“深度检查器”。我们知道某些指令或正则表达式可能对 NGINX 危险,因此深度检查器会检查 Ingress 对象的所有字段(在其协调期间,以及通过验证准入 Webhook),以验证是否有任何字段包含这些危险指令。

Ingress 控制器已经对注解执行此操作,我们的目标是在未来版本中将此现有验证移到深度检查中。

您可以在 https://github.com/kubernetes/ingress-nginx/blob/main/internal/ingress/inspector/rules.go 中查看现有规则。

由于需要检查和匹配相关 Ingress 对象中的所有字符串,此新功能可能会消耗更多 CPU。您可以通过在命令行参数中运行 --deep-inspect=false 来禁用它。

接下来是什么?

这并非我们的最终目标。我们的最终目标是将控制平面和数据平面进程分离。事实上,这样做也将有助于我们实现 Gateway API 实现,因为一旦它“知道”要向数据平面提供什么,我们可能会有不同的控制器(这里需要一些帮助!!)

Kubernetes 中的其他一些项目已经采取了这种方法(例如 KPNGkube-proxy 的拟议替代品),我们计划与它们保持一致,并在 Ingress-NGINX 中获得相同的体验。

进一步阅读

如果您想了解 Ingress NGINX 中如何实现 chroot,请查看 https://github.com/kubernetes/ingress-nginx/pull/8337。包含所有更改的 v1.2.0 版本可在 https://github.com/kubernetes/ingress-nginx/releases/tag/controller-v1.2.0 找到。