BLOG | NGINX

隆重推出 NGINX Plus R26

NGINX-Part-of-F5-horiz-black-type-RGB
Robert Haynes 缩略图
Robert Haynes
Published February 15, 2022

很高兴宣布推出 NGINX Plus 版本 26 (R26)。NGINX Plus 基于 NGINX 开源版构建而成,是唯一一款将软件 Web 服务器、负载均衡器、反向代理、内容缓存和 API 网关集于一身的多合一产品。

NGINX Plus R26 的新增特性包括:

  • 借助 JSON Web Key Set 缓存加快 JWT 验证 —— 在前面的几个版本中,对于 JSON Web Token (JWT) 的支持一直在不断增强,我们在这次的新版本中又引入了 JSON Web Key Set (JWKS) 内存缓存,使 JWT 验证的开销大幅下降。
  • 加固后的 TLS 握手机制 —— 如果客户端通过 ALPN 发起的通信协议和会话建立时所用的协议与 NGINX 配置中的上下文不匹配(例如,向 http{} 上下文中的虚拟服务器提出 IMAP 请求),NGINX Plus 将拒绝 TLS 握手。
  • NGINX JavaScript 模块的增强功能 —— 现在已经支持那些包含了 asyncawait 关键字以及 Promise 对象的异步函数,我们还实现了用于加密操作(如生成随机数或加密 cookie)的 WebCrypto API。

此外,此版本还支持 IBM System Z (s390x) 架构,能够独立关闭每个方向的 TCP 连接,并支持 Perl Compatible Regular Expression (PCRE) 正则表达式库的第二个版本。

重要行为变更

NGINX JavaScript 模块不再支持 js_include

我们在发布 NGINX Plus R23 时指出,在 NGINX JavaScript 模块 0.4.0 版中,js_import 指令取代了 js_include 指令。js_include 指令在 NGINX Plus R23 发布时就已被弃用,从 NGINX Plus R26 版开始,将停止支持该指令。

在升级到 NGINX Plus R26 之前,请将 NGINX 配置文件中的 js_include 替换为 js_import 并在 JavaScript 文件中添加 export 语句,以便 NGINX 配置可以引用相关函数。操作步骤如下:

  1. 编辑 NGINX 配置文件:

    • js_include 替换为 js_import,并记下隐式的 module_name(即该指令的 JavaScript 文件名参数,不包含 .js 扩展名)。

    • 在每个引用 JavaScript 函数的指令中,都将 module_name 用作函数名的前缀。这个函数名是以下这些指令的第一个参数:

      对于 js_set[HTTP][Stream] 指令来说,它是第二个参数。

    例如,将

    js_set $foo myFunction;

    更改为:

    js_set $foo module_name.myFunction;
  2. 编辑那些 NGINX 配置文件中引用的函数所在的 JavaScript(module_name.js) 文件。在所有这些文件中都添加export(如下所示),并指明那些被引用的函数。

    export default { myFunction, otherFunction }

    export 语句可以出现在 .js 文件中的任何位置,但通常习惯是将其直接放置于被引用的函数的上方或是文件的末尾。

弃用 Cookie-Flag 模块

NGINX Plus R23 在发布时已宣布弃用第三方 CookieFlag 模块,并宣布从该版本开始,模块库中不再提供该模块。

在升级到 NGINX Plus R26 之前,请修改您的 NGINX 配置,使用内置指令 proxy_cookie_flags 替换出现的 set_cookie_flag 指令(后者定义于已弃用的模块中)。

TLS 协商机制不再支持 NPN 协议

NGINX 建立 TLS 连接和 HTTP/2 连接的方式已经更新。作为 NGINX 和客户端(通常是浏览器)TLS 握手的一部分,它们会就在本次握手建立的会话中使用哪种通信协议进行协商(最常见的情况是,这种协商会将会话从 HTTP 1.x 升级到 HTTP/2)。TLS 的下一代协议协商 (Next Protocol Negotiation, NPN) 扩展是第一个用于此目的的方法,但 NPN 已经过时并被应用层协议协商 (Application‑Layer Protocol Negotiation, ALPN) 扩展所取代。

