V2RAY做透明代理

 

目标

  1. 配置 v2ray 使其接受透明代理的流量
  2. 配置 iptables 将所有 tcp 和 udp 53 的流量转发给 v2ray
  3. 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 这块的挑战比较大,我一路试错过来,总结来说有以下几点:

  1. 要理解 iptables 的各个表中的链的先后顺序
  2. 要捕捉其他设备过来的 tcp 流量
  3. 要捕捉本机发起的 tcp 流量
  4. 要捕捉其他设备过来的 udp 53 流量,也就是 DNS 流量
  5. 要捕捉本机发起的 DNS 流量

Netfilter 数据包流程图

Package flow in Netfilter

从这张图中我们可以看出对于其他设备过来的流量都应该在PREROUTING这个链来做,而对于本机发出的流量应该在OUTPUT这个链来做。但由于重定向 tcp 和 udp 流量在实现上有区别,分别用到了 iptables 里的REDIRECTTPROXY两种技术。参考这篇博客所说,是因为 ss-redir 应用没有实现 UDP REDIRECT 相关的代码,当然我也把 UDP 全都通过REDIRECT转发给了 v2ray 结果也不行,所以 UDP 转发的部分还是通过TPROXY来实现的。

REDIRECT vs TPROXY

REDIRECT其实是 DNAT 的一种特殊形式,特殊在其把数据包的目标 IP 改成了 127.0.0.1,端口改成了--to-ports 参数指定的本地端口,这样本机的透明代理程序就能处理这个包,应用能通过内核的状态信息拿到被改写之前的目标 IP 和端口号,具体参考这里

TPROXYREDIRECT新的特性,它能做到不修改数据包,应用只需一点改动就能实现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得以实现归结为三个要点:

  1. 将流量重定向到本地路由
  2. 路由规则定义去向
  3. 代理程序监听,通过特殊的参数可以响应非本机的 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。

然后分别在PREROUTINGOUTPUT连个链里应用我们新建的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 流量,然后应用TPROXYtarget,同时打上特定的 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

参考文档

感谢!

(stop 函数中,
iptables -t mangle -D OUTPUT -j V2RAY
应为
iptables -t mangle -D OUTPUT -j V2RAY_MAKR

否则会出现错误:

Couldn't load target `V2RAY':No such file or directory.
iptables: Too many links.

瑕不掩瑜。
这篇文章无论从内容还是结构上,都是我能在网上找到最好的透明代理教程了。
感谢作者!)

-------------------------------

家庭网接入V2RAY透明代理

目标

上一篇V2Ray 透明代理里详细解释了在 OpenWrt 上配置 V2RAY 作为透明代理,本文提供两种将这种透明代理应用到家庭局域网的两种方式。主要目标有以下几个:
  1. 兼容各种设备,比如 IPTV,NAS
  2. 接入设备无感知,无需做任何配置
  3. 设备间互访不受影响
下图为应用透明代理前我的家庭网拓扑:
home network before
由于联通路由光猫如果改成光猫的话 IPTV 就不好使了,所以这个猫比如像其名字一样作为家庭网关了。

方案 1 嵌套路由器模式

这种方案的网络拓扑图如下:
home network after1
直接把极路由刷上 OpenWrt 改成路由模式,然后上透明代理就可以了。这种方案优点就是配置成本很小,缺点是电视,IPTV 和 NAS 和手机电脑等设备被分割到了两个局域网内,这就造成了:
  1. 只能单向发起通信,比如电脑可以访问 NAS,而 NAS 就不能直接访问电脑了
  2. AirPlay 之类的就不好使了,手机没法投屏到电视了

方案 2 两个网关模式

这种方案拓扑图如下:
home network after2
极路由还是工作在 AP 模式,当然也可以把透明代理做在上面。由于我的极路由已经跑了老毛子系统,而且从 NAS 上虚拟出来的 OpenWrt 性能应该比极路由更好一些,所以我就跑了一个 OpenWrt 以单臂路由方式当做透明代理。首先,这个拓扑上所有的设备都跑在一个局域网内,包括极路由,NAS 以及 NAS 上的虚拟机,而且这个局域网里有两个网关路由光猫和 OpenWrt;然后,我要关闭路由光猫的 DHCP 功能,因为这个 DHCP 不满足条件,不能将非本机的的 IP 配置成 DHCP 网关;最后,我需要在 OpenWrt 上做如下的配置:
  1. 默认的 DHCP 配置让所有的设备的网关指向了 OpenWrt
  2. OpenWrt 要配置一个静态地址,而且默认路由要指向路由光猫
  3. 我们可以配置 DHCP 来根据 mac 地址来让部分设备的网关直接指向路由光猫,这样对于不需要翻墙的电视和 IPTV 就绕过了 OpenWrt 直接出去了。
