junkurihara/rust-rpxy-l4
GitHub: junkurihara/rust-rpxy-l4
一个用 Rust 编写的高性能四层反向代理,支持在单端口上对多种 TCP/UDP 协议进行多路复用和智能路由。
Stars: 115 | Forks: 7
# rpxy-l4:一个用 Rust 编写的、带有协议多路复用器的四层(TCP+UDP)反向代理
[](LICENSE)



[](https://hub.docker.com/r/jqtype/rpxy-l4)
## 简介
`rpxy-l4` 是一个支持 TCP 和 UDP 协议的四层反向代理,其设计理念与 [`rpxy`](https://github.com/junkurihara/rust-rpxy)(HTTP 反向代理)相同。它使用 Rust 编写,旨在为四层协议提供高性能且易于使用的反向代理。
## 功能
- **基础的 L4 反向代理功能**:`rpxy-l4` 可以将 TCP 和 UDP 数据包转发到后端服务器。
- **协议多路复用**:`rpxy-l4` 可以在同一个端口上对 TCP/UDP 上的多个协议进行多路复用,这意味着 `rpxy-l4` 会将特定协议路由到相应的后端服务器。目前支持以下协议:
- TCP:HTTP(明文)、TLS、SSH、Socks5
- UDP:QUIC(IETF QUIC[^quic])、WireGuard
- **负载均衡**:`rpxy-l4` 可以基于几种简单的负载均衡算法,将传入的连接分发到多个后端服务器。
- **协议净化**:利用协议多路复用特性,`rpxy-l4` 可以对传入的数据包进行净化,通过禁止默认路由,来防止客户端和后端服务器之间的 TCP/UDP 上的协议不匹配。(直接丢弃不符合预期协议的数据包)。
- **TLS/QUIC 转发器**:`rpxy-l4` 可以根据 Server Name Indication (SNI) 和 Application Layer Protocol Negotiation (ALPN) 的值,将 TLS/IETF QUIC 流转发到相应的后端服务器。
- **支持 HAProxy PROXY protocol**:`rpxy-l4` 支持 TCP 的出站 PROXY protocol(在后端连接前添加 header,全局/按协议配置)和入站 PROXY protocol(解析来自受信任上游代理的 header)。(需要启用 `proxy-protocol` Cargo 特性,该特性默认已启用。)
- **[实验性] TLS Encrypted Client Hello (ECH) 代理**:`rpxy-l4` 可以作为代理[^ech_proxy] 来处理带有 IETF-Draft Encrypted Client Hello 的 TLS/QUIC 流。换言之,`rpxy-l4` 会托管 ECH 私钥并解密经过 ECH 加密的 Client Hello,从而将流路由到对应的后端服务器。
[^quic]: 不是 Google QUIC。同时支持 QUIC v1([RFC9000](https://datatracker.ietf.org/doc/html/rfc9000), [RFC9001](https://datatracker.ietf.org/doc/html/rfc9001)) 和 QUIC v2 ([RFC9369](https://datatracker.ietf.org/doc/html/rfc9369))。
[^ech_proxy]: 在 [ECH Split Mode](https://www.ietf.org/archive/id/draft-ietf-tls-esni-24.html#section-3) 语境下面向客户端的服务器
## 安装说明
### 从源码构建
你可以通过检出这个 Git 仓库来自己构建可执行二进制文件。
```
# 克隆 git repository
% git clone https://github.com/junkurihara/rust-rpxy-l4
% cd rust-rpxy-l4
# 构建
% cargo build --release
```
然后你会得到一个可执行二进制文件 `rust-rpxy-l4/target/release/rpxy-l4`。
如果要在不包含 PROXY protocol 特性的情况下构建:
```
% cargo build --release --no-default-features
```
### Linux 包安装(RPM/DEB)
你可以在 [./.build](./.build) 目录中找到 `rpxy-l4` 的 Jenkins CI/CD 构建脚本。
由 [@Gamerboy59](https://github.com/Gamerboy59) 提供的预编译 Linux RPM 和 DEB 软件包可在 [https://rpxy.gamerboy59.dev](https://rpxy.gamerboy59.dev) 获取。
## 用法
`rpxy-l4` 总是会引用一个 TOML 格式的配置文件,例如 `config.toml`。你可以在这个仓库中找到配置文件的示例 `config.example.toml`。
你可以像下面这样使用配置文件运行 `rpxy-l4`
```
% ./target/release/rpxy-l4 --config config.toml
```
`rpxy-l4` 始终以实时方式跟踪 `config.toml` 的变更,并立即应用更改而无需重启进程。
完整的命令行选项如下:
```
% ./target/release/rpxy-l4 --help
Usage: rpxy-l4 [OPTIONS] --config
Options:
-c, --config Configuration file path like ./config.toml
-l, --log-dir Directory for log files. If not specified, logs are printed to stdout.
-h, --help Print help
-V, --version Print version
```
如果你设置了 `--log-dir=`,日志文件将在指定的目录中创建。否则,日志将输出到 stdout。
- `${log_dir}/access.log` 用于访问日志
- `${log_dir}/rpxy-l4.log` 用于系统和错误日志
## 基本配置
### 1. 第一步:基础的 TCP/UDP 反向代理场景
以下是 TCP/UDP 反向代理场景的基础配置示例。
```
# Listen port,必须设置
listen_port = 8448
# TCP 连接的默认目标。[default: empty]
# Format: [":", ":", ...]
tcp_target = ["192.168.0.2:8000"]
# UDP 连接的默认目标。[default: empty]
# Format: [":", ":", ...]
udp_target = ["192.168.0.3:4000"]
```
上述配置的工作方式如下。
- 将端口 `8448` 上接收到的 TCP 数据包转发到后端服务器 `192.168.0.2:8000`;
- 将端口 `8448` 上接收到的 UDP 数据包转发到后端服务器 `192.168.0.3:4000`。
### 2. 第二步:负载均衡
`rpxy-l4` 允许你基于几种简单的负载均衡算法,将传入的 TCP/UDP 数据包分发到多个后端服务器。对于多个 TCP/UDP 目标,你可以如下设置负载均衡算法。
```
# Listen port,必须设置
listen_port = 8448
# TCP 连接的默认目标。[default: empty]
# Format: [":", ":", ...]
tcp_target = ["192.168.0.2:8000", "192.168.0.3:8000"]
# 默认目标的 load balancing method [default: none]
tcp_load_balance = "source_ip" # source_ip, source_socket, random, or none
# UDP 连接的默认目标。[default: empty]
# Format: [":", ":", ...]
udp_target = ["192.168.0.2:4000", "192.168.0.3:4000"]
# (可选)默认目标的 load balancing method [default: none]
udp_load_balance = "source_socket"
```
目前,`rpxy-l4` 支持以下负载均衡算法:
- `source_ip`:基于源 IP 哈希
- `source_socket`:基于源 IP 和端口哈希
- `random`:随机选择
- `none`:始终使用第一个目标 [默认]
### 3. 第三步:协议多路复用
以下是 TCP/UDP 上协议多路复用场景的示例/用例。对于协议多路复用,你需要在配置文件中设置 `[protocols.]` 字段,如下所示。
```
listen_port = 8448
...
# 为每个 multiplexed service 设置
[protocols."http_service"]
...
```
目前,`rpxy-l4` 支持以下用于多路复用的协议:
- TCP:HTTP(明文)、TLS、SSH、Socks5
- UDP:QUIC(IETF [RFC9000](https://datatracker.ietf.org/doc/html/rfc9000))、WireGuard
#### 3.1. 带有 SNI/ALPN 的 TLS/QUIC 多路复用器示例
`rpxy-l4` 可以通过探测 TLS ClientHello 消息和 IETF QUIC Initial 数据包(包含 ClientHello)来检测并对 TLS/QUIC 流进行多路复用。以下示例演示了将任何 TLS/QUIC 流转发到与默认目标不同的相应后端的场景。
```
listen_port = 8448
tcp_target = ["192.168.0.2:8000"]
udp_target = ["192.168.0.3:4000"]
# TLS
[protocols."tls_service"]
# protocol 名称 tls|ssh|socks5|http|wireguard|quic
protocol = "tls"
# 检测为 TLS 的连接的目标。
target = ["192.168.0.5:443"]
# (可选)特定于此连接的 load balancing method [default: none]
load_balance = "source_ip"
#####################
# IETF QUIC
[protocols."quic_service"]
# protocol 名称 tls|ssh|socks5|http|wireguard|quic
protocol = "quic"
# 检测为 QUIC 的连接的目标。
target = ["192.168.0.6:443"]
# QUIC 连接的 load balancing method [default: none]
load_balance = "source_socket"
# QUIC 连接的空闲存活时间(秒) [default: 30]
idle_lifetime = 30
```
此外,你可以在 `protocol="tls"` 或 `protocol="quic"` 的情况下设置 `tls_alpn` 和 `tls_sni` 字段。这些是 TLS/QUIC 多路复用器的附加过滤器,用于根据 Application Layer Protocol Negotiation (ALPN) 和 Server Name Indication (SNI) 的值将流路由到相应的后端服务器。这意味着只有带有指定 ALPN 和 SNI 值的流才会被转发到目标。
```
[protocols."tls_service"]
protocol = "tls"
target = ["192.168.0.5:443"]
load_balance = "source_ip"
# (可选)基于 SNI 的 TLS/QUIC 连接路由。
# 如果指定,则只有匹配给定 SNI 的 TLS/QUIC 连接才会被转发到目标。
# Format: ["", "", ...]
server_names = ["example.com", "example.org"]
# (可选)基于 ALPN 的 TLS/QUIC 连接路由。
# 如果指定,则只有匹配给定 ALPN 的 TLS/QUIC 连接才会被转发到目标。
# Format: ["", "", ...]
alpns = ["h2", "http/1.1"]
```
#### 3.2. WireGuard 多路复用器示例
`rpxy-l4` 可以通过探测初始握手数据包来检测并对 WireGuard 数据包进行多路复用。以下示例同样演示了将任何 WireGuard 数据包转发到与默认目标不同的相应后端的场景。
```
[protocols."wireguard_service"]
protocol = "wireguard"
target = ["192.168.0.10:51820"]
load_balance = "none"
# 长于 wireguard tunnel 的 keepalive interval
idle_lifetime = 30
```
#### 3.3. 仅放行预期协议(协议净化)
这在某种程度上是一项安全功能,旨在防止客户端和后端服务器之间在 TCP/UDP 上的协议不匹配。*通过忽略默认路由*,即移除顶层的 `tcp_target` 和 `udp_target`,并仅设置特定的协议多路复用器,`rpxy-l4` 将只处理匹配预期协议的数据包并丢弃其他数据包。
### 4. PROXY protocol(保留客户端 IP)
`rpxy-l4` 支持 TCP 连接的 [HAProxy PROXY protocol](https://www.haproxy.org/download/2.9/doc/proxy-protocol.txt)(v1 和 v2)。这使得当 `rpxy-l4` 处于代理链中时能够保留客户端 IP.
#### 4.1. 出站:向后台服务器发送 PROXY header
在发往后端服务器的连接前添加一个 PROXY protocol header,以便后端可以看到原始客户端 IP。
**全局设置** — 适用于所有 TCP 后端连接:
```
listen_port = 8448
tcp_target = ["192.168.0.2:8000"]
# 为所有 TCP backend 连接预置 PROXY protocol v2 header
tcp_send_proxy_protocol = "v2" # "v1", "v2", or omit/"none" to disable
```
**按协议覆盖** — 每个协议条目都可以覆盖全局设置:
```
tcp_send_proxy_protocol = "v2" # global default
[protocols."tls_1"]
protocol = "tls"
target = ["192.168.0.5:443"]
send_proxy_protocol = "v1" # override: use v1 for this protocol
[protocols."http_1"]
protocol = "http"
target = ["192.168.0.6:80"]
send_proxy_protocol = "none" # override: disable for this protocol
```
#### 4.2. 入站:从上游代理解析 PROXY header
当 `rpxy-l4` 位于发送 PROXY protocol 的负载均衡器或代理(例如 AWS NLB、HAProxy)之后时,启用入站解析以从 header 中提取原始客户端 IP。
```
listen_port = 8448
tcp_target = ["192.168.0.2:8000"]
# 在每个 TCP 连接上预期入站 PROXY header
tcp_recv_proxy_protocol = true
# 受信任且允许发送 PROXY header 的来源(启用 recv 时必填)
tcp_trusted_proxies = ["10.0.0.0/8", "192.168.0.0/16"]
```
- `tcp_recv_proxy_protocol = true` 要求**所有**传入的 TCP 连接都以 PROXY header 开头。没有有效 header 的连接将被拒绝。
- 启用接收(recv)时,`tcp_trusted_proxies` 是**强制配置项**。来自不受信任源 IP 的连接将被拒绝。如果此字段缺失或为空,启动验证将失败。
- 自动检测 v1 和 v2 header —— 无需进行版本配置。
- 接受 LOCAL (v2) / UNKNOWN (v1) 命令(例如健康检查),而不修改源地址。
#### 4.3. 端到端客户端 IP 保留
当入站和出站同时启用时,`rpxy-l4` 即可通过代理链实现端到端的客户端 IP 保留:
```
Client → [LB with PROXY protocol] → rpxy-l4 (inbound parse → outbound send) → Backend
```
入站解析器提取原始客户端 IP,出站编码器将其转发给后端。除了同时启用这两者之外,不需要任何特殊配置。
### 5. 进阶:实验性功能
#### 5.1. TLS Encrypted Client Hello (ECH) 代理
有关 ECH 代理配置以及客户端和后端服务器的示例,请参阅 [./examples/README.md](./examples/README.md)。
## 容器化
容器、docker 镜像可在 Docker Hub 和 Github Container Registry 上获取。
- Docker Hub: [jqtype/rpxy-l4](https://hub.docker.com/r/jqtype/rpxy-l4)
- Github Container Registry: [ghcr.io/junkurihara/rust-rpxy-l4](https://ghcr.io/junkurihara/rust-rpxy-l4)
有关容器的详细配置可以在 [./docker](./docker) 目录中找到。
## 注意事项
### `UDP` 伪连接管理
如前所述,`rpxy-l4` 基于套接字地址管理来自每个客户端的 UDP 数据包的伪连接。同时,`rpxy-l4` 通过探测初始/握手数据包来识别特定的协议。这意味着如果伪连接的空闲生存时间太短,且客户端发送数据包的间隔较长,那么即使在通信期间,伪连接也会被移除。随后,来自客户端的数据包(即非初始/握手数据包)将*不会被路由到特定协议的目标,而是被路由到默认目标(如果没有默认目标则会被丢弃)*。为了避免这种情况,你应当将基于 UDP 的协议多路复用器的 `idle_lifetime` 值设置为长于客户端发送数据包的间隔。
### Encrypted Client Hello (ECH) 代理
#### 功能受限
*目前我们并未完全实现 [IETF draft](https://www.ietf.org/archive/id/draft-ietf-tls-esni-24.html#section-7.1) 中描述的面向客户端的服务器功能。* 它以下述*简化和缩减*的方式运行,这与草案不同:
- 如果找不到与给定 ECH 匹配的配置,它将按原样将 client hello 直接转发给后端服务器。
- `rpxy-l4` 不支持面向客户端的服务器的重试机制,即它目前没有关于 ECH 请求的状态,并且不会处理、转发或发送 `HelloRetryRequest` 消息给客户端。
#### 不支持 ECH over QUIC
ECH 代理功能仅限于 TLS 协议,目前尚不支持 ECH over QUIC。
### 其他
待定!
## 许可证
`rpxy-l4` 是免费的开源软件,基于 MIT License 授权。
## 安全
如果你发现了安全漏洞,**请不要公开 Issue**。
请使用 [GitHub 私密漏洞报告](../../security/advisories/new) 来通知维护者。
## 贡献
欢迎贡献(issues、功能请求、bug 报告、pull requests)。
请注意,本项目主要基于代码所有者的个人兴趣进行维护,不受任何商业协议的支持。
我们会在能力范围内尽力处理贡献。同时也欢迎赞助,以帮助维持该项目的运转。
有关贡献指南和项目范围的更多详细信息,请参阅 [CONTRIBUTING.md](./CONTRIBUTING.md)。
标签:Python安全, Rust, TCP, UDP, 内核驱动, 协议复用, 反向代理, 可视化界面, 底层编程, 网络协议, 网络流量审计, 请求拦截, 负载均衡, 通知系统