目标
- 配置 v2ray 使其接受透明代理的流量
- 配置 iptables 将所有 tcp 和 udp 53 的流量转发给 v2ray
- OpenWrt 配置 v2ray 服务
V2ray 配置
v2ray 的配置如下:
{
"log": {
"access": "",
"error": "",
"loglevel": "warning"
},
"policy": {
"levels": {
"0": {
"uplinkOnly": 0,
"downlinkOnly": 0,
"connIdle": 150,
"handshake": 4
}
}
},
"inbounds": [
{
"port": 1088,
"listen": "0.0.0.0",
"protocol": "http",
"settings": {
"userLevel": 0,
"auth": "noauth",
"udp": true,
"ip": "127.0.0.1"
},
"streamSettings": {
"sockopt": {
"mark": 255
}
}
},
{
"port": "1099",
"listen": "0.0.0.0",
"protocol": "dokodemo-door",
"settings": {
"userLevel": 0,
"network": "tcp,udp",
"timeout": 30,
"followRedirect": true
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"]
}
}
],
"outbounds": [
{
"mux": {
"enabled": false,
"concurrency": 8
},
"protocol": "vmess",
"tag": "default",
"settings": {
"vnext": [
{
"address": "127.0.0.1",
"users": [
{
"id": "a994b3c1-c7cc-4868-8072-c93e491bba0b",
"alterId": 64,
"level": 0,
"security": "aes-128-gcm"
}
],
"port": 10086
}
]
}
},
{
"protocol": "freedom",
"settings": {},
"tag": "direct",
"streamSettings": {
"sockopt": {
"mark": 255
}
}
}
],
"dns": {
"servers": ["8.8.8.8", "8.8.4.4", "localhost"]
},
"routing": {
"strategy": "rules",
"domainStrategy": "IPIfNonMatch",
"settings": {
"rules": [
{
"type": "field",
"ip": ["geoip:private"],
"outboundTag": "direct"
},
{
"type": "field",
"ip": ["geoip:cn"],
"outboundTag": "direct"
},
{
"type": "field",
"domain": ["geosite:cn"],
"outboundTag": "direct"
}
]
}
}
}
配置里有几个重点要说下,第一个是 dokudemo-door 的配置:
{
"port": "1099",
"listen": "0.0.0.0",
"protocol": "dokodemo-door",
"settings": {
"userLevel": 0,
"network": "tcp,udp",
"timeout": 30,
"followRedirect": true
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"]
}
}
不要忘了添加sniffing
的配置,这个配置是为了从流量中提取 ip 和 domain 信息,这样针对 ip 和 domain 的路由规则才能生效。
第二个是要给所有的outbound
都打上 mark 的配置:
"streamSettings": {
"sockopt": {
"mark": 255
}
}
这样 iptables 才能区分 v2ray 流量和非 v2ray 流量,非 v2ray 流量会被转发给 v2ray,v2ray 流量就直接从路由器发出去了。这样就避免了死循环,后面 iptables 规则的时候还会提到。
接下来就可以启动 v2ray 测试了
./v2ray -config client_proxy.json
通过 http 的inbound
来测试下隧道
curl -Is -x 127.0.0.1:1088 https://www.google.com
没问题就可以配置 iptables 了。
Iptables 配置
先说明一点,Linux 内核的包处理框架是 Netfilter,而 iptables 只是 userspace 的工具而已,但是多年来大家叫 iptables 其实多数都是指的 Netfilter,只是习惯了。
Iptables 这块的挑战比较大,我一路试错过来,总结来说有以下几点:
- 要理解 iptables 的各个表中的链的先后顺序
- 要捕捉其他设备过来的 tcp 流量
- 要捕捉本机发起的 tcp 流量
- 要捕捉其他设备过来的 udp 53 流量,也就是 DNS 流量
- 要捕捉本机发起的 DNS 流量
Netfilter 数据包流程图
从这张图中我们可以看出对于其他设备过来的流量都应该在PREROUTING
这个链来做,而对于本机发出的流量应该在OUTPUT
这个链来做。但由于重定向 tcp 和 udp 流量在实现上有区别,分别用到了 iptables 里的REDIRECT
和TPROXY
两种技术。参考这篇博客所说,是因为 ss-redir 应用没有实现 UDP REDIRECT 相关的代码,当然我也把 UDP 全都通过REDIRECT
转发给了 v2ray 结果也不行,所以 UDP 转发的部分还是通过TPROXY
来实现的。
REDIRECT vs TPROXY
REDIRECT
其实是 DNAT 的一种特殊形式,特殊在其把数据包的目标 IP 改成了 127.0.0.1,端口改成了--to-ports
参数指定的本地端口,这样本机的透明代理程序就能处理这个包,应用能通过内核的状态信息拿到被改写之前的目标 IP 和端口号,具体参考这里
TPROXY
比REDIRECT
新的特性,它能做到不修改数据包,应用只需一点改动就能实现REDIRECT
所有的功能,内核文档里有如下说明:
Transparent proxying often involves "intercepting" traffic on a router. This is usually done with the iptables REDIRECT target; however, there are serious limitations of that method. One of the major issues is that it actually modifies the packets to change the destination address -- which might not be acceptable in certain situations. (Think of proxying UDP for example: you won't be able to find out the original destination address. Even in case of TCP getting the original destination address is racy.)
从这段说明里似乎 UDP 并没有内核状态来记录更改前的 IP 地址,这与这篇博客所说所说的有些矛盾,我目前的理解还是 UDP 在内核没有状态记录。TPROXY
得以实现归结为三个要点:
- 将流量重定向到本地路由
- 路由规则定义去向
- 代理程序监听,通过特殊的参数可以响应非本机的 IP(因为包的目的地址没改嘛)
重定向 TCP 流量
新建一个 nat 链,排除私网地址流量
iptables -t nat -N V2RAY
# Ignore your V2Ray outbound traffic
# It's very IMPORTANT, just be careful.
iptables -t nat -A V2RAY -p tcp -j RETURN -m mark --mark 0xff
# Ignore LANs and any other addresses you'd like to bypass the proxy
# See Wikipedia and RFC5735 for full list of reserved networks.
iptables -t nat -A V2RAY -d 0.0.0.0/8 -j RETURN
iptables -t nat -A V2RAY -d 10.0.0.0/8 -j RETURN
iptables -t nat -A V2RAY -d 127.0.0.0/8 -j RETURN
iptables -t nat -A V2RAY -d 169.254.0.0/16 -j RETURN
iptables -t nat -A V2RAY -d 172.16.0.0/12 -j RETURN
iptables -t nat -A V2RAY -d 192.168.0.0/16 -j RETURN
iptables -t nat -A V2RAY -d 224.0.0.0/4 -j RETURN
iptables -t nat -A V2RAY -d 240.0.0.0/4 -j RETURN
# Anything else should be redirected to Dokodemo-door's local port
iptables -t nat -A V2RAY -p tcp -j REDIRECT --to-ports 1099
注意这里有一个关键规则iptables -t nat -A V2RAY -p tcp -j RETURN -m mark --mark 0xff
,这个规则就是为了排除 v2ray 要发出去的流量,没有这个规则的话就成死循环了,v2ray 要发出去的流量又被重定向给了 v2ray。
然后分别在PREROUTING
和OUTPUT
连个链里应用我们新建的V2RAY
链,前者是为了重定向其他设备过来的 TCP 流量,后者是重定向本机发出的 TCP 流量。
# apply redirect for traffic forworded by this proxy
iptables -t nat -A PREROUTING -p tcp -j V2RAY
# apply redirect for proxy itself
iptables -t nat -A OUTPUT -p tcp -j V2RAY
重定向 UDP 流量
这块要复杂一些,先新建一个 mangle 链,匹配 UDP 流量,然后应用TPROXY
target,同时打上特定的 mark
# UDP Redirect
iptables -t mangle -N V2RAY
iptables -t mangle -A V2RAY -p udp -j RETURN -m mark --mark 0xff
iptables -t mangle -A V2RAY -p udp --dport 53 -j TPROXY --on-port 1099 --tproxy-mark 0x01/0x01
注意这里也有一个关键规则iptables -t mangle -A V2RAY -p udp -j RETURN -m mark --mark 0xff
目的和 TCP REDIRECT 里的一样,避免死循环。
然后配置策略路由,按 mark 匹配流量,将流量路由到本机回环接口。
# add route for udp traffic
ip route add local default dev lo table 100
ip rule add fwmark 1 lookup 100
注意,这条路由规则的类型是local
,我的理解内核把TPEOXY
和路由关联起来了
最后就是,把这条链应用到PREROUTING
链里,这样就能重定向其他设备过来的 UDP 流量了。
iptables -t mangle -A PREROUTING -j V2RAY
好像还没有完,我们还没有重定向本机发出的 UDP 流量,这也是我目前的一个困惑点。先说我的做法吧,我再 mangle 表的OUTPUT
链里添加了如下两条规则:
iptables -t mangle -N V2RAY_MARK
iptables -t mangle -A V2RAY_MARK -p udp -j RETURN -m mark --mark 0xff
iptables -t mangle -A V2RAY_MARK -p udp --dport 53 -j MARK --set-mark 1
iptables -t mangle -A OUTPUT -j V2RAY_MARK
第一条规则仍然是排除 v2ray 自己的流量,第二条是给 UDP 数据包打上了 mark,而只是打上 mark 怎么就出发上面的重定向规则嘞?目前我的比较粗浅的理解就是上面数据包流程图里 mangle 的OUTPUT
链会触发 reroute check,也就让数据包重新从PREROUTING
链走了一遍。
OpenWrt 集成
将 v2ray 作为 OpenWrt 跑到时候需要安装一些依赖包
opkg update
opkg install bash kmod-ipt-tproxy iptables-mod-tproxy bind-dig
编写 iptables 操作脚本
#!/bin/bash
# -*- coding: utf-8 -*-
start() {
# TCP Redirect
# Create new chain
iptables -t nat -N V2RAY
# Ignore your V2Ray outbound traffic
# It's very IMPORTANT, just be careful.
iptables -t nat -A V2RAY -p tcp -j RETURN -m mark --mark 0xff
# Ignore LANs and any other addresses you'd like to bypass the proxy
# See Wikipedia and RFC5735 for full list of reserved networks.
iptables -t nat -A V2RAY -d 0.0.0.0/8 -j RETURN
iptables -t nat -A V2RAY -d 10.0.0.0/8 -j RETURN
iptables -t nat -A V2RAY -d 127.0.0.0/8 -j RETURN
iptables -t nat -A V2RAY -d 169.254.0.0/16 -j RETURN
iptables -t nat -A V2RAY -d 172.16.0.0/12 -j RETURN
iptables -t nat -A V2RAY -d 192.168.0.0/16 -j RETURN
iptables -t nat -A V2RAY -d 224.0.0.0/4 -j RETURN
iptables -t nat -A V2RAY -d 240.0.0.0/4 -j RETURN
# Anything else should be redirected to Dokodemo-door's local port
iptables -t nat -A V2RAY -p tcp -j REDIRECT --to-ports 1099
# apply redirect for traffic forworded by this proxy
iptables -t nat -A PREROUTING -p tcp -j V2RAY
# apply redirect for proxy itself
iptables -t nat -A OUTPUT -p tcp -j V2RAY
# UDP Redirect
iptables -t mangle -N V2RAY
iptables -t mangle -A V2RAY -p udp -j RETURN -m mark --mark 0xff
iptables -t mangle -A V2RAY -p udp --dport 53 -j TPROXY --on-port 1099 --tproxy-mark 0x01/0x01
iptables -t mangle -N V2RAY_MARK
iptables -t mangle -A V2RAY_MARK -p udp -j RETURN -m mark --mark 0xff
iptables -t mangle -A V2RAY_MARK -p udp --dport 53 -j MARK --set-mark 1
# add route for udp traffic
ip route add local default dev lo table 100
ip rule add fwmark 1 lookup 100
# Apply the rules
# apply udp tproxy for traffic forworded by this proxy
iptables -t mangle -A PREROUTING -j V2RAY
# apply udp tproxy for proxy itself
iptables -t mangle -A OUTPUT -j V2RAY_MARK
}
stop() {
iptables -t nat -D PREROUTING -p tcp -j V2RAY
iptables -t nat -D OUTPUT -p tcp -j V2RAY
iptables -t nat -F V2RAY
iptables -t nat -X V2RAY
iptables -t mangle -D PREROUTING -j V2RAY
iptables -t mangle -F V2RAY
iptables -t mangle -X V2RAY
iptables -t mangle -D OUTPUT -j V2RAY_MARK
iptables -t mangle -F V2RAY_MARK
iptables -t mangle -X V2RAY_MARK
ip rule del fwmark 1 lookup 100
ip route del local default dev lo table 100
}
case $1 in
start)
start
;;
stop)
stop
;;
*)
echo "$0 start|stop"
;;
esac
然后是服务管理脚本
#!/bin/sh /etc/rc.common
# "new" style init script
# Look at /lib/functions/service.sh on a running system for explanations of what other SERVICE_
# options you can use, and when you might want them.
START=80
STOP=20
APP=v2ray
SERVICE_WRITE_PID=1
SERVICE_DAEMONIZE=1
PREFIX=/usr/local/v2ray
start() {
service_start $PREFIX/v2ray -config $PREFIX/client_proxy.json
$PREFIX/client_proxy.sh start
}
stop() {
$PREFIX/client_proxy.sh stop
service_stop $PREFIX/v2ray
}
最后启用脚本,开机启动
/etc/init.d/v2ray start
/etc/init.d/v2ray enable
参考文档
感谢!
- Iptables 指南
- v2ray 官方文档
- Linux 使用 TPROXY 进行 UDP 的透明代理
- v2ray 白话文教程
- PowerDNS 关于 TPROXY 的解释
- TPROXY 官方文档
- Netfilter 维基百科
- OpenWrt 服务脚本
(stop 函数中,iptables -t mangle -D OUTPUT -j V2RAY
应为iptables -t mangle -D OUTPUT -j V2RAY_MAKR
-------------------------------
家庭网接入V2RAY透明代理
目标
- 兼容各种设备,比如 IPTV,NAS
- 接入设备无感知,无需做任何配置
- 设备间互访不受影响
方案 1 嵌套路由器模式
- 只能单向发起通信,比如电脑可以访问 NAS,而 NAS 就不能直接访问电脑了
- AirPlay 之类的就不好使了,手机没法投屏到电视了
方案 2 两个网关模式
- 默认的 DHCP 配置让所有的设备的网关指向了 OpenWrt
- OpenWrt 要配置一个静态地址,而且默认路由要指向路由光猫
- 我们可以配置 DHCP 来根据 mac 地址来让部分设备的网关直接指向路由光猫,这样对于不需要翻墙的电视和 IPTV 就绕过了 OpenWrt 直接出去了。
# 在lan接口上开启DHCP
uci set dhcp.lan.ignore='0'
uci set dhcp.lan.start='100'
uci set dhcp.lan.limit='99'
# 配置静态IP并且默认路由指向联通路由光猫
uci set network.lan.proto='static'
uci set network.lan.ipaddr='192.168.1.200'
uci set network.lan.netmask='255.255.255.0'
uci set network.lan.gateway='192.168.1.1'
uci set network.lan.dns='192.168.1.1'
# 匹配特定mac地址,对它们分配特殊的网关地址
uci set dhcp.iptv=host
uci set dhcp.iptv.name='iptv'
uci set dhcp.iptv.tag='free'
uci set dhcp.iptv.mac='2c:55:d3:ab:86:64' 'c8:0e:77:75:ea:b3'
uci set dhcp.free=tag
uci set dhcp.free.dhcp_option='3,192.168.1.1'
uci commit
service dnsmasq restart
方案 3 单路由器模式
- 进 http://192.168.1.1/hidden_version_switch.gch ,选 default version,密码 CUAdmin
- 机器自动重启之后就可以进 http://192.168.1.1/cu.html 了, 选管理员账户,密码 CUAdmin
- 进去之后删掉之前的 internet 配置,然后新建 internet bridge,vlan 选项记得选改 tag,然后在 vlan_id 里填 3961(这里是北京联通的 vlan_id,我测试是可以的)
- iptv 那块选 dhcp,vlan_i 填 3964 即可(这步我做了但是 iptv 并不能工作,IPTV 能正常工作的朋友请分享下你的配置吧)
参考文档
- 利用 Dnsmasq 部署 DHCP 服务
- DHCP Option Reference
- OpenWrt DNS and DHCP configuration
- OpenWrt DNS and DHCP configuration examples
from
-------------------------------------------------------------
Clash做透明代理
目标
- 配置 Clash 使其走 vmess 协议来处理透明代理流量
- 配置 iptables 转发流量
注意:本文是基于安装了 Debian 系统的的软路由配置的,Openwrt 也可以参考自行修改。
Clash 优势
相比原生的 V2Ray,Clash 的优势如下:
- 灵活管理 proxy,支持不同的策略来选择最优的 proxy
- 内置 DNS,支持 DOH
- 基于规则来的流量转发
Clash 安装配置
安装
TARGET=/usr/local/clash
[ -e $TARGET ] || mkdir $TARGET
VERSION=v1.8.0
FILENAME=clash-linux-amd64-$VERSION
curl -sLO https://github.com/Dreamacro/clash/releases/download/$VERSION/$FILENAME.gz
gunzip $FILENAME.gz
mv $FILENAME $TARGET/clash
chmod 755 $TARGET/clash
Clash 配置
配置这里我是基于这个模板修改的,精简了一些配置。
配置放在 /usr/local/clash/config.yml
mixed-port: 7890
redir-port: 7892
allow-lan: true
mode: Rule
log-level: info
# secret: ""
dns:
enable: true
listen: 0.0.0.0:53
enhanced-mode: redir-host
default-nameserver:
- 119.29.29.29
- 223.5.5.5
nameserver:
- https://doh.pub/dns-query
- https://dns.alidns.com/dns-query
fallback:
- https://1.1.1.1/dns-query # DNS over HTTPS
- https://8.8.8.8/dns-query # DNS over HTTPS
fallback-filter:
geoip: true
geoip-code: CN
ipcidr:
- 240.0.0.0/4
proxies:
- name: vmess-hkg01
type: vmess
server: 1.1.1.1
port: 443
uuid: changeme
alterId: 64
cipher: aes-128-gcm
network: ws
ws-path: /
ws-headers:
Host: foo.xdays.me
tls: true
- name: vmess-hkg02
type: vmess
server: 2.2.2.2
port: 443
uuid: changeme
alterId: 64
cipher: aes-128-gcm
network: ws
ws-path: /
ws-headers:
Host: foo.xdays.me
tls: true
# 代理组策略
proxy-groups:
# url-test 通过指定的 URL 测试并选择延迟最低的节点
- name: "Group1"
type: url-test
proxies:
- "vmess-hkg01"
- "vmess-hkg02"
url: 'http://www.gstatic.com/generate_204'
interval: 300
# 代理节点选择
- name: "GSLB"
type: select
proxies:
- "Group1"
- "DIRECT"
rules:
# Local Area Network
- IP-CIDR,192.168.0.0/16,DIRECT
- IP-CIDR,10.0.0.0/8,DIRECT
- IP-CIDR,172.16.0.0/12,DIRECT
- IP-CIDR,127.0.0.0/8,DIRECT
- IP-CIDR,100.64.0.0/10,DIRECT
- DOMAIN-KEYWORD,linkedin,GSLB
- GEOIP,CN,DIRECT
- MATCH,GSLB
透明代理配置
Clash 的透明代理需要做两步:
- DNS 解析要经过 Clash 自己监听的 DNS 服务, 我这里是 UDP 的 853 端口
- iptables 把流量 redirect 给 clash
先说第一步,我的做法是用 dnsmasq 来作为局域网的 dhcp 和 dns server,然后 dnsmasq 将 dns 请求转发给 clash 的 853 端口,dnsmasq 的配置如下:
bogus-priv
no-resolv
server=127.0.0.1#853
domain=lan,192.168.2.0/24
dhcp-range=192.168.2.100,192.168.2.200,12h
dhcp-leasefile=/var/lib/misc/dnsmasq.leases
cache-size=150
再说第二步,iptables 的配置也很简单,我写了一个脚本来开关 iptables 规则
脚本放在 /usr/local/clash/proxy.sh
#!/bin/bash
DNSCONF=/etc/dnsmasq.d/local
echo 1 > /proc/sys/net/ipv4/ip_forward
start() {
# TCP Redirect
# Create new chain
echo "create a new chain"
iptables -t nat -N CLASH
# Ignore LANs and any other addresses you'd like to bypass the proxy
# See Wikipedia and RFC5735 for full list of reserved networks.
iptables -t nat -A CLASH -d 0.0.0.0/8 -j RETURN
iptables -t nat -A CLASH -d 10.0.0.0/8 -j RETURN
iptables -t nat -A CLASH -d 127.0.0.0/8 -j RETURN
iptables -t nat -A CLASH -d 169.254.0.0/16 -j RETURN
iptables -t nat -A CLASH -d 172.16.0.0/12 -j RETURN
iptables -t nat -A CLASH -d 192.168.0.0/16 -j RETURN
iptables -t nat -A CLASH -d 224.0.0.0/4 -j RETURN
iptables -t nat -A CLASH -d 240.0.0.0/4 -j RETURN
iptables -t nat -A CLASH -s 192.168.2.0/24 -p tcp -j REDIRECT --to-ports 7892
# apply redirect for traffic forworded by this proxy
echo "apply the clash chain"
iptables -t nat -A PREROUTING -p tcp -j CLASH
# apply redirect for proxy itself
# for i in $OID; do
# iptables -t nat -A OUTPUT -m owner --uid-owner $i -j RETURN
# done
# iptables -t nat -A OUTPUT -p tcp -j CLASH
echo "change dns server"
use-gfw-dns
}
stop() {
iptables -t nat -D PREROUTING -p tcp -j CLASH
iptables -t nat -F CLASH
iptables -t nat -X CLASH
use-normal-dns
}
status() {
echo "==== Iptable rules ===="
iptables -t nat -nL
echo
echo "==== DNS Sever===="
grep "^server=" $DNSCONF
}
use-normal-dns() {
# 这里要改成一个你自己的dns服务器
sed -i '/server=.*/s/.*/server=192.168.1.1/' $DNSCONF
systemctl restart dnsmasq
}
use-gfw-dns() {
sed -i '/server=.*/s/.*/server=127.0.0.1#853/' $DNSCONF
systemctl restart dnsmasq
}
case $1 in
start)
start
;;
stop)
stop
;;
status)
status
;;
*)
echo "$0 start | stop | status"
;;
esac
改下可执行权限
chmod 755 /usr/local/clash/proxy.sh
这个脚本里还包括的切换 dnsmasq 的 dns 配置的操作,因为如果 clash 停了整个局域网所有机器的 dns 解析就会失败,这是配合后边一键启停翻墙做准备的。
一键开关透明代理
编辑 /etc/systemd/system/clash.service
[Unit]
Description=Clash Service
After=network.target
Wants=network.target
[Service]
# User=nobody
# Group=nobody
Type=simple
PIDFile=/run/clash.pid
ExecStartPre=/usr/local/clash/proxy.sh start
ExecStart=/usr/local/clash/clash -d /usr/local/clash/
ExecStopPost=/usr/local/clash/proxy.sh stop
Restart=on-failure
RestartPreventExitStatus=23
[Install]
WantedBy=multi-user.target
然后加载配置,开机启动透明代理
systemctl daemon-reload
systemctl start clash
systemctl enable clash
--------------------------------
v2ray-tproxy(本地机器需为linux系统)
Introduction
- macvlan: easy to deploy, but due to the limitation of macvlan, the host itself cannot connect to the container, so it can't use the transparent proxy
- bridge to host: unfortunately, docker has a very poor support for bridging to host network, it's feasible, but very tricky
- host: Docker host network just doesn't create network namespace and the script in the image will pollute host network namespace without cleaning it up, so host network is not recommended
Deployment
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--ip-range=192.168.1.251/32 \
--gateway=192.168.1.254 \
-o parent=ens3 v2ray
docker run -d \
--name=v2ray \
--privileged=true \
--network v2ray \
--env VMESS_SERVER= \
--env VMESS_PORT= \
--env VMESS_ID= \
--env NETWORK=192.168.1.0/24 \
--env ADDRESS=192.168.1.251/24 \
--env GATEWAY=192.168.1.254 \
core2duo/v2ray-bypass:200202
Testing
ip route change default via 192.168.1.251
echo "nameserver 192.168.1.251" > /etc/resolve.conf
Work with router
dnsmasq
and ipset
. You will need a dnsmasq configuration that indicates which domain will use v2ray DNS and will be added into ipset, something like this:
server=/.google.com/192.168.1.251#53
ipset=/.google.com/gfwlist
ipset create gfwlist hash:ip
ip rule add pref 10 fwmark 0xAA lookup 10
ip route add default via 192.168.1.251 table 10
iptables -t mangle -A PREROUTING -m set --match-set gfwlist dst -j MARK --set-mark 0xAA
- 局域网设备较多,比如说办公室、实验室、大家庭等;
- 设备(的软件)无法/不方便设置代理,比如说 Chromecast、电视盒子等;
- 希望设备的所有软件都走代理。
优点
- 解决了墙外 DNS 污染问题;
- 在解决了 1 的情况下国内域名的即能够解析到国内 CDN;
- 不需要外部软件或自建 DNS 就可决绝 1 和 2 的问题,只要系统支持 V2Ray 和 iptables;
- 能够完美利用 V2Ray 强大而灵活的路由功能,而不必额外维护一个路由表;
准备
- 一个有能力根据实际情况解决遇到问题的人
- 一台已经搭建 V2Ray 并能正常使用的 VPS ,本文假设 IP 为
110.231.43.65
; - 一台带 iptables、有 root 权限并且系统为 Linux 的设备,假设地址为
192.168.1.22
,已经配置好 V2Ray 作为客户端。这个设备可以是路由器、开发板、个人电脑、虚拟机和 Android 设备等,更具普适性地称之为网关。我个人不建议使用 MT7620 系路由器开透明代理,性能太差了,很多固件也没有开启 FPU 。要是真不愿意出这点钱,用电脑开个虚拟机吧(我就是这么干的),VirtualBox、Hyper 之类的都可以,但是别忘了网络模式用网桥。
设置步骤
- 网关设备开启 IP 转发。在 /etc/sysctl.conf 文件添加一行
net.ipv4.ip_forward=1
,执行下列命令生效:
sysctl -p
-
网关设备设置静态 IP,与路由器 LAN 口同一个网段,默认网关为路由器的 IP;进入路由器的管理后台,到 DHCP 设定将默认网关地址为网关设备的 IP,本例为 192.168.1.22,或者电脑手机等设备单独设置默认网关,然后电脑/手机重新连接到路由器测试是不是可以正常上网(这时还不能翻墙),如果不能上网先去学习一个把这个搞定,否则接下来再怎么也同样上不了网。网关设备设定静态 IP 是为了避免重启后 IP 会发生变化导致其他设备无法联网;路由器设定 DHCP 默认网关地址是为了让接入到这个路由器的设备将上网的数据包发到网关设备,然后由网关设备转发。
-
在服务器和网关安装最新版本的 V2Ray(如果不会就参照前面的教程,由于 GFW 会恶化 GitHub Releases 的流量,网关直接运行脚本几乎无法安装,建议先下载 V2Ray 的压缩包,然后用安装脚本通过 --local 参数进行安装),并配置好配置文件。一定要确定搭建的 V2Ray 能够正常使用。在网关执行
curl -x socks5://127.0.0.1:1080 google.com
测试配置的 V2Ray 是否可以翻墙(命令中socks5
指 inbound 协议为 socks,1080
指该 inbound 端口是 1080)。如果出现类似下面的输出则可以翻墙,如果没有出现就说明翻不了,你得仔细检查以下哪步操作不对或漏了。
- 在网关的配置,添加 dokodemo door 协议的入站配置 ,并开启 sniffing;还要在所有 outbound 的 streamSettins 添加 SO_MARK。配置形如(配置中的
...
代表原来客户端的通常配置):
{
"routing": {...},
"inbounds": [
{
...
},
{
"port": 12345, //开放的端口号
"protocol": "dokodemo-door",
"settings": {
"network": "tcp,udp",
"followRedirect": true // 这里要为 true 才能接受来自 iptables 的流量
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"]
}
}
],
"outbounds": [
{
...
"streamSettings": {
...
"sockopt": {
"mark": 255 //这里是 SO_MARK,用于 iptables 识别,每个 outbound 都要配置;255可以改成其他数值,但要与下面的 iptables 规则对应;如果有多个 outbound,最好将所有 outbound 的 SO_MARK 都设置成一样的数值
}
}
}
...
]
}
- 设定 TCP 透明代理的 iptables 规则,命令如下(
#
代表注释):
iptables -t nat -N V2RAY # 新建一个名为 V2RAY 的链
iptables -t nat -A V2RAY -d 192.168.0.0/16 -j RETURN # 直连 192.168.0.0/16
iptables -t nat -A V2RAY -p tcp -j RETURN -m mark --mark 0xff # 直连 SO_MARK 为 0xff 的流量(0xff 是 16 进制数,数值上等同与上面配置的 255),此规则目的是避免代理本机(网关)流量出现回环问题
iptables -t nat -A V2RAY -p tcp -j REDIRECT --to-ports 12345 # 其余流量转发到 12345 端口(即 V2Ray)
iptables -t nat -A PREROUTING -p tcp -j V2RAY # 对局域网其他设备进行透明代理
iptables -t nat -A OUTPUT -p tcp -j V2RAY # 对本机进行透明代理
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100
iptables -t mangle -N V2RAY_MASK
iptables -t mangle -A V2RAY_MASK -d 192.168.0.0/16 -j RETURN
iptables -t mangle -A V2RAY_MASK -p udp -j TPROXY --on-port 12345 --tproxy-mark 1
iptables -t mangle -A PREROUTING -p udp -j V2RAY_MASK
-
使用电脑/手机尝试直接访问被墙网站,这时应该是可以访问的(如果不能,你可能得请教大神手把手指导了)。
-
写开机自动加载上述的 iptables 的脚本,或者使用第三方软件(如 iptables-persistent),否则网关重启后 iptables 会失效(即透明代理会失效)。
注意事项
- 在上面的设置中,假设访问了国外网站,如 Google 等,网关依然会使用的系统 DNS 进行查询,只不过返回的结果是污染过的,而 V2Ray 提供的 sniffing 能够从流量中提取域名信息交由 VPS 解析。也就是说,每次打算访问被墙的网站,DNS 提供商都知道,鉴于国内企业尿性,也许 GFW 也都知道,会不会将这些数据收集喂 AI 也未可知。
- sniffing 目前只能从 TLS 和 HTTP 流量中提取域名,如果上网流量有非这两种类型的慎用 sniffing 解决 DNS 污染。
- 由于对 iptables 不熟,我总感觉上面对 UDP 流量的透明代理的设置使用上有点问题,知道为什么的朋友请反馈一下。如果你只是简单的上上网看看视频等,可以只代理 TCP 流量,不设 UDP 透明代理。
- 喜欢玩网游的朋友可能要失望了,使用 V2Ray 加速游戏效果不是很好。
- V2Ray 只能代理 TCP/UDP 的流量,ICMP 不支持,即就算透明代理成功了之后 ping Google 这类网站也是不通的。
- 按照网上其他的透明代理教程,设置 iptables 肯定要 RETURN 127.0.0.0/8 这类私有地址,但我个人观点是放到 V2Ray 的路由里好一些。
透明代理(TPROXY)
设置网关
- 用网线将树莓派接入路由器 LAN 口,假设分给树莓派的 IP 是 192.168.1.22。
- 树莓派开启 IP 转发(需要开启 IP 转发才能作为网关)。命令为
echo net.ipv4.ip_forward=1 >> /etc/sysctl.conf && sysctl -p
。执行后将出现 net.ipv4.ip_forward=1 的提示。 - 手动配置 PC 的网络,将默认网关指向树莓派的地址即
192.168.1.22
。此时 PC 应当能正常上网(由于还没设置代理,“正常”是指可以上国内的网站)。
树莓派安装配置 V2Ray
- 安装 V2Ray。可以使用 V2Ray 提供的 go.sh 脚本安装,由于 GFW 会恶化对 GitHub 的访问,直接运行脚本几乎无法安装,建议先下载 V2Ray 的压缩包,然后用安装脚本通过 --local 参数进行安装。
- 配置 V2Ray。按照前文教程将 V2Ray 配置成客户端形式。然后执行
curl -so /dev/null -w "%{http_code}" google.com -x socks5://127.0.0.1:1080
确认 V2Ray 已经可以翻墙(命令中 socks5 指 inbound 协议为 socks,1080 指该 inbound 端口是 1080)。如果执行这个命令出现了 301 或 200 这类数字的话代表可以翻墙,如果长时间没反应或者是 000 的话说明不可以翻墙。
配置透明代理
为V2Ray配置透明代理的入站和DNS分流
{
"inbounds": [
{
"tag":"transparent",
"port": 12345,
"protocol": "dokodemo-door",
"settings": {
"network": "tcp,udp",
"followRedirect": true
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
},
"streamSettings": {
"sockopt": {
"tproxy": "tproxy" // 透明代理使用 TPROXY 方式
}
}
},
{
"port": 1080,
"protocol": "socks", // 入口协议为 SOCKS 5
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls"]
},
"settings": {
"auth": "noauth"
}
}
],
"outbounds": [
{
"tag": "proxy",
"protocol": "vmess", // 代理服务器
"settings": {
"vnext": [
...
]
},
"streamSettings": {
"sockopt": {
"mark": 255
}
},
"mux": {
"enabled": true
}
},
{
"tag": "direct",
"protocol": "freedom",
"settings": {
"domainStrategy": "UseIP"
},
"streamSettings": {
"sockopt": {
"mark": 255
}
}
},
{
"tag": "block",
"protocol": "blackhole",
"settings": {
"response": {
"type": "http"
}
}
},
{
"tag": "dns-out",
"protocol": "dns",
"streamSettings": {
"sockopt": {
"mark": 255
}
}
}
],
"dns": {
"servers": [
"8.8.8.8", // 非中中国大陆域名使用 Google 的 DNS
"1.1.1.1", // 非中中国大陆域名使用 Cloudflare 的 DNS(备用)
"114.114.114.114", // 114 的 DNS (备用)
{
"address": "223.5.5.5", //中国大陆域名使用阿里的 DNS
"port": 53,
"domains": [
"geosite:cn",
"ntp.org", // NTP 服务器
"$myserver.address" // 此处改为你 VPS 的域名
]
}
]
},
"routing": {
"domainStrategy": "IPOnDemand",
"rules": [
{ // 劫持 53 端口 UDP 流量,使用 V2Ray 的 DNS
"type": "field",
"inboundTag": [
"transparent"
],
"port": 53,
"network": "udp",
"outboundTag": "dns-out"
},
{ // 直连 123 端口 UDP 流量(NTP 协议)
"type": "field",
"inboundTag": [
"transparent"
],
"port": 123,
"network": "udp",
"outboundTag": "direct"
},
{
"type": "field",
"ip": [
// 设置 DNS 配置中的国内 DNS 服务器地址直连,以达到 DNS 分流目的
"223.5.5.5",
"114.114.114.114"
],
"outboundTag": "direct"
},
{
"type": "field",
"ip": [
// 设置 DNS 配置中的国内 DNS 服务器地址走代理,以达到 DNS 分流目的
"8.8.8.8",
"1.1.1.1"
],
"outboundTag": "proxy" // 改为你自己代理的出站 tag
},
{ // 广告拦截
"type": "field",
"domain": [
"geosite:category-ads-all"
],
"outboundTag": "block"
},
{ // BT 流量直连
"type": "field",
"protocol":["bittorrent"],
"outboundTag": "direct"
},
{ // 直连中国大陆主流网站 ip 和 保留 ip
"type": "field",
"ip": [
"geoip:private",
"geoip:cn"
],
"outboundTag": "direct"
},
{ // 直连中国大陆主流网站域名
"type": "field",
"domain": [
"geosite:cn"
],
"outboundTag": "direct"
}
]
}
}
- dokodemo-door 是用来接收透明代理的入站协议,followRedirect 项须为 true 以及 sockopt.tproxy 项须为 tproxy,建议开启 snifing,否则路由无法匹配域名;
- 本节添加了 DNS 配置,用来对国内外域名进行 DNS 分流,需要
DNS 配置
、DNS 入站
、DNS 出站
和路由
四者配合,在本例中 DNS 入站直接使用透明代理入站,可参考DNS 及其应用
- ;
- 在 DNS 配置中,依次配置了 Google、Cloudflare、114 和阿里的 DNS,由于在阿里的 DNS 中指定了 domain,所以匹配的域名会用阿里的 DNS 查询,其他的先查询 Google 的 DNS,如果查不到的话再依次查 Cloudflare 及 114 的。所以达到了国内外域名 DNS 分流,以及 DNS 备用。要注意把 NTP 服务器和你自己 VPS 域名也加入到直连的 DNS ,否则会导致 V2Ray 无法与 VPS 正常连接;
- DNS 配置只是说明哪些域名查哪个 DNS,至于哪个 DNS 走代理哪个 DNS 直连要在 routing 里设置规则;
- routing 也要设置 123 端口的 UDP 流量直连,不然的话要是时间误差超出允许范围(90s),要使用 NTP 校准时间就要先连上代理,但是连代理又要确保时间准确,结果就是既连不上代理,也无法自动校准时间;
- freedom 的出站设置 domainStrategy 为 UseIP,以避免直连时因为使用本机的 DNS 出现一些奇怪问题;
- 注意要在所有的 outbound 加一个 255 的 mark,这个 mark 与下文 iptables 命令中
iptables -t mangle -A V2RAY_MASK -j RETURN -m mark --mark 0xff
配合,以直连 V2Ray 发出的流量(blackhole 可以不配置 mark)。
配置透明代理规则
#
开头的为注释。
# 设置策略路由
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100
# 代理局域网设备
iptables -t mangle -N V2RAY
iptables -t mangle -A V2RAY -d 127.0.0.1/32 -j RETURN
iptables -t mangle -A V2RAY -d 224.0.0.0/4 -j RETURN
iptables -t mangle -A V2RAY -d 255.255.255.255/32 -j RETURN
iptables -t mangle -A V2RAY -d 192.168.0.0/16 -p tcp -j RETURN # 直连局域网,避免 V2Ray 无法启动时无法连网关的 SSH,如果你配置的是其他网段(如 10.x.x.x 等),则修改成自己的
iptables -t mangle -A V2RAY -d 192.168.0.0/16 -p udp ! --dport 53 -j RETURN # 直连局域网,53 端口除外(因为要使用 V2Ray 的
iptables -t mangle -A V2RAY -p udp -j TPROXY --on-port 12345 --tproxy-mark 1 # 给 UDP 打标记 1,转发至 12345 端口
iptables -t mangle -A V2RAY -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1 # 给 TCP 打标记 1,转发至 12345 端口
iptables -t mangle -A PREROUTING -j V2RAY # 应用规则
# 代理网关本机
iptables -t mangle -N V2RAY_MASK
iptables -t mangle -A V2RAY_MASK -d 224.0.0.0/4 -j RETURN
iptables -t mangle -A V2RAY_MASK -d 255.255.255.255/32 -j RETURN
iptables -t mangle -A V2RAY_MASK -d 192.168.0.0/16 -p tcp -j RETURN # 直连局域网
iptables -t mangle -A V2RAY_MASK -d 192.168.0.0/16 -p udp ! --dport 53 -j RETURN # 直连局域网,53 端口除外(因为要使用 V2Ray 的 DNS)
iptables -t mangle -A V2RAY_MASK -j RETURN -m mark --mark 0xff # 直连 SO_MARK 为 0xff 的流量(0xff 是 16 进制数,数值上等同与上面V2Ray 配置的 255),此规则目的是避免代理本机(网关)流量出现回环问题
iptables -t mangle -A V2RAY_MASK -p udp -j MARK --set-mark 1 # 给 UDP 打标记,重路由
iptables -t mangle -A V2RAY_MASK -p tcp -j MARK --set-mark 1 # 给 TCP 打标记,重路由
iptables -t mangle -A OUTPUT -j V2RAY_MASK # 应用规则
1. UDP 只能 TPROXY
2. TPROXY 不能用于 OUTPUT 链
iptables -t mangle -A V2RAY_MASK -p udp -j MARK --set-mark 1
iptables -t mangle -A V2RAY_MASK -p tcp -j MARK --set-mark 1
iptables -t mangle -A OUTPUT -j V2RAY_MASK
开机自动运行透明代理规则
- 由于 iptables 命令有点多,所以先将 iptables 规则保存到 /etc/iptables/rules.v4 中。
mkdir -p /etc/iptables && iptables-save > /etc/iptables/rules.v4
- 在 /etc/systemd/system/ 目录下创建一个名为 tproxyrule.service 的文件,然后添加以下内容并保存。
[Unit]
Description=Tproxy rule
After=network.target
Wants=network.target
[Service]
Type=oneshot
#注意分号前后要有空格
ExecStart=/sbin/ip rule add fwmark 1 table 100 ; /sbin/ip route add local 0.0.0.0/0 dev lo table 100 ; /sbin/iptables-restore /etc/iptables/rules.v4
[Install]
WantedBy=multi-user.target
- 执行下面的命令使 tproxyrule.service 可以开机自动运行。
systemctl enable tproxyrule
其他
解决 too many open files 问题
- 修改 /etc/systemd/system/v2ray.service 文件,在
[Service]
下加入LimitNPROC=500
和LimitNOFILE=1000000
,修改后的内容如下。
[Unit]
Description=V2Ray Service
After=network.target
Wants=network.target
[Service]
# This service runs as root. You may consider to run it as another user for security concerns.
# By uncommenting the following two lines, this service will run as user v2ray/v2ray.
# More discussion at https://github.com/v2ray/v2ray-core/issues/1011
# User=v2ray
# Group=v2ray
Type=simple
PIDFile=/run/v2ray.pid
ExecStart=/usr/bin/v2ray/v2ray -config /etc/v2ray/config.json
Restart=on-failure
# Don't restart in the case of configuration error
RestartPreventExitStatus=23
LimitNPROC=500
LimitNOFILE=1000000
[Install]
WantedBy=multi-user.target
- 执行
systemctl daemon-reload && systemctl restart v2ray
生效。
设定网关为静态 IP
设定 DHCP
备注
- TPROXY 与 REDIRECT 是针对 TCP 而言的两种透明代理模式,两者的差异主要在于 TPROXY 可以透明代理 IPV6,而 REDIRECT 不行,本文主要是将透明代理模式改为 TPROXY 并且使用了 V2Ray 的 DNS。但我没有 IPV6 环境,无法进行测试,所以本文只适用于 IPV4。
- 据我了解,到目前(2019.10)为止,在我所知的具备透明代理功能的翻墙工具中,TCP 透明代理方式可以使用的 TPROXY 的只有 V2Ray。所以你要找其他资料参考的话,要注意透明代理方式,因为基本上都是 REDIRECT 模式的(包括 V2Ray 官网给的示例)。
- 在透明代理中,不要用 V2Ray 开放 53 端口做 DNS 服务器。如果这么做了,DNS 会出问题,这应该是个 BUG。(详情见此 Issue
- )
我用 NatTypeTester
测试过 NAT 类型,结果是 FullCone,但也看到有反馈说玩游戏依然是 PortRestrictedCone。我也不清楚是怎么回事,这点需要玩游戏的朋友来确认了。不过目前测试发现代理 QUIC 的效果还不不错的。 V2Ray 仍然不支持 FullCone,详情见此 Issue
参考资料
漫谈各种黑科技式DNS技术在代理环境中的应用
Linux transparent proxy supportV2Ray
透明代理样例