现在我们来配置下 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 单路由器模式

这种方案拓扑图如下:
home network after3
首先,修改联通路由光猫改成桥接模式让其只作为猫使用,具体可以参考这个帖子,具体方法如下:
  1. http://192.168.1.1/hidden_version_switch.gch ,选 default version,密码 CUAdmin
  2. 机器自动重启之后就可以进 http://192.168.1.1/cu.html 了, 选管理员账户,密码 CUAdmin
  3. 进去之后删掉之前的 internet 配置,然后新建 internet bridge,vlan 选项记得选改 tag,然后在 vlan_id 里填 3961(这里是北京联通的 vlan_id,我测试是可以的)
  4. iptv 那块选 dhcp,vlan_i 填 3964 即可(这步我做了但是 iptv 并不能工作,IPTV 能正常工作的朋友请分享下你的配置吧)
然后极路由作为网关和透明代理,所有的设备都以极路由为网关,而且 DHCP 这块不需要啥特殊配置了。但是路由光猫改成桥接模式后 IPTV 就不好使了,所以这种场景不适合我,如果你不需要 IPTV,那么这种模式最为理想,当然得有个比较牛逼的路由器哇。

参考文档

from 

https://archive.is/5lMYR#selection-121.0-523.43

 -------------------------------------------------------------

Clash做透明代理

目标

  1. 配置 Clash 使其走 vmess 协议来处理透明代理流量
  2. 配置 iptables 转发流量

注意:本文是基于安装了 Debian 系统的的软路由配置的,Openwrt 也可以参考自行修改。

Clash 优势

相比原生的 V2Ray,Clash 的优势如下:

  1. 灵活管理 proxy,支持不同的策略来选择最优的 proxy
  2. 内置 DNS,支持 DOH
  3. 基于规则来的流量转发

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 的透明代理需要做两步:

  1. DNS 解析要经过 Clash 自己监听的 DNS 服务, 我这里是 UDP 的 853 端口
  2. 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系统)

 
 
 
This is an experimental image to prove some assumptions, more research to make it really useful in real world is inevitable.

 

Introduction

 
This is a docker image for bypass transparent proxy of v2ray, using techniques in this article: https://guide.v2fly.org/app/tproxy.html
 
The image uses network namespace to isolate ip route and iptables settings and accelerate deployment.
 
It should be deployed on a device other than router, like Raspberry Pi or NAS, using Docker network that is attched (bridged) to host physical network, there are several choices:
 
  • 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

 
Suppose the device IP address is 192.168.1.250, and the gateway is 192.168.1.254, the container's address will be 192.168.1.251.
 
First, create a macvlan:
 
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
 
Then start the container:
 
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

 
After the container is running, change the default route and DNS namespace to the container address in client:
 
ip route change default via 192.168.1.251
echo "nameserver 192.168.1.251" > /etc/resolve.conf
 
The client should work then.
 

 

Work with router

 
Use the container as a default route is not a good idea, as all the traffic will go through v2ray and bring extra performance pressure on it. The better way is to collaborate with the router, redirect the traffic to the proxy only when it's necessary.
 
The solution is to use 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
 
See gfwlist2dnsmasq for a tool that can automatically generate the configuration.
 
Then on router, create the ipset if it doesn't exist.
 
ipset create gfwlist hash:ip
 
Use iptables to mark traffics to ip that is in the ipset and route them to the proxy:
 
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
 
Done, all devices connected to the router should be able to use the transparent proxy now.
 
 
 
 
-----
 
 
 
用v2ray做透明代理
 
 
 
