博客 | NGINX

宣布推出 NGINX Plus R18

NGINX-F5-horiz-black-type-RGB 的一部分
Liam Crilly 缩略图
利亚姆·克里利
2019 年 4 月 9 日发布


我们很高兴地宣布NGINX Plus Release 18 (R18)现已推出。 NGINX Plus 是唯一负载均衡器、内容缓存、Web 服务器和 API 网关于一体的服务器。 NGINX Plus 基于 NGINX 开源,包含独有的增强功能和屡获殊荣的支持。 R18 简化了 DevOps 的配置工作流程,并大规模增强了应用的安全性和可靠性。

目前,超过 87% 的网站使用 SSL/TLS来加密互联网通信,而三年前这一比例仅为 66%。 端到端加密现在是网站和应用的默认部署模式,SSL/TLS 证书的激增意味着一些公司在生产环境中管理着数千个证书。 这需要一种更灵活的方法来部署和配置证书。

此版本中的新功能是支持动态证书加载。 由于存在数千个证书,因此在配置中手动定义每个证书以便从磁盘加载是不可扩展的 - 不仅该过程繁琐,而且配置变得难以管理,并且 NGINX Plus 启动速度令人无法接受。 使用NGINX Plus R18 ,SSL/TLS 证书现在可以按需加载,而无需在配置中单独列出。 为了进一步简化自动部署,可以使用NGINX Plus API配置证书,甚至不必将其存储在磁盘上。

NGINX Plus R18的其他新功能包括:

  • OpenID Connect 增强功能- 我们继续改进我们支持的 OpenID Connect 参考实现,最初在NGINX Plus R15中发布。 在此版本中,我们增加了对不透明会话令牌、刷新令牌和注销 URL 的支持。
  • 虚拟服务器的端口范围- 现在可以配置 NGINX Plus 虚拟服务器来监听一系列端口,例如80-90 。 这使得 NGINX Plus 能够支持更广泛的应用,例如需要保留端口范围的被动 FTP。
  • 配置中的键值定义- NGINX Plus 键值存储为广泛的用例提供解决方案,包括 IP 地址的动态拒绝名单和动态 DDoS 缓解。 您现在可以在 NGINX Plus 配置中直接使用变量创建键值对,从而开辟更多的用例。
  • 主动健康检查的灵活性更高– NGINX Plus 的主动健康检查是监控后端系统健康状况的强大工具。 使用NGINX Plus R18 ,您现在可以测试任何 NGINX 变量的值,并自动关闭与故障服务器的现有 TCP 连接。

此版本完善了集群环境的配置,并增加了新的和更新的动态模块(包括 Brotli)。 NGINX Plus 生态系统的更新包括使用 NGINX JavaScript 模块进行模块化代码组织,以及直接在 Helm 上安装适用于 Kubernetes 的官方 NGINX Ingress Controller。

行为方面的重要变化

  • 过时的 API - NGINX Plus R13 (2017 年 8 月)引入了全新的NGINX Plus API,用于指标收集和上游组的动态重新配置,取代了之前实现这些功能的 Status 和 Upstream Conf API。 正如当时宣布的那样,弃用的 API 仍然可用并支持相当长的一段时间,直到NGINX Plus R16才结束。 如果您的配置包含status和/或upper_conf指令,则必须在升级到 R18 的过程中将它们替换为api指令。

    有关迁移到新NGINX Plus API 的建议和帮助,请参阅我们博客上的过渡指南,或联系我们的支持团队。

  • 更新了listen指令- 以前,当listen指令指定解析为多个 IP 地址的主机名时,仅使用第一个 IP 地址。 现在为每个返回的 IP 地址创建一个监听套接字。

  • NGINX JavaScript 模块 (njs) 更改- 已从NGINX JavaScript 模块中删除已弃用的req.response对象。 使用function(req,res)语法声明的函数如果引用了res对象的属性,则会产生运行时错误,并返回 HTTP 状态代码500以及错误日志中的相应条目:

    YYYY / MM / DD hh : mm : ss [错误] 34#34:js 异常: TypeError:无法获取未定义的属性“return”

    由于 JavaScript 代码是在运行时解释的,因此nginx -t语法验证命令不会检测无效对象和属性的存在。 在升级到NGINX Plus R18之前,您必须仔细检查您的 JavaScript 代码并删除此类对象。

    此外,当给定属性没有值时,表示 NGINX 状态的 JavaScript 对象(例如, r.headersIn )现在返回undefined而不是空字符串。 这一变化意味着 NGINX 特定的 JavaScript 对象现在的行为与内置 JavaScript 对象相同。

  • 已删除或将要删除的旧操作系统:

    • Amazon Linux 2017.09 不再受支持;最旧受支持的版本现在是 2018.03
    • CentOS/Oracle Linux/Red Hat Enterprise Linux 7.3 不再受支持;最旧受支持的版本现在是 7.4
    • Debian 8.0 将在NGINX Plus R19中删除
    • Ubuntu 14.04 将在NGINX Plus R19中删除

