mirror of
https://github.com/alibaba/higress.git
synced 2026-05-22 11:47:27 +08:00
256 lines
6.6 KiB
Markdown
256 lines
6.6 KiB
Markdown
---
|
||
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
|
||
```
|