透明代理是什么意思请自行 Google,在这儿指使用 V2Ray 做透明代理实现路由器翻墙。然而,我个人认为路由器翻墙的说法并不准确,应该叫网关翻墙。所以本例实际上是关于网关翻墙的内容。当然了,单纯使用路由器翻墙也是可以的,因为普通的家用路由器本就是一个网关。使用网关翻墙可以使局域网内的所有设备都具有直接翻墙的能力,并且能够全局代理,而不必每台设备都安装 V2Ray,配置更新时只需在网关修改配置,用一些网友的话说就是就感觉没有墙一样。但是,有意上透明代理的同学请评估一下透明代理是否合适自己,而不要盲目跟风。
 
透明代理适用于以下情况:
 
  • 局域网设备较多,比如说办公室、实验室、大家庭等;
  • 设备(的软件)无法/不方便设置代理,比如说 Chromecast、电视盒子等;
  • 希望设备的所有软件都走代理。
 

优点

 
其实,V2Ray 早就可以作透明代理,当时我也研究了好一段时间,最终是折腾出来了。但是由于 DNS 的问题,我用着总感觉不太舒服。虽然有 ChinaDNS 这类的解决方案,但个人主观上并不喜欢。 不过嘛,现在就不一样了。就目前来说,使用 V2Ray 透明代理:
 
  1. 解决了墙外 DNS 污染问题;
  2. 在解决了 1 的情况下国内域名的即能够解析到国内 CDN;
  3. 不需要外部软件或自建 DNS 就可决绝 1 和 2 的问题,只要系统支持 V2Ray 和 iptables;
  4. 能够完美利用 V2Ray 强大而灵活的路由功能,而不必额外维护一个路由表;
 

准备

 
  • 一个有能力根据实际情况解决遇到问题的人
  • 一台已经搭建 V2Ray 并能正常使用的 VPS ,本文假设 IP 为 110.231.43.65
  • 一台带 iptables、有 root 权限并且系统为 Linux 的设备,假设地址为 192.168.1.22,已经配置好 V2Ray 作为客户端。这个设备可以是路由器、开发板、个人电脑、虚拟机和 Android 设备等,更具普适性地称之为网关。我个人不建议使用 MT7620 系路由器开透明代理,性能太差了,很多固件也没有开启 FPU 。要是真不愿意出这点钱,用电脑开个虚拟机吧(我就是这么干的),VirtualBox、Hyper 之类的都可以,但是别忘了网络模式用网桥。
 

设置步骤

 
设置步骤如下,假设使用 root。
 
  1. 网关设备开启 IP 转发。在 /etc/sysctl.conf 文件添加一行 net.ipv4.ip_forward=1 ,执行下列命令生效:
 
sysctl -p
 
  1. 网关设备设置静态 IP,与路由器 LAN 口同一个网段,默认网关为路由器的 IP;进入路由器的管理后台,到 DHCP 设定将默认网关地址为网关设备的 IP,本例为 192.168.1.22,或者电脑手机等设备单独设置默认网关,然后电脑/手机重新连接到路由器测试是不是可以正常上网(这时还不能翻墙),如果不能上网先去学习一个把这个搞定,否则接下来再怎么也同样上不了网。网关设备设定静态 IP 是为了避免重启后 IP 会发生变化导致其他设备无法联网;路由器设定 DHCP 默认网关地址是为了让接入到这个路由器的设备将上网的数据包发到网关设备,然后由网关设备转发。
  2. 在服务器和网关安装最新版本的 V2Ray(如果不会就参照前面的教程,由于 GFW 会恶化 GitHub Releases 的流量,网关直接运行脚本几乎无法安装,建议先下载 V2Ray 的压缩包,然后用安装脚本通过 --local 参数进行安装),并配置好配置文件。一定要确定搭建的 V2Ray 能够正常使用。在网关执行 curl -x socks5://127.0.0.1:1080 google.com 测试配置的 V2Ray 是否可以翻墙(命令中 socks5 指 inbound 协议为 socks,1080 指该 inbound 端口是 1080)。如果出现类似下面的输出则可以翻墙,如果没有出现就说明翻不了,你得仔细检查以下哪步操作不对或漏了。
 
 

301 Moved


The document has moved
here.

 
  1. 在网关的配置,添加 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 都设置成一样的数值
        }
      }
    }
    ...
  ]
}
 
  1. 设定 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 # 对本机进行透明代理
 
