NGINX.COM
Web Server Load Balancing with NGINX Plus

很高兴宣布推出 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.1
23225320050,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 天免费试用版

Hero image
免费白皮书:
NGINX 企阅版全解析

助力企业用户规避开源治理风险,应对开源使用挑战

关于作者

Robert Haynes

技术营销经理

关于 F5 NGINX

F5, Inc. 是备受欢迎的开源软件 NGINX 背后的商业公司。我们为现代应用的开发和交付提供一整套技术。我们的联合解决方案弥合了 NetOps 和 DevOps 之间的横沟,提供从代码到用户的多云应用服务。访问 nginx-cn.net 了解更多相关信息。