什么是透明代理
透明代理的核心理念是让被代理的设备 无感知 地使用代理服务。也就是说, 被代理的设备无需运行任何代理软件 (如 Clash、V2RayNG、Shadowrocket 等),只要连接到网络, 流量就会自动经过代理。
这意味着, 代理软件运行在其他设备上, 比如路由器中。这样, 所有通过该路由器上网的设备都会被自动代理。
在本文中, 我们需要一台 Linux 主机 连接到 路由器的 LAN 口, 使其作为局域网中的透明代理网关, 为局域网内的所有设备提供透明代理功能。
为什么需要透明代理?
透明代理适用于以下情况:
- 局域网设备较多: 例如 办公室、实验室、大型家庭等, 不想手动为每台设备配置代理
- 设备无法设置代理: 例如 Chromecast、电视盒子、Apple TV、HomePod 等智能设备
哪些设备可以作为透明代理网关?
任何能够运行 Linux 的设备或者 24 小时开机的设备都可以充当透明代理网关, 例如:
- 路由器: (软路由、OpenWrt、LEDE、爱快等)
- 开发板: (树莓派、香橙派等)
- 个人电脑: (Linux 虚拟机、J1900 软路由、普通 PC、笔记本电脑)
- NAS 设备
- 电视盒子: (Apple TV)
至于到底用什么?具体选择取决于需求。
- 如果只是 随便折腾 学习技术, ARM 架构的树莓派是个不错的选择
- 如果追求 长期稳定运行, 建议使用 x86 软路由
当然, 到底怎么选择还是得看自己。
透明代理的实现原理 (iptables)
Linux 使用 Netfilter 来管理网络流量, 进行数据包过滤和路由管理, 其数据包处理流程如下:
数据包流向解析
假设使用主路由器作为网关 (即我们平时的上网方式), 数据包的流向如下:
-
局域网设备访问互联网 (正常上网流量):
PREROUTING 链 -> FORWARD 链 -> POSTROUTING 链
-
局域网设备访问路由器本身 (如登录 Web 管理界面、SSH、DNS 请求):
PREROUTING 链 -> INPUT 链 -> 网关系统
-
路由器本身访问互联网 (例如网关自身的 DNS 查询、系统更新、路由器安装软件包等):
网关系统 -> OUTPUT 链 -> POSTROUTING 链
如何实现透明代理?
我们可以使用 firewall-cmd
来控制 PREROUTING 和 OUTPUT 链, 将匹配的数据包转发至 V2Ray。这样, 局域网设备和网关本身的数据包都可以通过 V2Ray 进行处理。
准备条件
在搭建透明代理前, 你需要具备以下条件:
- 正常运行的主路由, 可以使用 RouterOS 等
- 抗污染 DNS, 如 DoH、DoT、DNSCrypt, 避免域名解析被污染
- 已经安装好 CentOS 7 系统的设备, 用于搭建透明代理网关
- 静态 IP 配置, 网关设备 IP 必须是固定的
- 使用 Firewalld 管理 Netfilter (本文不直接使用 iptables 或 nftables)
- 了解基础的 TCP/IP 知识 (如 TCP/IP、DNS、NAT、路由)
- 能手动编辑 V2Ray JSON 配置文件 (至少能看懂配置逻辑)
- 有解决问题的能力, 遇到问题时能自行根据实际情况解决问题。不可使用预制人
主路由分流策略
主路由使用 BGP 协议按照 ASN (自治系统编号) 进行流量分流。进入透明代理网关的流量全部走代理, 网关本身不再做 IP 级别的分流或域名分流。
旁路由系统选择
OpenWrt / LEDE 作为路由器系统, 拥有易用的 Web 管理界面 (LuCI) 和丰富的生态, 使其广受好评。但另一方面, 也许它也只能作为「路由器系统」了, 对于旁路由或 x86 软路由, 它的局限性较大, 与标准的 Linux 发行版功能上相比差距太大了。
为什么我之前选择 LEDE 呢?当时讨论的最多的就是 爱快 (iKuai) 主路由 + LEDE 旁路由 方案。考虑到它是专门打造的路由器系统, 界面美观还有丰富的软件中心, 和原版 OpenWrt 内核优化上或许有些许不同, 所以当时选择了 LEDE。然而, 随着时间推移, 软件长期缺乏维护, bug 越来越多, 甚至很多软件因不可抗力被下架, 已经无法正常使用。
因此, 搭建旁路由, 建议使用 CentOS / Ubuntu / Debian 等标准的 Linux 发行版, 适合在软路由、工控机、虚拟机等设备上作为透明代理网关, 长期运行也更加稳定。
透明代理的核心难点
透明代理的最大难点在于 路由, 即如何正确分流:
- 哪些流量应直连?
- 哪些流量应走代理?
- 如何避免数据包在网关内无限循环?
透明代理的搭建阶段
我们可以把路由分流, 由易到难分为以下几个阶段:
- 第一阶段: 代理所有进入网关的流量 (最简单)
- 第二阶段: 在 1 的基础上, 优化路由
- 局域网 IP、组播地址、E 类地址、广播地址, 直连
- 其他流量走代理
- 第三阶段: 在 2 的基础上, 优化网关自身的流量
- 让网关自身流量也能走代理
- V2Ray 出站流量不再进入代理 (避免回环)
- 第四阶段: 在 3 的基础上, 二次分流优化代理
- 根据域名将流量分配到不同出口
设置网关
-
连接网络
- 将透明代理网关 (准备运行 V2Ray 的设备) 连接到主路由器的 LAN 口
- 手动分配静态 IP, 假设网关设备的 IP 地址为
192.168.2.2
-
开启 IP 转发
需要开启 IP 转发才能作为网关, 我们这里仅开启 Linux 的 IPv4 转发 功能
echo "net.ipv4.ip_forward=1">> /etc/sysctl.conf sysctl -p
执行后应出现以下提示, 表示成功开启 IP 转发
net.ipv4.ip_forward = 1
-
配置防火墙规则
由于
firewalld
防火墙默认可能会阻止转发流量, 我们需要手动添加规则# 其中 ens192 为你的网卡名称, 请根据实际情况更改 (使用 ip a 查看网卡名称) firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 1 -o ens192 -j ACCEPT
执行完毕后, 重新加载防火墙配置, 使规则生效
# 因为有 --permanent 参数, 需要重新加载配置才能生效 firewall-cmd --reload
验证规则是否已生效
# 可以使用这个命令查看已经生效的规则 firewall-cmd --direct --get-all-rules
-
手动配置局域网设备网络
在局域网中的设备上手动设置网关, 网关地址:
192.168.2.2
(即透明代理网关的 IP)
配置完成后, 局域网设备应当可以正常上网, 但由于还没设置代理, “正常” 是指可以访问国内网站。访问外网仍不可用, 后续需要进一步配置透明代理规则, 使国际流量走 V2Ray 代理。
安装配置 V2Ray
-
启用 TProxy 监听 12345 端口
- V2Ray 需要在透明代理模式 (TProxy) 下工作, 因此首先要开启 dokodemo-door 监听 12345 端口, 用于接收 TCP / UDP 流量并进行代理转发
-
启用 SOCKS5 代理, 监听 1080 端口
- 为了测试 V2Ray 是否正常运行, 我们在
1080
端口开启一个 SOCKS5 代理。此端口仅用于测试, 后续可以删除
- 为了测试 V2Ray 是否正常运行, 我们在
我们由易到难, 不写路由 (routing), 写两个入站 (inbound) 但只写一个出站 (outbound)
{
"log": {
"loglevel": "info",
"access": "/home/v2ray/log_access.log",
"error": "/home/v2ray/log_error.log"
},
"inbounds": [
{
"port": 12345,
"protocol": "dokodemo-door",
"settings": {
"network": "tcp,udp",
"followRedirect": true
},
"streamSettings": {
"sockopt": {
"tproxy": "tproxy"
}
}
},
{
"port": 1080,
"protocol": "socks",
"listen": "0.0.0.0",
"settings": {
"auth": "noauth",
"udp": true,
"ip": null
}
}
],
"outbounds": [
{
// 你的服务器配置
}
]
}
测试 V2Ray 是否正常运行
配置完成后, 启动 V2Ray 并执行以下命令, 检查是否可以正常访问外网
curl -so /dev/null -w "%{http_code}" --connect-timeout 10 google.com -x socks5://127.0.0.1:1080
测试结果
- 结果返回
301
或200
: 表示 V2Ray 代理正常工作, 可以访问外网 - 长时间无响应或返回
000
: 表示 V2Ray 配置可能有问题, 无法访问外网
第一阶段: 代理所有进入网关的流量
配置防火墙规则。首先, 将所有 PREROUTING
链的流量转发到 v2ray 中
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100
# 创建自定义 V2RAY 链
firewall-cmd --direct --add-chain ipv4 mangle V2RAY
# mark 只有设置为 1, 数据包才能被 v2ray 任意门接受
# 给 TCP 打标记 1, 转发至 12345 端口
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 1 -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1
# 给 UDP 打标记 1, 转发至 12345 端口
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 2 -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
# 将 PREROUTING 数据包转发到 V2RAY 链
firewall-cmd --direct --add-rule ipv4 mangle PREROUTING 1 -j V2RAY
执行完上述命令后, 透明代理无法正常上网。如果使用 SSH 连接到网关, 你会发现 SSH 连接被断开了 (不用紧张)
可以通过以下方法恢复:
-
直接断电重启, 系统重启后规则会被清除
-
重启防火墙, 会移除所有临时规则
systemctl restart firewalld
-
手动删除
PREROUTING
规则firewall-cmd --direct --remove-rule ipv4 mangle PREROUTING 1 -j V2RAY
无法访问互联网的异常情况分析
V2Ray 的日志 (access.log) 可能会出现许多源地址为网关 (192.168.2.1) 请求目标地址为透明网关 LAN 口 IP (192.168.2.2) 的异常回环请求。
2024/02/03 00:52:55 192.168.2.1:53 accepted udp:192.168.2.2:56276 [blocked]
2024/02/03 00:52:55 192.168.2.1:53 accepted udp:192.168.2.2:35645 [blocked]
2024/02/03 00:52:55 192.168.2.1:53 accepted udp:192.168.2.2:32808 [blocked]
2024/02/03 00:52:55 192.168.2.1:53 accepted udp:192.168.2.2:60001 [blocked]
2024/02/03 00:52:55 192.168.2.1:53 accepted udp:192.168.2.2:38648 [blocked]
2024/02/03 00:52:55 192.168.2.1:53 accepted udp:192.168.2.2:40134 [blocked]
2024/02/03 00:52:55 192.168.2.1:53 accepted udp:192.168.2.2:44284 [blocked]
2024/02/03 00:52:55 192.168.2.1:53 accepted udp:192.168.2.2:39017 [blocked]
2024/02/03 00:52:55 192.168.2.1:53 accepted udp:192.168.2.2:59267 [blocked]
2024/02/03 00:52:55 192.168.2.1:53 accepted udp:192.168.2.2:45138 [blocked]
2024/02/03 00:52:56 192.168.2.1:53 accepted udp:192.168.2.2:44694 [blocked]
2024/02/03 00:52:56 192.168.2.1:53 accepted udp:192.168.2.2:33505 [blocked]
2024/02/03 00:53:12 192.168.2.100:63890 accepted tcp:17.248.216.65:443 [us4]
2024/02/03 00:53:26 192.168.2.100:63892 accepted tcp:17.57.145.151:443 [us4]
2024/02/03 00:54:06 192.168.2.100:63894 accepted tcp:17.57.145.137:443 [us4]
这些异常回环请求被我设置的 v2ray 路由规则拦截了, 如果没有拦截则会出现 CPU 负载增高
{
"outboundTag": "blocked",
"type": "field",
"ip": [
"geoip:private"
]
}
从理论上讲, 网关自身访问互联网只会经过 OUTPUT
链和 POSTROUTING
链, 为什么操控 PREROUTING
链会导致透明网关无法上网呢?
这是因为网络通讯是双向的, 虽然网关自身访问互联网不需要经过 PREROUTING
链, 但被访问的服务器返回的响应数据时需要经过 PREROUTING
链, 而这部分流量被转发到 v2ray 了, 因此出现了日志中的回环请求, 导致连接异常。
例如, DNS 请求和回复流程如下
# 网关自身向互联网请求 DNS 解析
192.168.2.2:44284 -> 192.168.2.1:53
# DNS 解析结果被错误地转发到 v2ray
192.168.2.1:53 -> 192.168.2.2:44284
优化规则
为避免上述问题, 我们修改一下防火墙规则, 使源 IP 不是来自局域网的请求则返回 (不转发至 v2ray)
重启防火墙, 临时规则就被清空了, 执行以下指令:
[root@gateway ~]# systemctl restart firewalld
[root@gateway ~]# firewall-cmd --direct --get-all-rules
ipv4 filter FORWARD 1 -o ens192 -j ACCEPT
修改后的防火墙规则
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100
# 创建自定义 V2RAY 链
firewall-cmd --direct --add-chain ipv4 mangle V2RAY
# "网关LAN_IP地址段" 通过运行命令 "ip address | grep -w "inet" | awk '{print $2}'" 获得, 是其中的一个
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 1 ! -s 网关LAN_IP地址段 -j RETURN
# 标记 TCP 和 UDP 流量
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 2 -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 3 -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
# 将 PREROUTING 数据包转发到 V2RAY 链
firewall-cmd --direct --add-rule ipv4 mangle PREROUTING 1 -j V2RAY
执行完上述命令后, SSH 仍然会断开, 但透明代理已经可用。为了让网关正常上网, 我们需要手动修改透明网关系统 DNS (/etc/resolv.conf
), 设置为公共 DNS (如 119.29.29.29)。因为现在主路由网关访问不了, 所以 DNS 设置为主路由网关是不行的。
至此, 第一阶段就完成了。
第二阶段: 使主路由网关流量直连
目前仍然无法访问主路由网关。原因是防火墙规则代理了全部流量, 包括访问本地主路由网关的流量。试想在 VPS 上访问你本地的主路由网关, 肯定是访问不了的, 所以我们要对这部分流量直连。为了让本地主路由网关直连, 需进一步修改防火墙规则。
重启防火墙后, 执行以下指令:
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100
# 创建自定义 V2RAY 链
firewall-cmd --direct --add-chain ipv4 mangle V2RAY
# 直连所有目标地址属于网关所在网段的流量
# 通过运行命令 "ip address | grep -w "inet" | awk '{print $2}'" 获取网关网段, 一般来说有多个
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 1 -d 网关所在网段1 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 2 -d 网关所在网段2 -j RETURN
...
# 允许组播地址 / E 类地址 / 广播地址直连
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 3 -d 224.0.0.0/3 -j RETURN
# 标记 TCP 和 UDP 流量
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 4 -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 5 -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
# 将 PREROUTING 数据包转发到 V2RAY 链
firewall-cmd --direct --add-rule ipv4 mangle PREROUTING 1 -j V2RAY
使用第二阶段的规则后, 第一阶段中的以下规则便成为了多余规则, 可以删去:
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 1 ! -s 网关LAN_IP地址段 -j RETURN
主路由器网关已经可以正常访问, SSH 连接不会断开, 同时透明代理也能正常工作。
至此, 第二阶段完成。
第三阶段: 网关自身的透明代理
通常, 我们的 DNS 解析来源于主路由器, 但当前的防火墙规则仅代理了局域网中的设备, 并未代理透明网关自身。
这会导致:
- DNS 查询返回错误或受到污染
- 在透明网关自身下载 GitHub 文件资源时会下载失败
- 访问国际互联网时返回错误
这有两个解决方案:
- 搭建抗污染 DNS, 如 DoH、DoT、DNSCrypt, 避免域名解析被污染
- 使用策略路由代理透明网关自身
策略路由
由于 iptables-tproxy
不支持直接操作 OUTPUT
链, 我们可以通过 策略路由 的方式, 将 OUTPUT
链中的相关数据包重新路由至 PREROUTING
链, 从而实现网关自身的透明代理。
# 添加策略路由: 标记为 1 的包, 走路由表 100
ip rule add fwmark 1 table 100
# 添加路由表 100 的路由条目, 使所有包路由到本地
ip route add local 0.0.0.0/0 dev lo table 100
上述配置完成后, 我们只需要在 OUTPUT
链上给需要代理的包标记为 1
, 由于 Netfilter 的特性, 在 OUTPUT
链上打标记会使这些数据包被重路由到 PREROUTING
链上, 从而被现有的透明代理规则处理。
给 OUTPUT
链的流量打标记
firewall-cmd --direct --add-chain ipv4 mangle GATEWAY_ITSELF
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 5 -j MARK --set-mark 1
firewall-cmd --direct --add-rule ipv4 mangle OUTPUT 1 -p tcp -j GATEWAY_ITSELF
firewall-cmd --direct --add-rule ipv4 mangle OUTPUT 2 -p udp -j GATEWAY_ITSELF
这样, OUTPUT
链上的 TCP 和 UDP 数据包都会被标记为 1
, 并通过策略路由重新进入 PREROUTING
链, 从而实现网关自身流量的透明代理。
解决代理自身的回环问题
如果代理透明网关自身发出的全部请求, 就会引入一个新问题。由于透明网关自身运行着代理软件 (如 v2ray),如果不进行特殊处理, v2ray 向代理服务器发送的请求会被再次代理, 形成回环。因此, 我们需要在代理规则中排除 v2ray 的流量。
方案一: 直连 VPS 流量
最简单的方法是直连目标地址为 VPS 的流量, 避免它被代理。
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100
firewall-cmd --direct --add-chain ipv4 mangle V2RAY
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 1 -d 网关所在网段1 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 2 -d 网关所在网段2 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 3 -d 224.0.0.0/3 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 4 -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 5 -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
# 将 PREROUTING 数据包转发到 V2RAY 链
firewall-cmd --direct --add-rule ipv4 mangle PREROUTING 1 -j V2RAY
# 代理透明网关自身
firewall-cmd --direct --add-chain ipv4 mangle GATEWAY_ITSELF
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 1 -d 网关所在网段1 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 2 -d 网关所在网段2 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 3 -d 224.0.0.0/3 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 4 -d VPS公网ip/32 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 5 -j MARK --set-mark 1
firewall-cmd --direct --add-rule ipv4 mangle OUTPUT 1 -p tcp -j GATEWAY_ITSELF
firewall-cmd --direct --add-rule ipv4 mangle OUTPUT 2 -p udp -j GATEWAY_ITSELF
但是这样配置有个缺点, 如果出站的域名使用了 CDN 或者 VPS 很多的话, 就需要手动维护大量规则, 不够灵活。
方案二: 基于 mark
规则排除 v2ray 出站数据包
在 v2ray 配置文件中, 为出站 (outbound) 的 streamSettings
添加 SO_MARK
, 使其数据包可以被 netfilter
识别并绕过代理。
方案二也有 缺点
- 可能会有未知流量进入
PREROUTING
链, 导致tproxy
负载过高。tproxy 透明代理占用大量 CPU - 该方案在安卓系统上不可用, 因为安卓有自己的
mark
机制
v2ray 配置示例。在所有 outbound
的 streamSettings
中添加 SO_MARK
{
"inbounds": [],
"outbounds": [
{
"tag": "proxy",
"protocol": "vmess",
"settings": {
"vnext": [
// 你的服务器配置
]
},
"streamSettings": {
"sockopt": {
// 这里是 SO_MARK, 用于 netfilter 识别, 每个 outbound 都要配置
// 255 可以改成其他数值, 但要与下面的 firewalld 规则对应
// 如果有多个 outbound, 最好将所有 outbound 的 SO_MARK 都设置成一样的数值
"mark": 255
}
},
"mux": {
"enabled": true
}
},
{
"tag": "direct",
"protocol": "freedom",
"settings": {},
"streamSettings": {
"sockopt": {
// 这里是 SO_MARK, 用于 netfilter 识别, 每个 outbound 都要配置
// 255 可以改成其他数值, 但要与下面的 firewalld 规则对应
// 如果有多个 outbound, 最好将所有 outbound 的 SO_MARK 都设置成一样的数值
"mark": 255
}
}
},
{
"tag": "blocked",
"protocol": "blackhole",
"settings": {}
}
],
"routing": {}
}
防火墙规则
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100
firewall-cmd --direct --add-chain ipv4 mangle V2RAY
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 1 -d 网关所在网段1 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 2 -d 网关所在网段2 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 3 -d 224.0.0.0/3 -j RETURN
# 直连 SO_MARK 为 0xff (255) 的流量, 防止 v2ray 进入代理回环
# 0xff 是 16 进制数, 数值上等同与上面 v2ray 配置的 255
# 此规则目的是解决 v2ray 占用大量 CPU (https://github.com/v2ray/v2ray-core/issues/2621)
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 4 -m mark --mark 0xff -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 5 -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 6 -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
# 将 PREROUTING 数据包转发到 V2RAY 链
firewall-cmd --direct --add-rule ipv4 mangle PREROUTING 1 -j V2RAY
# 代理透明网关自身
firewall-cmd --direct --add-chain ipv4 mangle GATEWAY_ITSELF
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 1 -d 网关所在网段1 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 2 -d 网关所在网段2 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 3 -d 224.0.0.0/3 -j RETURN
# 直连 SO_MARK 为 0xff (255) 的流量, 防止 v2ray 进入代理回环
# 0xff 是 16 进制数, 数值上等同与上面 v2ray 配置的 255
# 此规则目的是避免透明网关自身流量出站回环问题
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 4 -m mark --mark 0xff -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 5 -j MARK --set-mark 1
firewall-cmd --direct --add-rule ipv4 mangle OUTPUT 1 -p tcp -j GATEWAY_ITSELF
firewall-cmd --direct --add-rule ipv4 mangle OUTPUT 2 -p udp -j GATEWAY_ITSELF
方案三: 基于 UID/GID 规避代理 (推荐)
tproxy 流量只能被 root (uid=0
) 权限用户或具备 CAP_NET_ADMIN
权限的用户接收, 在使用 v2ray 用户运行 v2ray 时需要保证分配了 CAP_NET_ADMIN
权限。
只需要在 v2ray.service
中加入以下内容
[Service]
User=v2ray
Group=v2ray
PIDFile=/run/v2ray.pid
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
NoNewPrivileges=true
ExecStart=/home/v2ray/bin/v2ray run -config /home/v2ray/v2ray.json
Restart=on-failure
# Don't restart in the case of configuration error
RestartPreventExitStatus=23
我们可以通过 uid
或 gid
排除 v2ray 进程的流量:
-m owner --gid-owner overseas
所有overseas
分组运行的程序流量-m owner --uid-owner v2ray
所有v2ray
用户运行的程序流量
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100
firewall-cmd --direct --add-chain ipv4 mangle V2RAY
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 1 -d 网关所在网段1 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 2 -d 网关所在网段2 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 3 -d 224.0.0.0/3 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 4 -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 5 -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
# 将 PREROUTING 数据包转发到 V2RAY 链
firewall-cmd --direct --add-rule ipv4 mangle PREROUTING 1 -j V2RAY
# 代理透明网关自身
firewall-cmd --direct --add-chain ipv4 mangle GATEWAY_ITSELF
# 用户 v2ray 的流量直连
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 1 -m owner --uid-owner v2ray -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 2 -d 网关所在网段1 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 3 -d 网关所在网段2 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 4 -d 224.0.0.0/3 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 5 -j MARK --set-mark 1
firewall-cmd --direct --add-rule ipv4 mangle OUTPUT 1 -p tcp -j GATEWAY_ITSELF
firewall-cmd --direct --add-rule ipv4 mangle OUTPUT 2 -p udp -j GATEWAY_ITSELF
这样就成功实现了 网关自身的透明代理, 也就是平时说的全局代理。但需要注意将透明网关的 DNS 服务器设置为国外的 DNS 服务器, 否则依然会返回被污染的解析结果。
至此, 第三阶段完成。
第四阶段: 二次分流优化代理
对于大多数用户来说, 第三阶段的全局代理已经能够满足日常需求, 并不是所有人都需要实现第四阶段。
第四阶段是在主路由已经完成了海外 IP 的分流, 透明网关还可以 二次分流, 可以根据不同应用的需求, 将流量更精准地路由到最佳的代理服务器, 从而提升可用性、访问速度和稳定性。
通过 Sniffing 进行多出口分流
开启 sniffing 功能后, V2Ray 可以根据域名进行智能分流、负载均衡等。例如
域名 | 服务器 |
---|---|
Netflix | 香港 大流量 |
Disney+ | 美国 解锁 Star 频道 |
OpenAI / ChatGPT | 美国 ChatGPT 解锁线路 |
Telegram | 日本 低延迟 CN2 GIA |
套 Cloudflare 的 PT 域名 | 直连 |
替换默认路由规则文件
为了获得更好的分流体验, 应使用 Loyalsoldier/v2ray-rules-dat 作为路由规则文件。否则, V2Ray 将无法加载此配置文件。
下载最新的 geoip.dat
和 geosite.dat
规则文件
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat -O /home/v2ray/bin/geoip.dat
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat -O /home/v2ray/bin/geosite.dat
配置 v2ray.json
进行域名和 IP 分流
修改 V2Ray 配置文件 /home/v2ray/v2ray.json
, 在 routing
部分添加相应的分流规则
{
"inbounds": [],
"outbounds": [],
"routing": {
"domainMatcher": "mph",
"domainStrategy": "IpIfNonMatch",
"rules": [
{
"type": "field",
"outboundTag": "hk",
"domains": [
"geosite:netflix"
]
},
{
"type": "field",
"outboundTag": "us1",
"domains": [
"geosite:disney",
"geosite:reddit"
]
},
{
"type": "field",
"balancerTag": "us2",
"domains": [
"geosite:spotify"
]
},
{
"type": "field",
"outboundTag": "us3",
"domains": [
"geosite:openai"
]
},
{
"type": "field",
"outboundTag": "jp1",
"ip": [
"geoip:telegram"
]
},
{
"type": "field",
"outboundTag": "direct",
"domains": [
// 套 Cloudflare 的 PT 域名
]
},
{
"type": "field",
"outboundTag": "direct",
"ip": [
// 其它 Cloudflare IP 直连
"geoip:cloudflare"
]
},
{
"type": "field",
"ip": [
"geoip:private"
],
"outboundTag": "blocked"
}
]
}
}
测试 V2Ray 配置是否正确
完成配置后, 运行以下命令检查配置文件是否正确
[root@gateway ~]# /home/v2ray/bin/v2ray test -config /home/v2ray/v2ray.json
V2Ray 5.12.1 (V2Fly, a community-driven edition of V2Ray.) Custom (go1.21.4 linux/amd64)
A unified platform for anti-censorship.
Configuration OK.
如果显示 Configuration OK.
, 说明配置文件正确, 可以正常启动。
至此, 第四阶段完成。
开机自动运行透明代理规则
在测试配置没有问题后, 我们可以让路由规则添加删除自动化, 使用 systemd 来管理路由规则脚本, 使其在开机时自动添加, 并在需要维护规则时可以自动清除。
创建 systemd
服务文件
该配置的作用是确保 tproxy-rule.sh
在网络启动后执行, 并在关闭时清除相应的规则
新建 /etc/systemd/system/tproxy-rule.service
文件, 并添加以下内容
[Unit]
Description=V2Ray Tproxy Rule
After=network.target
Wants=network.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/home/v2ray/tproxy-rule.sh start
ExecStop=/home/v2ray/tproxy-rule.sh stop
[Install]
WantedBy=multi-user.target
创建 tproxy-rule.sh
路由脚本
tproxy-rule.sh
脚本负责添加和删除 TProxy 相关规则, 以确保透明代理的正常工作。
新建 /home/v2ray/tproxy-rule.sh
并添加以下内容
#!/bin/bash
do_start() {
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100
firewall-cmd --direct --add-chain ipv4 mangle V2RAY
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 1 -d 网关所在网段1 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 2 -d 网关所在网段2 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 3 -d 224.0.0.0/3 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 4 -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 5 -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
firewall-cmd --direct --add-rule ipv4 mangle PREROUTING 1 -j V2RAY
# 代理透明网关自身
firewall-cmd --direct --add-chain ipv4 mangle GATEWAY_ITSELF
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 1 -m owner --uid-owner v2ray -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 2 -d 网关所在网段1 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 3 -d 网关所在网段2 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 4 -d 224.0.0.0/3 -j RETURN
firewall-cmd --direct --add-rule ipv4 mangle GATEWAY_ITSELF 5 -j MARK --set-mark 1
firewall-cmd --direct --add-rule ipv4 mangle OUTPUT 1 -p tcp -j GATEWAY_ITSELF
firewall-cmd --direct --add-rule ipv4 mangle OUTPUT 2 -p udp -j GATEWAY_ITSELF
}
do_stop() {
ip rule del fwmark 1 table 100
ip route del local 0.0.0.0/0 dev lo table 100
firewall-cmd --direct --remove-rule ipv4 mangle V2RAY 1 -d 网关所在网段1 -j RETURN
firewall-cmd --direct --remove-rule ipv4 mangle V2RAY 2 -d 网关所在网段2 -j RETURN
firewall-cmd --direct --remove-rule ipv4 mangle V2RAY 3 -d 224.0.0.0/3 -j RETURN
firewall-cmd --direct --remove-rule ipv4 mangle V2RAY 4 -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1
firewall-cmd --direct --remove-rule ipv4 mangle V2RAY 5 -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
firewall-cmd --direct --remove-rule ipv4 mangle PREROUTING 1 -j V2RAY
firewall-cmd --direct --remove-chain ipv4 mangle V2RAY
# 代理透明网关自身
firewall-cmd --direct --remove-rule ipv4 mangle GATEWAY_ITSELF 1 -m owner --uid-owner v2ray -j RETURN
firewall-cmd --direct --remove-rule ipv4 mangle GATEWAY_ITSELF 2 -d 网关所在网段1 -j RETURN
firewall-cmd --direct --remove-rule ipv4 mangle GATEWAY_ITSELF 3 -d 网关所在网段2 -j RETURN
firewall-cmd --direct --remove-rule ipv4 mangle GATEWAY_ITSELF 4 -d 224.0.0.0/3 -j RETURN
firewall-cmd --direct --remove-rule ipv4 mangle GATEWAY_ITSELF 5 -j MARK --set-mark 1
firewall-cmd --direct --remove-rule ipv4 mangle OUTPUT 1 -p tcp -j GATEWAY_ITSELF
firewall-cmd --direct --remove-rule ipv4 mangle OUTPUT 2 -p udp -j GATEWAY_ITSELF
firewall-cmd --direct --remove-chain ipv4 mangle GATEWAY_ITSELF
}
case "$1" in
start)
do_start
exit $?
;;
stop)
do_stop
exit $?
;;
*)
echo "Usage: ${0} {start|stop}"
exit 2
;;
esac
设置脚本权限
chmod +x /home/v2ray/tproxy-rule.sh
设置开机启动并检查运行状态
systemctl enable tproxy-rule
systemctl start tproxy-rule
systemctl status tproxy-rule
firewall-cmd --direct --get-all-rules
常见问题
透明代理的其它注意事项
如果 透明网关 作为 主路由, 需要在 PREROUTING
链的规则中额外添加一条规则, 即在第一阶段使用, 第二阶段删除的命令。避免被其他人蹭用你的代理。
firewall-cmd --direct --add-rule ipv4 mangle V2RAY 1 ! -s 网关LAN_IP地址段 -j RETURN
如果不添加此规则, WAN 口中 同网段的其他设备 可以将网关填写成你的 WAN IP, 从而蹭用你的透明代理, 甚至可能带来一定的危险性。
解决 too many open files 问题
对于 UDP 透明代理, 比较容易出现 “卡住” 的情况。这个时候细心的朋友可能会在 日志 中发现大量 too many open files
相关错误信息, 这说明受到了 最大文件描述符 数值的限制。把这个数值往大调就好了。
修改 systemd 服务配置
编辑 /etc/systemd/system/v2ray.service
文件, 在 [Service]
下添加
LimitNPROC=500
LimitNOFILE=500000
修改后的配置如下
[Unit]
Description=V2Ray Service
Documentation=https://www.v2fly.org/
After=network.target nss-lookup.target
[Service]
User=v2ray
Group=v2ray
PIDFile=/run/v2ray.pid
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
NoNewPrivileges=true
ExecStart=/home/v2ray/bin/v2ray run -config /home/v2ray/v2ray.json
Restart=on-failure
# Don't restart in the case of configuration error
RestartPreventExitStatus=23
LimitNPROC=500
LimitNOFILE=500000
[Install]
WantedBy=multi-user.target
重载配置后重启 v2ray
systemctl daemon-reload
systemctl restart v2ray
检查文件描述符限制是否生效
检查 V2Ray 进程的 最大文件打开数 是否设置成功, 服务端和客户端都要检查
在服务端和客户端分别执行以下命令
cat /proc/$(pgrep -u v2ray v2ray)/limits
或者手动替换 V2Ray 进程的 PID
进行查询
# cat /proc/v2ray的pid/limits
cat /proc/3812/limits
找到 Max open files
应该显示 500000
, 表示文件描述符限制已成功提升。
[root@gateway ~]# cat /proc/3812/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 500 500 processes
Max open files 500000 500000 files
Max locked memory 8388608 8388608 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 15612 15612 signals
Max msgqueue size 819200 819200 bytes
Max nice priority 0 0
Max realtime priority 0 0
Max realtime timeout unlimited unlimited us
原文
透明代理入门
透明代理(TPROXY)
透明代理通过 gid 规避 Xray 流量
透明代理
Unable to get iptables owner module (gid-owner) to work
Red Hat Linux 8.0: The Official Red Hat Linux Reference Guide - Chapter 13. Firewalls and iptables
Linux Netfilter and Traffic Control
Firewalld
systemd.service — Service unit configuration
systemd - archlinux: RemainAfterExit
Systemd: how to start service after another service started
第一篇万字长文: 围绕透明代理的又一次探究