Files
higress/plugins/wasm-go/extensions/nginx-rewrite-compatible/README.md
2026-05-14 11:24:58 +08:00

6.6 KiB
Raw Blame History

title, keywords, description
title keywords description
Nginx Rewrite 兼容迁移
higress
nginx
rewrite
set
migration
nginx rewrite + set 安全迁移插件说明

功能说明

nginx-rewrite-compatible 插件提供与 nginx rewrite + set 指令组合等价的常见能力,包括路径重写、查询串追加或替换、正则捕获组变量保存,以及通过请求头将变量传递给上游服务。

这个插件面向从 Nginx 迁移到 Higress 的场景,作为安全替代方案,避免继续依赖受 CVE-2026-42945 影响的重写链路。

安全背景

CVE-2026-42945 是一个与 Nginx rewriteset 指令组合相关的长期堆溢出问题,漏洞存在约 18 年。其触发条件集中在以下模式:

  1. rewrite 使用带 ? 的替换目标,在一次 rewrite pass 中同时修改 URI 和 query string。
  2. set 在后续步骤中继续引用前一次正则匹配得到的捕获组,如 $1$2
  3. 两次 pass 之间rewrite 状态和捕获组状态没有保持一致,导致后续 set 读取了不匹配的捕获组元数据,最终触发越界访问和堆溢出。

Higress 的 WASM 插件没有这个问题,原因是:

  1. 每个请求都在独立的 WASM 上下文中处理。
  2. 本插件在一次请求回调内完成“匹配、重写、变量保存、向上游透传”全过程,不依赖 Nginx rewrite module 的两阶段状态机。
  3. 捕获组结果只存在当前请求的内存和请求属性中,不会跨 pass 泄漏,也不会复用失配状态。

运行属性

插件执行阶段:默认阶段 插件执行优先级:100

配置字段

字段名 类型 必填 默认值 说明
rules array of object - 重写规则列表,按顺序执行

rules 配置说明

字段名 类型 必填 默认值 说明
regex string - 匹配请求 path 的正则表达式,不包含 query string
replacement string - 新的路径模板,支持 $1$2 等捕获组引用
query_append string - 追加到原 query string 的片段,支持 $1$2
query_template string - 完全替换原 query string 的模板,支持 $1$2
set_vars array of object - 将捕获组写入请求级变量,或按变量名前缀改写 query/header/cookie
pass_to_upstream bool false 是否把当前规则的变量同时写入请求头传给上游
mode string last 规则流转模式,支持 breaklast

说明:

  1. query_appendquery_template 不能同时配置。
  2. mode: break 表示命中当前规则后停止继续匹配后续规则。
  3. mode: last 表示命中当前规则后,使用重写后的 path 继续匹配后续规则。
  4. set_vars 中,arg_ 前缀会修改请求 query parameterhttp_ 前缀会修改请求 headercookie_ 前缀会修改请求 cookie其他变量名会通过 proxywasm.SetProperty([]string{"nginx_rewrite_compatible","vars",name}) 保存。
  5. http_ 前缀对应的 header 名称会去掉前缀,并把下划线转换成横杠。
  6. pass_to_upstream: true 时,变量还会额外写入请求头 x-higress-rewrite-var-<name>

set_vars 配置说明

字段名 类型 必填 说明
name string 变量名。arg_/http_/cookie_ 前缀分别表示写 query parameter、header、cookie其他名称表示自定义属性
capture_group int 捕获组编号,0 表示整个匹配,1 表示第一个分组

Nginx 配置对照表

1. 简单路径重写

Nginx:

rewrite ^/old/(.*)$ /new/$1;

插件配置:

rules:
  - regex: ^/old/(.*)$
    replacement: /new/$1

2. 正则捕获组替换

Nginx:

rewrite ^/product/([0-9]+)$ /detail/$1;

插件配置:

rules:
  - regex: ^/product/([0-9]+)$
    replacement: /detail/$1

3. Query String 操作

追加 query:

rewrite ^/api/(.*)$ /internal?migrated=true;
rules:
  - regex: ^/api/(.*)$
    replacement: /internal
    query_append: migrated=true

替换 query:

rewrite ^/x/(.*)/(.*)$ /y?a=$1&b=$2;
rules:
  - regex: ^/x/(.*)/(.*)$
    replacement: /y
    query_template: a=$1&b=$2

4. 特殊变量前缀

rules:
  - regex: "^/api/(.*)/(.*)$"
    replacement: "/internal"
    set_vars:
      - name: original_path
        capture_group: 1
      - name: http_x_original
        capture_group: 1
      - name: arg_source
        capture_group: 2
      - name: cookie_track_id
        capture_group: 1

语义:

  1. original_path 保存为请求属性,可供后续插件通过 GetProperty(["nginx_rewrite_compatible","vars","original_path"]) 读取。
  2. http_x_original 设置请求头 x-original
  3. arg_source 设置 query parameter source
  4. cookie_track_id 设置 cookie track_id

5. 变量保存与传递

Nginx:

rewrite ^/api/(.*)$ /internal?migrated=true;
set $original_endpoint $1;

插件配置:

rules:
  - regex: ^/api/(.*)$
    replacement: /internal
    query_append: migrated=true
    set_vars:
      - name: original_endpoint
        capture_group: 1
    pass_to_upstream: true

6. 多规则组合

Nginx:

rewrite ^/stage/(.*)$ /mid/$1;
rewrite ^/mid/(.*)$ /final/$1;

插件配置:

rules:
  - regex: ^/stage/(.*)$
    replacement: /mid/$1
    mode: last
  - regex: ^/mid/(.*)$
    replacement: /final/$1

7. break / last 控制

Nginx break:

rewrite ^/stage/(.*)$ /mid/$1 break;
rules:
  - regex: ^/stage/(.*)$
    replacement: /mid/$1
    mode: break

Nginx last:

rewrite ^/stage/(.*)$ /mid/$1 last;
rewrite ^/mid/(.*)$ /final/$1;
rules:
  - regex: ^/stage/(.*)$
    replacement: /mid/$1
    mode: last
  - regex: ^/mid/(.*)$
    replacement: /final/$1

使用示例

rules:
  - regex: ^/api/(.*)$
    replacement: /internal
    query_append: migrated=true
    set_vars:
      - name: original_endpoint
        capture_group: 1
      - name: http_x_original_endpoint
        capture_group: 1
      - name: arg_source
        capture_group: 1
      - name: cookie_track_id
        capture_group: 1
    pass_to_upstream: true
    mode: break

  - regex: ^/old/(.*)$
    replacement: /new/$1

  - regex: ^/x/(.*)/(.*)$
    replacement: /y
    query_template: a=$1&b=$2
    set_vars:
      - name: first
        capture_group: 1
      - name: second
        capture_group: 2