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

256 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Nginx Rewrite 兼容迁移
keywords: [higress, nginx, rewrite, set, migration]
description: nginx rewrite + set 安全迁移插件说明
---
## 功能说明
`nginx-rewrite-compatible` 插件提供与 `nginx rewrite + set` 指令组合等价的常见能力,包括路径重写、查询串追加或替换、正则捕获组变量保存,以及通过请求头将变量传递给上游服务。
这个插件面向从 Nginx 迁移到 Higress 的场景,作为安全替代方案,避免继续依赖受 `CVE-2026-42945` 影响的重写链路。
## 安全背景
`CVE-2026-42945` 是一个与 Nginx `rewrite``set` 指令组合相关的长期堆溢出问题,漏洞存在约 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` | 规则流转模式,支持 `break``last` |
说明:
1. `query_append``query_template` 不能同时配置。
2. `mode: break` 表示命中当前规则后停止继续匹配后续规则。
3. `mode: last` 表示命中当前规则后,使用重写后的 path 继续匹配后续规则。
4. `set_vars` 中,`arg_` 前缀会修改请求 query parameter`http_` 前缀会修改请求 header`cookie_` 前缀会修改请求 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:
```nginx
rewrite ^/old/(.*)$ /new/$1;
```
插件配置:
```yaml
rules:
- regex: ^/old/(.*)$
replacement: /new/$1
```
### 2. 正则捕获组替换
Nginx:
```nginx
rewrite ^/product/([0-9]+)$ /detail/$1;
```
插件配置:
```yaml
rules:
- regex: ^/product/([0-9]+)$
replacement: /detail/$1
```
### 3. Query String 操作
追加 query:
```nginx
rewrite ^/api/(.*)$ /internal?migrated=true;
```
```yaml
rules:
- regex: ^/api/(.*)$
replacement: /internal
query_append: migrated=true
```
替换 query:
```nginx
rewrite ^/x/(.*)/(.*)$ /y?a=$1&b=$2;
```
```yaml
rules:
- regex: ^/x/(.*)/(.*)$
replacement: /y
query_template: a=$1&b=$2
```
### 4. 特殊变量前缀
```yaml
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:
```nginx
rewrite ^/api/(.*)$ /internal?migrated=true;
set $original_endpoint $1;
```
插件配置:
```yaml
rules:
- regex: ^/api/(.*)$
replacement: /internal
query_append: migrated=true
set_vars:
- name: original_endpoint
capture_group: 1
pass_to_upstream: true
```
### 6. 多规则组合
Nginx:
```nginx
rewrite ^/stage/(.*)$ /mid/$1;
rewrite ^/mid/(.*)$ /final/$1;
```
插件配置:
```yaml
rules:
- regex: ^/stage/(.*)$
replacement: /mid/$1
mode: last
- regex: ^/mid/(.*)$
replacement: /final/$1
```
### 7. break / last 控制
Nginx `break`:
```nginx
rewrite ^/stage/(.*)$ /mid/$1 break;
```
```yaml
rules:
- regex: ^/stage/(.*)$
replacement: /mid/$1
mode: break
```
Nginx `last`:
```nginx
rewrite ^/stage/(.*)$ /mid/$1 last;
rewrite ^/mid/(.*)$ /final/$1;
```
```yaml
rules:
- regex: ^/stage/(.*)$
replacement: /mid/$1
mode: last
- regex: ^/mid/(.*)$
replacement: /final/$1
```
## 使用示例
```yaml
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
```