然后设定 UDP 流量透明代理的 iptables 规则,命令如下
 
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
 
  1. 使用电脑/手机尝试直接访问被墙网站,这时应该是可以访问的(如果不能,你可能得请教大神手把手指导了)。
  2. 写开机自动加载上述的 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)

 
原来出过一篇透明代理的教程
 
,但过了许久,V2Ray 也已经迭代了好多个版本。原来的教程依旧可以正常使用,但随着 V2Ray 的更新,V2Ray 推出了新的透明代理方式—— TPROXY,原来的叫 REDIRECT。最近测试了一下 TPROXY ,效果还不错,主观感觉比 REDIRECT 好。并且在本文的透明代理中,DNS 服务将由 V2Ray 提供。不过这种方式需要 iptables 的 TPROXY 模块支持,有一些阉割版的系统会精简掉 TPROXY 模块,这种系统是不适用于本文的。
 
普通家庭大多数是光纤入户接光猫调制解调,一个路由器的 WAN 口接光猫的 LAN 口,要上网的设备(如 PC 、电视盒子、手机)接路由器 LAN 口。本文的透明代理需要一台 Linux 主机接路由器 LAN 口,作为局域网中的网关,为其他接入局域网中的设备提供翻墙功能。这样的方式与我原来的透明代理教程是一样的,都是搭建在一个 Linux 主机上。这样可以透明代理的设备,有的人叫“透明网关”,也有的叫“旁路由”。我觉得这种不是很严肃的场合,叫什么都行,只要不妨碍理解。
 
很多设备都可以做透明网关,路由器、开发板、个人电脑、虚拟机和 Android 设备等。路由器可能比较特殊点,因为它本身就可以充当网关。上面可能说得太抽象,我就举些实际的,比如说树莓派、香橙派、用 PC 装的 Linux 虚拟机、淘宝的工控机(如 j1900)、NAS、电视盒子(如翻车迅)、你刚配的牙膏厂或农厂的电脑,这些都没问题。至于到底用什么?这得看需求,我觉得网络 200M 以下搞个高性能的类树莓派的 SBC 就够了,200M 以上就得考虑 X86 主机了(如今甚火的软路由)。当然,到底怎么选择还是得看自己。
 
本文假设你已经有一个设备(就以树莓派举例),将用来作网关(或说旁路由),并且已经安装好 Linux。关于系统,我更推荐 Debian 或 Debian 衍生版。为方面起见,本文均以 root 权限账户执行命令。并且有一台 PC 以便于操作。
 

设置网关

 
  1. 用网线将树莓派接入路由器 LAN 口,假设分给树莓派的 IP 是 192.168.1.22。
  2. 树莓派开启 IP 转发(需要开启 IP 转发才能作为网关)。命令为 echo net.ipv4.ip_forward=1 >> /etc/sysctl.conf && sysctl -p。执行后将出现 net.ipv4.ip_forward=1 的提示。
  3. 手动配置 PC 的网络,将默认网关指向树莓派的地址即 192.168.1.22。此时 PC 应当能正常上网(由于还没设置代理,“正常”是指可以上国内的网站)。
 

树莓派安装配置 V2Ray

 
  1. 安装 V2Ray。可以使用 V2Ray 提供的 go.sh 脚本安装,由于 GFW 会恶化对 GitHub 的访问,直接运行脚本几乎无法安装,建议先下载 V2Ray 的压缩包,然后用安装脚本通过 --local 参数进行安装。
  2. 配置 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分流

 
以下是 V2Ray 透明代理的配置示例,配置文件之后有说明。
 
{
  "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"
      }
    ]
  }
}
 
以上是 V2Ray 透明代理的参考配置,关于配置有一些注意点及说明:
 
  • 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)。
 

配置透明代理规则

 
执行下面的命令开启透明代理。由于使用了 TPROXY 方式的透明代理,所以 TCP 流量也是使用 mangle 表。以下命令中,以 # 开头的为注释。
 
# 设置策略路由
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 # 应用规则
 
执行了以上 ip 和 iptables 命令后,局域网同网段的设备以及网关本身就可以直接翻墙了。
 
关于 iptables 规则,比较容易理解,如果不太理解的话也可以 Google 搜索其他相关文章资料对比学习。在类 ss-redir 透明代理中,有两个观点非常深入人心:
 