新功能详情

动态 SSL/TLS 证书加载

在 NGINX Plus 的早期版本中,管理安全站点和应用的 SSL/TLS 证书的典型方法是为每个主机名创建一个单独的服务器块,并将证书和关联的私钥静态指定为磁盘上的文件。 (为了方便阅读,从现在开始我们将使用证书来指代配对的证书和密钥。) NGINX Plus 启动时,证书就被加载。 使用NGINX Plus R18 ,可以动态加载证书,并可选择将其存储在内存中的 NGINX Plus 键值存储中,而不是磁盘上。

动态证书加载有两个主要用例:

在这两种情况下,NGINX Plus 都可以根据服务器名称指示(SNI)提供的主机名作为 TLS 握手的一部分执行动态证书加载。 这使得 NGINX Plus 能够在单个服务器配置下托管多个安全网站,并根据需要为每个传入请求选择适当的证书。

从磁盘延迟加载 SSL/TLS 证书

通过“延迟加载”,SSL/TLS 证书仅在请求到达并指定相应的主机名时才加载到内存中。 这既简化了配置(通过消除每个主机名证书列表),又减少了主机上的资源利用率。 由于证书数量巨大(数千个),因此可能需要几秒钟才能从磁盘读取所有证书并将其加载到内存中。 此外,重新加载 NGINX 配置时会使用大量内存,因为新的一组工作进程会将证书的新副本与上一组工作进程加载的证书一起加载到内存中。 先前的证书将保留在内存中,直到在旧配置下建立的最终连接完成并且先前的工作程序终止。 如果配置频繁更新且客户端连接寿命很长,则内存中可能会有多个证书副本,这可能会导致内存耗尽。

从磁盘延迟加载证书非常适合具有大量证书的部署和/或频繁重新加载配置的情况。 例如,SaaS 公司通常为每个客户分配一个单独的子域。 引入新客户很困难,因为您必须为每个客户创建一个新的虚拟服务器,然后将新配置和客户的证书复制到每个 NGINX Plus 实例。 延迟加载消除了配置更改的需要——只需在每个实例上部署证书即可完成。

为了支持延迟加载, ssl_certificatessl_certificate_key指令现在接受可变参数。 该变量必须在 SNI 处理期间可用,该处理发生在读取请求行和标头之前。 最常用的变量是$ssl_server_name ,它保存 NGINX Plus 在 SNI 处理期间提取的主机名。 在每个客户端会话开始时的 TLS 握手期间,证书和密钥从磁盘读取,并缓存在文件系统缓存的内存中,从而进一步减少内存利用率。

安全站点配置变得如此简单:

相同的服务器配置可用于无限数量的安全站点。 这有两个好处:

  1. 它消除了每个主机名的单独服务器块,使配置更小,从而更易于阅读和管理。
  2. 它消除了每次添加新主机名时重新加载配置的需要。 当您确实需要重新加载配置时,速度会更快,因为 NGINX Plus 不会加载所有证书。

请注意,由于需要文件系统调用来从磁盘检索证书,因此延迟加载会使 TLS 握手所需的时间延长 20-30%,具体取决于环境。 然而,额外的延迟只会影响握手——一旦建立 TLS 会话,请求处理就会花费通常的时间。

内存 SSL/TLS 证书存储

您现在可以将 SSL/TLS 证书数据存储在内存中、NGINX Plus键值存储中以及磁盘上的文件中。 当ssl_certificatessl_certificate_key指令的参数以data:前缀开头时,NGINX Plus 将该参数解释为原始 PEM 数据(以变量的形式提供,该变量标识数据实际所在的键值存储中的条目)。

