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

在 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 和 chrooted NGINX 进程

主要挑战之一是 Ingress-NGINX 在 Ingress 控制器(有权访问 Kubernetes API 并创建 nginx.conf 文件的组件)旁边运行 Web 代理服务器(NGINX)。

因此,NGINX 确实具有对控制器文件系统的相同访问权限(以及 Kubernetes 服务帐户令牌和容器中的其他配置)。虽然拆分这些组件是我们的最终目标,但该项目需要快速响应;这使我们想到了使用 chroot() 的想法。

让我们看一下此更改之前的 Ingress-NGINX 容器的样子

Ingress NGINX pre chroot

如我们所见,提供 HTTP 代理的同一个容器(不是 Pod,是容器!)是监视 Ingress 对象并写入容器卷的容器

现在,了解一下新架构

Ingress NGINX post chroot

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

虽然这并不完全正确,但要理解这里所做的工作,最好了解 Linux 容器(以及底层机制,如内核命名空间)是如何工作的。您可以在 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 图表部署控制器,请在 values.yaml 中更改以下设置

controller:
  image:
    chroot: true

Ingress 控制器通常在集群范围内设置(IngressClass API 是集群范围的)。如果您管理 Ingress-NGINX 控制器但不是整个集群的操作员,请在您在部署中启用 SYS_CHROOT 功能之前,与您的集群管理员核实您是否可以使用 SYS_CHROOT 功能。

好的,但这如何提高我的 Ingress 控制器的安全性?

请看以下配置代码段,并想象一下,由于某些原因,它被添加到您的 nginx.conf

location /randomthing/ {
      alias /;
      autoindex on;
}

如果您部署此配置,有人可以调用 http://website.example/randomthing 并获得对 Ingress 控制器整个文件系统的某些列表(和访问权限)。

现在,您能发现以下列表中 chrooted 和非 chrooted 的 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

左侧的没有 chrooted。因此,NGINX 具有对文件系统的完全访问权限。右侧的已 chrooted,因此创建了一个新的文件系统,其中只有使 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 运行 Ingress 控制器来禁用它。

接下来是什么?

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

Kubernetes 中的其他一些项目已经采用了这种方法(如 KPNG,建议的 kube-proxy 替代品),我们计划与他们保持一致,并为 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 中找到