NGINX Plus R26 不再支持 NPN,因此客户端现在只能使用 ALPN。

此外,我们的 ALPN 实现已经扩展和加固 —— 请参阅“加固后的 TLS 握手”

旧 NGINX Plus 软件仓库不再更新

NGINX Plus R24 发布之时,所有 NGINX 软件的软件包仓库都进行了重组,这使得 NGINX Plus 安装步骤发生了改变。

安装或升级 NGINX Plus 时,操作系统的软件包管理器(aptyum 或类似管理器)是用 NGINX Plus 的软件仓库来进行配置的。

如果在使用 plus-pkgs.nginx.com repo (those running NGINX Plus R23 旧仓库(运行 NGINX Plus R23 或更早版本的仓库)的现有系统上升级到 NGINX Plus R26,您必须更新软件包管理器,以引用新的 pkgs.nginx.com/plus 仓库。请参阅 F5 知识库中的相关说明。

对于 NGINX Plus R26 的初始安装,请参阅《NGINX Plus 管理指南》中的 “安装 NGINX Plus” 部分。旧仓库中不会提供 NGINX Plus R26,也将不会再有后续更新。

对于适用于 NGINX Plus API 的 OpenAPI Spec 的获取方式变更

NGINX Plus 软件包将不再以 YAML 格式包括适用于 NGINX Plus API 的 OpenAPI specificationSwagger UI 您现在可以在《NGINX Plus 管理指南》中获取它们。

平台支持的变更

支持的全新操作系统和架构:

不再支持的旧版操作系统:

  • Alpine Linux 3.11(现支持的最早版本是 3.12)

已弃用并计划在 NGINX Plus R27 中移除的旧版操作系统和架构:

  • Power8 架构 (ppc64le)
  • CentOS 8.1+
  • Alpine Linux 3.12

新增特性详情

借助 JSON Web Key Set 缓存加快 JWT 验证

在验证 JSON Web Token 时,NGINX 使用 JSON Web Key Set (JWKS) 来验证令牌的签名或解密令牌。JWKS 可以存储在配置文件中,也可以通过 HTTP 请求从外部服务获取。此外,在内存中缓存 JWKS 具有以下几个优势:

  • 显著降低 CPU 使用率
  • 缩短请求延迟
  • 简化 JWT 验证——因为作为缓存过程的一部分,JWKS 密钥从 JSON 转换为了一种二进制格式,这种格式针对加密操作进行了优化

要将 JWKS 缓存到内存中,需要添加新的 auth_jwt_key_cache 指令,并为每个密钥集指定过期时间(在本例中为 3 小时):

如果从外部服务器获取 JWK,我们还建议配置标准内容缓存并添加 proxy_cache_use_stale 指令。在后台刷新过期的 JWKS 时,该指令将告知 NGINX Plus 继续为过期的 JWKS 提供服务。

在 JWKS 缓存的基础上配置内容缓存有以下两个好处:

  • 弹性 —— 即使 JWKS 已过期,也可以从缓存中被检索出来。这样做会在 JWKS 的提供机制暂时无法使用时提高弹性,但同时也需要权衡增加的安全风险。

  • 对授权服务器的影响 —— 缓存的 JWKS 过期会对身份验证服务器造成不同的影响,具体取决于 JWKS 缓存是单独使用还是与内容缓存一起使用:

    • 如果 JWKS 缓存单独使用,所有传入的授权请求都将转发到身份验证服务器,直到该缓存被过期的 JWKS 的新版本重新填充。如果身份验证服务器响应缓慢,则重复的对 JWKS 的 HTTP 请求可能会急速增加。这种额外的负载可能会导致身份验证服务不堪重负,从而让问题变得更加复杂。

    • 如果在有过期的 JWKS 的情况下启用内容缓存,则只有一个 JWKS 请求会被转发到身份验证服务器;随后的请求将排队等待,直到内容缓存填充完毕,NGINX 可以处理这些请求为止。这会降低对身份验证服务的需求(进而减少资源消耗)。

加固后的 TLS 握手

TLS 遭到的攻击(如 ALPACA)越来越多。我们始终致力于主动防御漏洞,为此,我们加强了 NGINX 对 TLS 连接的处理。

应用层协议协商 (ALPN) 是一种可选的 TLS 握手扩展,客户端和服务器在 TLS 握手期间使用它来选择七层协议,并在由握手建立的加密会话中使用该协议。ALPN 最常见的用例是协商从 HTTP/1.x 到 HTTP/2 的升级,并用于浏览器和 Web 或应用服务器之间的会话。

如果客户端通过 ALPN 提出使用的协议与正在建立的会话的 NGINX 配置上下文不匹配,最新版本的 NGINX Plus将会拒绝 TLS 握手。举例来说,http{} 上下文中定义的虚拟服务器需要一个用于 HTTP 的 ALPN 协议 ID,而 mail{} 上下文中的虚拟服务器则需要一个用于 SMTP、POP 或 IMAP 的协议 ID。

NGINX Plus R26 引入了 $ssl_alpn_protocol[HTTP][Stream] 变量来捕获协商的协议。( NGINX Plus R15 中的 stream{} 上下文引入的 $ssl_preread_alpn_protocols 变量仍然可捕获握手期间客户端发布的所有协议的列表。)

这段代码定义了 alpn 日志格式,它使用 $ssl_alpn_protocol 将协议添加到访问日志条目的 alpn= 字段。

stream{} 上下文中的新 ssl_alpn 指令定义了 NGINX Plus 可以接受的协议。删除该指令后,NGINX Plus 将考虑客户端提供的所有协议。

NGINX JavaScript 模块的增强功能

NGINX Plus R26 整合了 0.7.2 版 NGINX JavaScript 模块 (njs),并增强了两个功能:

注:本部分内容假设您已了解用于异步和加密操作的 JavaScript 构造。对代码段的完整分析不在本文的讨论范围内。

增强了对异步函数的支持

在许多常用的脚本语言(如 PHP)中,命令和函数是同步执行的 —— 也就是说,脚本调用函数后会暂停(停止执行),直到函数返回结果才会再次执行。

JavaScript 还可以异步操作:当异步调用函数时,脚本可连续执行,而不需要等待函数返回结果。

以下面的脚本为例:

它返回一个空值,原因是 njs 运行时的等待时间不会超过定义的超时时间(如果超过定义的超时时间,输出结果将是 b,a):

$ curl http://127.0.0.1/$ 

显然,正确处理异步操作对于获得预期结果至关重要。JavaScript 提供了许多方法来实现这一点,但在常见的 NGINX 用例中,人们通常希望以可实现执行流同步的方式将异步函数打包。这就需要使用 Promise 对象以及 asyncawait 关键字。

ECMAScript 6 (JavasScript 语言规范 ECMA262 的第六版)将 Promise 对象定义为异步函数的返回类型。它有三种状态:

  • 完成 —— 操作成功完成
  • 拒绝 —— 操作失败
  • 挂起 —— 初始状态(既未完成,也未拒绝)

使用关键字 async 定义 JavaScript 函数,会将函数的返回类型设置为 Promise。在写处理 Promise 对象的 njs 函数时,asyncawait 关键字非常重要。

以下面的脚本为例:

fs.readFile 函数(第 12 行)返回一个 Promise。它被封装在一个自定义的 async 函数中,以确保仅在文件名为 user.text 时才调用 fs.readFile()。由于包含 await 关键字,封装函数会等待 Promise 并返回数据。

fs.readFile() 封装到另一个函数中能够更方便地捕获错误;无论 async 函数中出现什么异常,Promise 的状态都会变成 rejected。另一种方法是将第 9 行替换为返回被拒绝 Promise 的语句:

您也可以直接使用 Promise 对象。在下面的示例中,Promise.resolve 函数分别为 p1p2 返回一个 PromisePromise.all 函数等待 p1p2Promise 解析完之后返回结果。

现在,curl 命令的输出结果就是我们想要的结果(请注意,超时值越小越好,所以将先返回 b):

$ curl http://127.0.0.1/b,a
$ 

使用 WebCrypto API 的新加密函数

NGINX JavaScript 现在可以通过 WebCrypto API 使用增强的加密功能。常见的 njs 加密用例包括:

  • 为会话 ID 生成安全的随机数
  • 加密和解密消息、数据和 cookie
  • 使用对称和非对称加密算法创建或验证数字签名

以下 njs 代码生成一个随机数:

以下 NGINX Plus 配置调用了 njs 代码:

该函数的输出结果是一个随机数,如下所示:

$ curl 127.0.0.123225320050,3668407277,1101267190,2061939102,2687933029,2361833213,32543985,4162087386

在开始使用安全随机数和 WebCrypto 时,WebCrypto 中的 getRandomValues 函数是一个不错的切入点。它的实现非常简单,函数将直接返回结果,而不是返回 Promise

然而,其他一些更密集的 WebCrypto 加密函数是异步操作的。举例来说,сrypto.subtle.digest() 的文档中写道:

生成给定数据的摘要。将要使用的摘要算法和要摘要的数据的标识符作为其参数。返回一个 Promise(将由摘要实现)。

因此,直接调用 сrypto.subtle.digest() 并不能保证其结果可以用于下一步操作,除非将其封装在一个 async 函数中。所以,此处我们使用 asyncawait 关键字将其封装在一个函数中,以确保哈希变量在函数返回之前填充了一个结果:

NGINX Plus 配置中的 js_set 指令使用 setReturnValue 函数(封装在 host_hash 函数中)返回的值填充 $hosthash 变量:

下面是一个散列主机名 example.com 的示例。

$ curl -H "Host: example.com" 127.1#
e8e624a82179b53b78364ae14d14d63dfeccd843b026bc8d959ffe0c39fc4ded1f4dcf4c8ebe871e657a12db6f11c3af87c9a1d4f2b096ba3deb56596f06b6f4

NGINX Plus R26 的其他增强功能

现已支持 IBM Z (s390x) 架构

现代应用在我们的日常生活中随处可见,基本支持组件(如 NGINX)是保障它们正常运行的关键所在。我们很高兴能够为搭载 CentOS 8.1+、RHEL 8.1+ 和 Ubuntu 20.04 的 IBM Z (s390x) 架构提供 NGINX Plus 支持。希望在现有大型机资产上托管现代应用的企业现在可以将 NGINX 和 NGINX Plus 部署为基于软件的 Web 服务器、负载均衡器、反向代理、内容缓存和 API 网关。

现已支持流模块 TCP 半封闭

新的 proxy_half_close 指令允许独立关闭每个方向的 TCP 连接,以提高 stream{} 上下文的效率。

现已支持 PCRE2 正则表达式库

以前的 NGINX Plus 版本使用 Perl Compatible Regular Expression (PCRE) library 库(版本 1)来计算 NGINX 配置中使用的正则表达式。这个重要的开源项目最近已经终结并已被 PCRE2 取代。NGINX Plus 现在支持 PCRE 和 PCRE2,可自动使用底层操作系统的可用版本。无需更改配置。

升级或试用 NGINX Plus

如果您是 NGINX Plus 用户,我们强烈建议您尽快升级到 NGINX Plus R26。除了上面提到的内容,您还将获得更多修复和改进,并且 NGINX 团队将会在您需要帮助时为您及时提供支持。

如果您还不是 NGINX Plus 用户,我们建议您立即申请试用——它具备强大的安全性,可充当负载均衡器、API 网关或者具备增强的监控和管理 API的 Web 服务器,并且 NGINX 团队将提供支持服务。请立即下载 30 天免费试用版


"This blog post may reference products that are no longer available and/or no longer supported. For the most current information about available F5 NGINX products and solutions, explore our NGINX product family. NGINX is now part of F5. All previous NGINX.com links will redirect to similar NGINX content on F5.com."