在键值存储而不是磁盘上存储的另一个好处是,部署映像和备份不再包含私钥的副本,攻击者可以使用私钥来解密发送到服务器和从服务器发送的所有流量。 拥有高度自动化部署管道的公司可以灵活地使用NGINX Plus API以编程方式将证书插入键值存储。 此外,将应用迁移到没有真正的硬件安全模块 (HSM) 来保护私钥的公共云环境的公司可以受益于不将私钥存储在磁盘上所带来的额外安全性。

以下是从键值存储加载证书的示例配置:

使用NGINX Plus API将证书和私钥上传到键值存储的一种方法是运行以下curl命令(仅显示密钥数据的最开头)。 如果使用curl命令,请记住复制 PEM 数据并将每个换行符替换为\n ;否则换行符将从 JSON 有效负载中删除。

$ curl -d '{"www.example.com":"-----BEGIN RSA PRIVATE KEY-----\n..."}' http://localhost:8080/api/4/http/keyvals/ssl_key

使用证书的键值存储非常适合 NGINX Plus 的集群部署,因为您只需上传一次证书即可在集群中自动传播。 为了保护证书数据本身,请使用zone_sync_ssl指令对集群成员之间的连接进行 TLS 加密。 使用键值存储对于短期证书或与证书颁发机构(例如Let's EncryptHashicorp Vault )的自动集成也非常理想。

与从磁盘的延迟加载一样,从键值存储加载证书发生在每次 TLS 握手期间,这会导致性能损失。 为了实现最快的 TLS 握手,请使用ssl_certificatessl_certificate_key指令以及磁盘上文件的硬编码参数。 此外, ECC证书比RSA证书更快。

请注意,虽然键值存储使攻击者获取私钥文件比从磁盘存储更困难,但具有 NGINX Plus 主机 shell 访问权限的攻击者仍然能够访问内存中加载的密钥。 键值存储对私钥的保护程度不如硬件安全模块(HSM);要让 NGINX Plus 从 HSM 获取密钥,请使用ssl_certificate_key指令的engine: engine-name : key-id参数。

OpenID Connect 增强功能

NGINX Plus 通过我们的参考实现支持后端应用的OpenID Connect 身份验证和单点登录。 现在,可以使用变量直接从 JavaScript 模块修改键值存储,这已经得到简化和增强(见下文)。

OpenID Connect 参考实现现在以浏览器 cookie 的形式向客户端发出不透明会话令牌。 不透明令牌不包含任何有关用户的个人身份信息,因此客户端上不会存储任何敏感信息。 NGINX Plus 将实际的 ID 令牌存储在键值存储中,并将其替换为客户端提供的不透明令牌。 每个请求都会执行 JWT 验证,以便拒绝过期或无效的令牌。

OpenID Connect 参考实现现在还支持刷新令牌,以便无缝刷新过期的 ID 令牌而无需用户交互。 NGINX Plus 将授权服务器发送的刷新令牌存储在键值存储中,并将其与不透明会话令牌关联。 当 ID 令牌过期时,NGINX Plus 会将刷新令牌发送回授权服务器。 如果会话仍然有效,授权服务器将发出一个新的 ID 令牌,该令牌会在键值存储中无缝更新。 刷新令牌使得使用短期 ID 令牌成为可能,这提供了更好的安全性,而不会给用户带来不便。

OpenID Connect 参考实现现在提供了注销 URL。 当登录用户访问/logout URI 时,他们的 ID 和刷新令牌将从键值存储中删除,并且他们必须在将来发出请求时重新进行身份验证。

虚拟服务器的端口范围

服务器块通常有一个listen指令,指定 NGINX Plus 监听的单个端口;如果需要配置多个端口,则每个端口都有一个额外的listen指令。 使用NGINX Plus R18 ,您现在还可以指定端口范围,例如80-90 ,因为指定大量单独的监听指令不方便。

可以为 HTTP侦听指令和 TCP/UDP(流)侦听指令指定端口范围。 以下配置使 NGINX Plus 能够以被动模式充当 FTP 服务器的代理,其中数据端口从大量 TCP 端口中选择。

此配置设置了一个虚拟服务器,用于代理与连接接入的同一端口上的 FTP 服务器的连接。

通过变量更新键值存储

当启用键值存储时,NGINX Plus 会根据输入键(通常是请求元数据的一部分)为存储在那里的值提供一个变量。 以前,创建、修改或删除键值存储中的值的唯一方法是使用NGINX Plus API 。使用NGINX Plus R18 ,您可以通过设置保存该值的变量直接在配置中更改键的值。

以下示例使用键值存储来维护最近访问该站点的客户端 IP 地址列表以及他们请求的最后一个 URI。

set指令(第 7 行)为每个客户端 IP 地址( $remote_addr )分配一个值( $last_uri ),如果不存在则创建一个新条目,或者修改该值以反映当前请求的$uri 。 因此,通过调用NGINX Plus API可以获得最近的客户端及其请求的 URI 的当前列表:

$ curl http://localhost:8080/api/4/http/keyvals/recents { “10.19.245.68”:“/blog/nginx-plus-r18-released/”, “172.16.80.227”:“/products/nginx/”, “10.219.110.168”:“/blog/nginx-unit-1-8-0-now-available” }

使用脚本扩展(例如 NGINX JavaScript 模块(njs)和 Lua 模块)可以实现更强大的用例。 任何使用 njs 的配置都可以访问所有变量,包括由键值存储支持的变量,例如r.variables.last_uri

更加灵活地进行主动健康检查

NGINX Plus 的主动健康检查会定期测试后端系统,以便流量不会被引导到已知不健康的系统。 NGINX Plus R18通过两项附加功能扩展了这一重要功能。

在健康检查中测试任意变量

在为后端应用定义健康检查时,可以使用match块来指定响应的多个方面的预期值,包括 HTTP 状态代码和响应标头和/或正文中的字符串。 当响应包含所有预期值时,后端被视为健康的。

对于更复杂的检查, NGINX Plus R18现在提供了require指令来测试任何变量的值 - 标准 NGINX 变量和您声明的变量。 这使得您在定义健康检查时具有更大的灵活性,因为可以使用映射块、正则表达式甚至脚本扩展来评估变量。

match块内的require指令指定一个或多个变量,所有变量都必须具有非零值才能通过测试。 以下示例配置将健康的上游服务器定义为返回指示响应可缓存的标头的服务器 - 具有非零值的Expires标头或Cache-Control标头。

以这种方式使用映射块是将 OR 逻辑合并到 NGINX Plus 配置中的常用方法。 require指令使您能够在健康检查中利用此技术,以及执行高级健康检查。 还可以通过使用 JavaScript 模块(njs)来定义高级健康检查,以分析来自每个上游服务器的响应的附加属性,例如响应时间

健康检查失败时终止第 4 层连接

当 NGINX Plus 充当 TCP/UDP应用的第 4 层 (L4)负载均衡器时,它会在客户端和后端服务器之间建立的连接上双向代理数据。 主动健康检查是此类配置的重要组成部分,但默认情况下,仅当新客户端尝试建立连接时才会考虑后端服务器的健康状态。 如果后端服务器离线,已建立的客户端在向服务器发送数据时可能会遇到超时。

使用NGINX Plus R18中新增的proxy_session_drop指令,您可以在从离线服务器接收到下一个数据包或向离线服务器发送下一个数据包时立即关闭连接。 客户端被强制重新连接,此时 NGINX Plus 将其请求代理到健康的后端服务器。

启用此指令后,另外两个条件也会触发终止现有连接:主动健康检查失败,以及由于任何原因从上游组中删除服务器。 这包括通过 DNS 查找进行删除,其中后端服务器由具有多个 IP 地址的主机名定义,例如服务注册表提供的 IP 地址。

NGINX Plus R18 中的其他增强功能

简化集群配置

NGINX Plus 自NGINX Plus R15以来就支持集群范围的运行时状态同步。 区域同步模块目前支持在 NGINX Plus 实例集群部署中共享有关粘性会话速率限制键值存储的状态数据。

现在可以将单个zone_sync配置用于集群中的所有实例。 以前,您必须明确配置每个成员的 IP 地址或主机名,这意味着每个实例的配置都略有不同。 现在,您可以通过为listen指令的address : port参数指定通配符值,让zone_sync服务器监听所有本地接口。 当将 NGINX Plus 部署到动态集群中时,这一点尤其有价值,因为在部署之前无法知道实例的 IP 地址。

在每个实例上使用相同的配置极大地简化了动态环境中的部署(例如,使用自动扩展组或容器化集群)。

新的和更新的动态模块

此版本中添加或更新了以下动态模块:

  • 新的Brotli 压缩模块– Brotli 是一种通用的无损数据压缩算法,它使用 LZ77 算法、Huffman 编码和二阶上下文建模的变体。
  • 新的OpenTracing 模块- 您现在可以使用符合 OpenTracing 要求的请求来为 NGINX Plus 提供一系列分布式跟踪服务,例如 Datadog、Jaeger 和 Zipkin。
  • 更新的Lua 模块– Lua 是 NGINX Plus 的脚本语言。 该模块现在使用 LuaJIT 2.1。

NGINX Plus 生态系统的更新

NGINX JavaScript 模块的增强功能

NGINX JavaScript 模块(njs)已更新至版本 0.3.0 。 最显著的增强是对 JavaScript导入导出模块的支持,这使您能够将 JavaScript 代码组织成多个特定于功能的文件。 以前,所有 JavaScript 代码都必须位于单个文件中。

下面的示例展示了如何使用 JavaScript 模块来组织和简化相对简单的用例所需的代码。 在这里,我们使用 JavaScript 执行数据屏蔽以保护用户隐私,以便记录客户端 IP 地址的散列(屏蔽)版本而不是真实地址。 日志中给定的屏蔽 IP 地址始终代表同一个客户端,但无法转换回真实 IP 地址。

[编辑器– 本节中的示例只是 NGINX JavaScript 模块的众多用例之一。 有关完整列表,请参阅NGINX JavaScript 模块的用例

代码已更新为使用js_import指令,该指令取代了 NGINX Plus R23 及更高版本中的js_include指令。 有关更多信息,请参阅NGINX JavaScript 模块的参考文档 -示例配置部分显示了 NGINX 配置和 JavaScript 文件的正确语法。

在 NGINX Plus R22 及更高版本中,除了 JavaScript导入导出模块之外,您还可以使用 njs js_import指令将您的 JavaScript 代码组织成多个特定于功能的文件。 ]

我们将 IP 地址屏蔽所需的函数放入一个 JavaScript 模块中,该模块导出单个函数maskIp() 。 导出函数依赖于仅在模块内可用的私有函数,并且不能被其他 JavaScript 代码调用。

现在可以将此模块导入到主 JavaScript 文件( main.js )中,并引用导出的函数。

因此, main.js非常简单,只包含NGINX配置引用的函数。 导入语句指定模块文件的相对或绝对路径。 当提供相对路径时,您可以使用新的js_path指令来指定要搜索的其他路径。

新功能大大提高了可读性和维护性,特别是在使用大量 njs 指令和/或大量 JavaScript 代码时。 不同的团队现在可以维护自己的 JavaScript 代码,而无需执行复杂的合并到主 JavaScript 文件的操作。

直接在 Helm 上安装适用于 Kubernetes 的 NGINX Ingress Controller

您现在可以直接从我们的新 Helm 存储库安装 NGINX Ingress Controller for Kubernetes,而无需下载 Helm 图表源文件(尽管仍然支持)。 有关更多信息,请参阅GitHub 存储库

值得注意的错误修复

UDPapplications的带宽限制

proxy_upload_rateproxy_download_rate指令现在可以正确地用于 UDP 数据报。

带有健康检查的 PROXY 协议 TLV 导致 NGINX 崩溃

以前,当health_check指令包含在引用$proxy_protocol_tlv_0xEA变量的位置时(例如在AWS PrivateLink环境中),NGINX Plus 可能会崩溃。

最少时间负载平衡忽略最慢的上游

以前,如果上游服务器长时间响应缓慢,它可能永远不会再被选中,因为与其他上游服务器相比,它的指定时间指标的值太高。 现在,由于新的测量结果允许移动平均值降低,因此之前速度较慢的上游服务器最终会重新引入到负载均衡器选择过程中。

这适用于使用上游响应时间作为选择指标的负载平衡算法,特别是least_time和带有least_time参数的随机算法

升级或尝试 NGINX Plus

如果您正在运行 NGINX Plus,我们强烈建议您尽快升级到NGINX Plus R18 。 您还将获得许多额外的修复和改进,这将帮助 NGINX, Inc. 在您需要提出支持单时为您提供帮助。

在继续升级之前,请仔细查看本博客文章中描述的新功能行为变化

如果您还没有尝试过 NGINX Plus,我们鼓励您尝试一下 – 为了安全、负载均衡和 API 网关,或者作为具有增强监控和管理 API 的完全支持的 Web 服务器。 您今天就可以开始享受30 天免费评估服务。 亲自了解 NGINX Plus 如何帮助您交付和扩展您的应用。


“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”