1. UDP 只能 TPROXY
2. TPROXY 不能用于 OUTPUT 链
 
然后我们从这两个观点很容易得出一个推论:无法在提供透明代理的本机(即本例中的网关)上对 UDP 透明代理。 这个结论好像并没有什么问题,对吧?但实际上,在本例的配置中无论是 TCP 还是 UDP,都可以实现在本机上的透明代理,而且都是用 TPROXY。那好像又跟前面的结论矛盾了?其实关键在于这三句命令:
 
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
 
这几句是说给 OUTPUT 链的 TCP 和 UDP 打个标记 1(OUTPUT 应用 V2RAY_MASK 链)。由于 Netfilter 的特性,在 OUTPUT 链打标记会使相应的包重路由到 PREROUTING 链上,在已经配置好了 PREROUTING 相关的透明代理的情况下,OUTPUT 链也可以透明代理了,也就是网关对自身的 UDP 流量透明代理自身(当然 TCP 也不在话下)。因为这是 netfilter 本身的特性,Shadowsocks 应该也可以用同样的方法对本机的 UDP 透明代理,但我没有实际测试过效果。
 

开机自动运行透明代理规则

 
由于策略路由以及 iptables 有重启会失效的特性,所以当测试配置没有问题之后,需要再弄个服务在开机时自动配置策略路由和 iptables,否则每次开机的时候就要手动来一遍了。
 
  1. 由于 iptables 命令有点多,所以先将 iptables 规则保存到 /etc/iptables/rules.v4 中。
 
mkdir -p /etc/iptables && iptables-save > /etc/iptables/rules.v4
 
  1. 在 /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
 
  1. 执行下面的命令使 tproxyrule.service 可以开机自动运行。
 
systemctl enable tproxyrule
 

其他

 

解决 too many open files 问题

 
对 UDP 透明代理比较容易出现”卡住“的情况,这个时候细心的朋友可能会发现日志中出现了非常多 "too many open files" 的语句,这主要是受到最大文件描述符数值的限制,把这个数值往大调就好了。设置步骤如下。
 
  1. 修改 /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
 
  1. 执行 systemctl daemon-reload && systemctl restart v2ray 生效。
 

设定网关为静态 IP

 
最好给网关设成静态 IP,以免需要重启的时 IP 发生变化。如何设置请自行探究。 提示一下,如果你用 nmcli 命令设置静态 IP,最好先另外添加一个 connection 进行配置,配置好之后在切换到新添加的这个 connection 来。因为如果在原有的 connection 上直接修改成静态 IP 可能会导致无法透明代理
 

设定 DHCP

 
在路由器上设定 DHCP,将网关地址指向网关设备,在本文的举例中即为树莓派的 IP 192.168.1.22; DNS 随意,因为已经配置了劫持 53 端口的 UDP,当然填常规的 DNS 也更是没有问题的。
 

备注

  1. TPROXY 与 REDIRECT 是针对 TCP 而言的两种透明代理模式,两者的差异主要在于 TPROXY 可以透明代理 IPV6,而 REDIRECT 不行,本文主要是将透明代理模式改为 TPROXY 并且使用了 V2Ray 的 DNS。但我没有 IPV6 环境,无法进行测试,所以本文只适用于 IPV4。
  2. 据我了解,到目前(2019.10)为止,在我所知的具备透明代理功能的翻墙工具中,TCP 透明代理方式可以使用的 TPROXY 的只有 V2Ray。所以你要找其他资料参考的话,要注意透明代理方式,因为基本上都是 REDIRECT 模式的(包括 V2Ray 官网给的示例)。
  3. 在透明代理中,不要用 V2Ray 开放 53 端口做 DNS 服务器。如果这么做了,DNS 会出问题,这应该是个 BUG。(详情见此 Issue
  4. )
  5. 我用 NatTypeTester
 测试过 NAT 类型,结果是 FullCone,但也看到有反馈说玩游戏依然是 PortRestrictedCone。我也不清楚是怎么回事,这点需要玩游戏的朋友来确认了。不过目前测试发现代理 QUIC 的效果还不不错的。 V2Ray 仍然不支持 FullCone,详情见此 Issue
 

参考资料

漫谈各种黑科技式DNS技术在代理环境中的应用

Linux transparent proxy supportV2Ray

透明代理样例