很高兴宣布推出 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 模块的增强功能 —— 现在已经支持那些包含了
async
和await
关键字以及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 配置可以引用相关函数。操作步骤如下:
编辑 NGINX 配置文件:
将
js_include
替换为js_import
,并记下隐式的 module_name(即该指令的 JavaScript 文件名参数,不包含 .js 扩展名)。在每个引用 JavaScript 函数的指令中,都将 module_name 用作函数名的前缀。这个函数名是以下这些指令的第一个参数:
例如,将
js_set $foo myFunction;
更改为:
js_set $foo module_name.myFunction;
编辑那些 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 时,操作系统的软件包管理器(apt
、yum
或类似管理器)是用 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 specification 和 Swagger UI 您现在可以在《NGINX Plus 管理指南》中获取它们。
平台支持的变更
支持的全新操作系统和架构:
- Alpine Linux 3.15 (x86_64, arm64)
- IBM Z (s390x) 平台,搭载 CentOS 8.1+、RHEL 8.1+ 和 Ubuntu 20.04;请参阅“支持 IBM Z s390x 架构”
不再支持的旧版操作系统:
- 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),并增强了两个功能:
- 通过
async
和await
关键字及在 ECMAScript 6 中引入的Promise
对象,增强了对异步函数支持 - 实现了 WebCrypto API
注:本部分内容假设您已了解用于异步和加密操作的 JavaScript 构造。对代码段的完整分析不在本文的讨论范围内。
增强了对异步函数的支持
在许多常用的脚本语言(如 PHP)中,命令和函数是同步执行的 —— 也就是说,脚本调用函数后会暂停(停止执行),直到函数返回结果才会再次执行。
JavaScript 还可以异步操作:当异步调用函数时,脚本可连续执行,而不需要等待函数返回结果。
以下面的脚本为例:
它返回一个空值,原因是 njs 运行时的等待时间不会超过定义的超时时间(如果超过定义的超时时间,输出结果将是 b,a
):
$ curl http://127.0.0.1/$
显然,正确处理异步操作对于获得预期结果至关重要。JavaScript 提供了许多方法来实现这一点,但在常见的 NGINX 用例中,人们通常希望以可实现执行流同步的方式将异步函数打包。这就需要使用 Promise
对象以及 async
和 await
关键字。
ECMAScript 6 (JavasScript 语言规范 ECMA262 的第六版)将 Promise
对象定义为异步函数的返回类型。它有三种状态:
- 完成 —— 操作成功完成
- 拒绝 —— 操作失败
- 挂起 —— 初始状态(既未完成,也未拒绝)
使用关键字 async
定义 JavaScript 函数,会将函数的返回类型设置为 Promise
。在写处理 Promise
对象的 njs 函数时,async
和 await
关键字非常重要。
以下面的脚本为例:
fs.readFile
函数(第 12 行)返回一个 Promise
。它被封装在一个自定义的 async
函数中,以确保仅在文件名为 user.text 时才调用 fs.readFile()
。由于包含 await
关键字,封装函数会等待 Promise
并返回数据。
将 fs.readFile()
封装到另一个函数中能够更方便地捕获错误;无论 async
函数中出现什么异常,Promise
的状态都会变成 rejected
。另一种方法是将第 9 行替换为返回被拒绝 Promise
的语句:
您也可以直接使用 Promise
对象。在下面的示例中,Promise.resolve
函数分别为 p1
和 p2
返回一个 Promise
。Promise.all
函数等待 p1
和 p2
的 Promise
解析完之后返回结果。
现在,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
函数中。所以,此处我们使用 async
和 await
关键字将其封装在一个函数中,以确保哈希变量在函数返回之前填充了一个结果:
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 天免费试用版。