Pages

Saturday, 2 May 2020

在linux桌面系统上,用v2ray做全局代理(透明代理)

透明代理(TPROXY)

原来出过一篇透明代理的教程,但过了许久,V2Ray 也已经迭代了好多个版本。原来的教程依旧可以正常使用,但随着 V2Ray 的更新,V2Ray 推出了新的透明代理方式—— TPROXY,原来的叫 REDIRECT。最近测试了一下 TPROXY ,效果还不错,主观感觉比 REDIRECT 好。并且在本文的透明代理中,DNS 服务将由 V2Ray 提供。不过这种方式需要 iptables 的 TPROXY 模块支持,有一些阉割版的系统会精简掉 TPROXY 模块,这种系统是不适用于本文的。

普通家庭大多数是光纤入户接光猫调制解调,一个路由器的 WAN 口接网线,要上网的设备(如 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. 我用 NatTypeTester 测试过 NAT 类型,结果是 FullCone,但也看到有反馈说玩游戏依然是 PortRestrictedCone。我也不清楚是怎么回事,这点需要玩游戏的朋友来确认了。不过目前测试发现代理 QUIC 的效果还不不错的。 V2Ray 仍然不支持 FullCone,详情见此 Issue

参考资料

from https://guide.v2fly.org/app/tproxy.html
-----------------------------------------------------

相关帖子:https://briteming.blogspot.com/2018/08/v2ray.html
-------------------------------------------------------

https://github.com/v2fly/v2ray-step-by-step
-------------------------------------------------------

Transparent proxy on OpenWrt based router.

OTransproxy

OTransproxy stands for a transparent proxy on OpenWrt. OTransproxy is a simple script to implement a transparent proxy. V2Ray is used as the back-end proxy software. It forwards foreign network traffic to your V2Ray server, but bypass the local (Chinese sites) traffic. You can also choose which site to bypass/proxy by simply modify the script. My intention in writing this script is to learning shell programming skill. Using at your own risk.

Typical usage

Install

  1. Download the OTransproxy and configurations:
mkdir -p /opt/OTransproxy && \
cd /opt/OTransproxy && \
wget https://raw.githubusercontent.com/pwrliang/OTransproxy/master/otransproxy.sh && \
wget https://raw.githubusercontent.com/pwrliang/OTransproxy/master/config.json && \
wget https://raw.githubusercontent.com/pwrliang/OTransproxy/master/bypass.list && \
wget https://raw.githubusercontent.com/pwrliang/OTransproxy/master/proxy.list && \
chmod +x otransproxy.sh
  1. Custom you own config.json of V2Ray. Fill your IPs/domains of V2Ray servers into bypass.list, otherwise it causes INFINITELY proxy.
  2. ./otransproxy.sh --install This step will download V2Ray binaries and rules.
  3. ./otransproxy.sh --start This step will start V2Ray and configure iptables and dnsmasq.
  4. ./otransproxy.sh --enable This step will startup OTransproxy service when system up.
Now you are good to go. You can run curl https://google.com to test the network connectivity.

Uninstall

OTransproxy will not dirty your system. You can easily run the following commands to remove it.
  1. /opt/OTransproxy/otransproxy.sh --stop
  2. /opt/OTransproxy/otransproxy.sh --disable
  3. rm -rf /opt/OTransproxy

Update rules automatically

The following operation implements update rules atomically at 5 A.M. for every day.
  1. crontab -e
  2. append 0 5 * * * /opt/OTransproxy/otransproxy.sh --update-rules

Sample output

BusyBox v1.28.3 () built-in shell (ash)

  _______                     ________        __
 |       |.-----.-----.-----.|  |  |  |.----.|  |_
 |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
 |_______||   __|_____|__|__||________||__|  |____|
          |__| W I R E L E S S   F R E E D O M
 -----------------------------------------------------
 OpenWrt 18.06.1, r7258-5eb055306f
 -----------------------------------------------------
root@GL-AR750:/opt# mkdir -p /opt/OTransproxy && \
> cd /opt/OTransproxy && \
> wget -q https://raw.githubusercontent.com/pwrliang/OTransproxy/master/otransproxy.sh && \
> wget -q https://raw.githubusercontent.com/pwrliang/OTransproxy/master/config.json && \
> wget -q https://raw.githubusercontent.com/pwrliang/OTransproxy/master/bypass.list && \
> wget -q https://raw.githubusercontent.com/pwrliang/OTransproxy/master/proxy.list && \
> chmod +x otransproxy.sh
root@GL-AR750:/opt/OTransproxy# vi bypass.list
root@GL-AR750:/opt/OTransproxy# vi config.json
root@GL-AR750:/opt/OTransproxy# ./otransproxy.sh --install
Tue Oct  8 15:01:07 UTC 2019 - Checking V2Ray version
Tue Oct  8 15:01:17 UTC 2019 - Downloading V2Ray v4.20.0
Downloaded
Unziping...
V2Ray installed
Tue Oct  8 15:11:21 UTC 2019 - Detected rules changed, downloading...
Tue Oct  8 15:11:40 UTC 2019 - Generating /opt/OTransproxy/tmp/transproxy_accelerated_domains.conf
Tue Oct  8 15:12:00 UTC 2019 - China route list downloaded
root@GL-AR750:/opt/OTransproxy# ./otransproxy.sh --start
Tue Oct  8 15:13:00 UTC 2019 - V2Ray has been started
Tue Oct  8 15:13:00 UTC 2019 - Copy dnsmasq configurations
Tue Oct  8 15:13:00 UTC 2019 - Restart dnsmasq
Tue Oct  8 15:13:02 UTC 2019 - Creating ipset
Tue Oct  8 15:13:04 UTC 2019 - Applying iptables
Tue Oct  8 15:13:04 UTC 2019 - Optimize network
root@GL-AR750:/opt/OTransproxy# ./otransproxy.sh --enable
root@GL-AR750:/opt/OTransproxy# curl google.com
<HTML>"content-type"
content="text/html;charset=utf-8"> <TITLE>301 Moved</TITLE>> <H1>301 Moved</H1> The document has moved <A HREF="http://www.google.com/">here</A>. </BODY>>

Note

  • Only tested and works on following platforms: Koolshare x86_64, OpenWrt mips
  • The config.json, bypass.list and proxy.list must be putted with otransproxy.sh under the same location.
  • Requirements: dnsmasq, iptables, ipset.
  • You must add your IPs/domains of V2Ray servers into bypass.list to avoid infinitely proxy.
  • The script will try to parse the configuration of dnsmasq under the /tmp/resolv.conf.auto to get the upstream DNS server. If the file does not exist, 192.168.1.1 will be used as upstream DNS server.
  • You must append the following JSON for each outbound. This prevents iptables forward the network traffic infinitely.
    "streamSettings": {
        "sockopt": {
          "mark": 255
        }
      } 
  • Currently, OTransproxy only supports forwarding TCP traffic.

----

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"
        }
      ]
    }
  }
}

(我按照上面的配置文件,想搭配mac系统的pf做全局代理,没成功)

配置里有几个重点要说下,第一个是 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 数据包流程图:

https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg

从这张图中我们可以看出对于其他设备过来的流量都应该在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

参考文档

家庭网接入V2RAY透明代理

上文详细解释了在 OpenWrt 上配置 V2RAY 作为透明代理,本文提供两种将这种透明代理应用到家庭局域网的两种方式。主要目标有以下几个:
  1. 兼容各种设备,比如 IPTV,NAS
  2. 接入设备无感知,无需做任何配置
  3. 设备间互访不受影响

下图为应用透明代理前我的家庭网拓扑:


由于联通路由光猫如果改成光猫的话 IPTV 就不好使了,所以这个猫比如像其名字一样作为家庭网关了。

方案 1 嵌套路由器模式

这种方案的网络拓扑图如下:

直接把极路由刷上 OpenWrt 改成路由模式,然后上透明代理就可以了。这种方案优点就是配置成本很小,缺点是电视,IPTV 和 NAS 和手机电脑等设备被分割到了两个局域网内,这就造成了:

  1. 只能单向发起通信,比如电脑可以访问 NAS,而 NAS 就不能直接访问电脑了
  2. AirPlay 之类的就不好使了,手机没法投屏到电视了:

方案 2 两个网关模式

这种方案拓扑图如下:


极路由还是工作在 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 单路由器模式

这种方案拓扑图如下:

首先,修改联通路由光猫改成桥接模式让其只作为猫使用,具体可以参考这个帖子,具体方法如下:

  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,然后在 vlanid 里填 3961(这里是北京联通的 vlanid,我测试是可以的)
  4. iptv 那块选 dhcp,vlan_i 填 3964 即可(这步我做了但是 iptv 并不能工作,IPTV 能正常工作的朋友请分享下你的配置吧)

然后极路由作为网关和透明代理,所有的设备都以极路由为网关,而且 DHCP 这块不需要啥特殊配置了。但是路由光猫改成桥接模式后 IPTV 就不好使了,所以这种场景不适合我,如果你不需要 IPTV,那么这种模式最为理想,当然得有个比较牛逼的路由器哇。

参考文档

  • --------------------------------

  • V2Ray透明代理/透明网关/路由器翻墙

  • 这篇文章主要记录V2Ray当作透明代理时客户端的配置。
  • 首先我在PVE里面开了一台虚拟机,安装了Debian9,使用ROOT权限登录进去开启IPv4转发:

    echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf && sysctl -p

    安装系统的时候使用了DHCP,这里因为要当成网关(旁路由)使用,所以编辑网卡配置文件,改为静态IP:

    nano /etc/network/interfaces

    我的具体配置:

    allow-hotplug ens18
    iface ens18 inet static
     address 192.168.0.11 # 这台虚拟机的IP
     gateway 192.168.0.1 # 上级路由的IP
     netmask 255.255.255.0

    重启网络服务,因为是在PVE内的虚拟机,可能重启网络服务会失败,那么直接重启虚拟机:

    systemctl restart networking.service
    reboot

    重启登录上来之后,开始安装V2Ray,因为是国内的网络环境,官方的install.direct一键安装脚本无法正常使用,所以这里改为手动安装,首先下载最新版本的V2Ray/解压:

    mkdir -p /opt/v2ray && cd /opt/v2ray
    wget https://github.com/v2ray/v2ray-core/releases/download/v4.20.0/v2ray-linux-64.zip
    unzip v2ray-linux-64.zip

    创建需要的目录:

    mkdir -p /usr/bin/v2ray /etc/v2ray

    移动文件到对应的目录:

    cp v2ctl /usr/bin/v2ray
    cp v2ray /usr/bin/v2ray
    cp geoip.dat /usr/bin/v2ray
    cp geosite.dat /usr/bin/v2ray
    cp vpoint_vmess_freedom.json /etc/v2ray/config.json
    cp systemd/v2ray.service /etc/systemd/system/v2ray.service

    编辑systemd服务文件:

    nano /etc/systemd/system/v2ray.service

    在[Service]下面加一行,解决too many open files的问题:

    LimitNOFILE=1048576

启动V2Ray服务:

systemctl daemon-reload
systemctl start v2ray.service 
systemctl enable v2ray.service

现在编辑V2Ray的配置文件,清空里面的所有配置:

nano /etc/v2ray/config.json

这里给出我目前正在用的两份配置文件,第一种就相当于是全局代理,所有流量都走代理:

{
  "inbounds": [
    {
      "port": 1080,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true
      }
    },
    {
      "port": 12315, // 透明代理开放的端口号
      "protocol": "dokodemo-door",
      "settings": {
        "network": "tcp,udp",
        "followRedirect": true // 这里要为true才能接受来自iptables的流量
      },
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "lala.im", // 服务器地址,请修改为你自己的服务器IP或域名。
            "port": 50000,  // 服务器端口,与服务器上的配置文件要相同
            "users": [
              {
                "id": "你的UUID",  // 用户的UUID必须与服务器端配置相同
                "alterId": 64 // 此处的值也应当与服务器相同
              }
            ]
          }
        ]
      }
    }
  ]
}

第二种对流量进行智能路由,需要的域名走代理,国内的域名/IP则直连,因为这套配置还使用了外部GEO文件,所以要想正常使用,还需要先下载外部GEO文件到V2Ray的运行目录:

wget https://github.com/ToutyRater/V2Ray-SiteDAT/raw/master/geofiles/h2y.dat -O /usr/bin/v2ray/h2y.dat

配置如下:

{
  "inbounds": [
    {
      "port": 1080,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": true
      }
    },
    {
      "port": 12315, // 透明代理开放的端口号
      "protocol": "dokodemo-door",
      "settings": {
        "network": "tcp,udp",
        "followRedirect": true // 这里要为true才能接受来自iptables的流量
      },
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      }
    }
  ],
  "outbounds": [
    {
      "tag": "proxy", // 打一个TAG,让外部GeoFile使用此TAG处理被GFW屏蔽的域名
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "lala.im", // 服务器地址,请修改为你自己的服务器IP或域名。
            "port": 50000,  // 服务器端口,与服务器上的配置文件要相同
            "users": [
              {
                "id": "你的UUID",  // 用户的UUID必须与服务器端配置相同
                "alterId": 64 // 此处的值也应当与服务器相同
              }
            ]
          }
        ]
      }
    },
    {
      "tag": "block", // 黑洞TAG,让外部GeoFile使用此TAG屏蔽广告域名
      "protocol": "blackhole",
      "settings": {}
    },
    {
      "tag": "direct", // 直连TAG,处理国内域名和IP使其直连
      "protocol": "freedom",
      "settings": {}
    }
  ],
  "routing": {
    "domainStrategy": "IPOnDemand",
    "rules": [
      {
        "type": "field",
        "outboundTag": "proxy",
        "domain": ["ext:h2y.dat:gfw"] // GFWList
      },
      {
        "type": "field",
        "outboundTag": "block",
        "domain": ["ext:h2y.dat:ad"] // 广告域名屏蔽
      },
      {
        "type": "field",
        "outboundTag": "direct",
        "domain": ["geosite:cn"] // 中国大陆主流网站的域名
      },
      {
        "type": "field",
        "outboundTag": "direct",
        "ip": [
          "geoip:cn", // 中国大陆的IP
          "geoip:private" // 私有地址IP,如路由器等
        ]
      }
    ]
  }
}

配置完成之后测试:

/usr/bin/v2ray/v2ray -config /etc/v2ray/config.json -test

有错排错没问题的话重启V2Ray:

systemctl restart v2ray.service

最后创建iptables规则,对流量进行处理:

iptables -t nat -N V2RAY
iptables -t nat -A V2RAY -d 192.168.0.0/16 -j RETURN
iptables -t nat -A V2RAY -p tcp -j REDIRECT --to-ports 12315
iptables -t nat -A PREROUTING -p tcp -j V2RAY

局域网内的其他机器,修改网关IP为这台虚拟机的IP即可:


现在只要接入你这个网络的设备,无论是电脑还是手机等,都可以实现自动翻墙/广告屏蔽等功能。

一点补充:

如果你使用第一套配置(全局代理)其实也可以实现对流量进行路由,并且据说这种路由方法比V2Ray自带的原生GeoIP方法效率更高,更适合跑在CPU性能不咋地的路由器上。

先重启机器清空所有iptables规则:

reboot

安装ipset:

apt -y install ipset

下载中国IP地址列表:

wget https://raw.githubusercontent.com/17mon/china_ip_list/master/china_ip_list.txt

创建一个ipset链:

ipset -N cn hash:net

将中国的IP都加入到ipset链:

for i in $(cat china_ip_list.txt); do ipset -A cn $i; done

执行下面的命令处理流量:

iptables -t nat -N V2RAY
iptables -t nat -A V2RAY -d 192.168.0.0/16 -j RETURN
iptables -t nat -A V2RAY -p tcp -m set --match-set cn dst -j RETURN
iptables -t nat -A V2RAY -p tcp -j REDIRECT --to-ports 12315
iptables -t nat -A PREROUTING -p tcp -j V2RAY
----------------------------------------------------------------


差不多用了5年的ss透明代理,最近换成了v2ray。ss透明代理的好处是配置方便、生态成熟、对硬件要求不高。

只要不是太老的路由器,可刷OpenWRT,就基本可以用ss透明代理,就连很多年前的TP link 703N也有不少玩家魔改内存上OpenWRT。

因为一段时间前,ss官方推荐换v2ray混淆插件而不是simple obfs了,后来我试了一次,在路由器上运行非常缓慢,而且我的路由器还不错,Linksys 1900ac v2,就放弃了。最近又想折腾一下,于是用电脑上的v2ray试了下,飞一般的速度,同时用ss并不会达到这样的速度,于是开始着手准备v2ray透明代理搭建。

V2Ray透明代理

如果v2ray没法用透明代理我可能也还会将就ss。因为目前的路由器没法直接运行v2ray,空间和性能都跟不上了,考虑软路由,我的youtube首页总有几个视频是软路由选购、测评。最终没有买,用NAS开了台虚拟机安装v2ray,作为旁路由接入。其实用软路由多半也是用旁路由的模式。另外还考虑了买下一代Linksys产品,再刷openWRT安装v2ray,本着艰苦创业的精神,放弃了。

旁路由也类似一台设备接入到主路由中,只不过有一部分特别的职责,它可以处理其他设备发来的请求,处理完后还会发回对应设备,作用与主路由的部分功能类似。

一般家庭网络中只有一个网关,作用是连接内部与外部网络,网络内部设备需要访问互联网时,例如要访问百度,则需要通过网关与外界沟通,在百度看来,一直与网关沟通,而不知道网络中的那一台设备在和自己通信,百度也并不关心。

所以最简单的配置就是把旁路由配置成网关,这样网络内其他设备需要访问外网时就会与这台旁路由联系,而旁路由本身并不能直接访问外网,它需要继续与上级设备沟通,而上级设备就是主路由了。

因为还在实验,不想让所有设备都走旁路由,而且需要出国的设备也不多,没必要都接入到旁路由。于是采用了更复杂的路由配置——源地址路由。

source route

在主路由上添加了一个路由表,修改/etc/iproute2/rb_tables

10	v2ray

添加默认网关

ip route add default via 192.168.1.32 table 10

给路由表10添加默认网关,没有其他规则,这样进入该路由表的流量就都转入到了192.168.1.32了,这是旁路由的IP

再添加一条规则

ip rule add from 192.168.1.240/32 table 10

意思是来自192.168.1.240的流量走路由表10,而进入了路由表10就会继续进入到旁路由

不过这样添加很麻烦,每次需要让某个设备出国的话需要在主路由上添加ip rule,后面针对这个做了优化。

透明代理配置

v2ray配置主要包含inboundoutbounddnsrouting

inbound

{
      "tag": "transparent",
      "port": 12345,
      "protocol": "dokodemo-door",
      "settings": {
        "network": "tcp,udp",
        "followRedirect": true
      },
      "sniffing": {
        "enabled": true,
        "destOverride": [
          "http",
          "tls"
        ]
      },
      "streamSettings": {
        "sockopt": {
          "tproxy": "tproxy"
        }
      }
    }

协议dokodemo-door,监听12345端口,使用tproxy方式完成透明代理。

本想省略followRedirect,文档描述这个参数设置成true才可以处理iptablesredirect,但我用的是tproxy,理论讲可以省略,然而省略了就不行。

network配置tcp,udp表示接受这两种协议的流量。

tproxy可用tproxy或者redirect,两种透明代理的方法,推荐tproxy,可以代理IPv4/6流量、UDP流量;redirect只能IPv4和UDP。代理UDP流量的目的是让DNS解析也能走代理,因为某些网站的DNS解析也会被恶意篡改,即便你的代理再好再快,如果不能得到正确IP地址的话,一样无法正常访问网站。

sniffing配置,可以从流量中解析出域名和协议,尝试解析,并不是所有流量都能得到域名和协议。猜测:如果解析出来了,并且与配置的destOverride匹配的话,那么就会修改这些流量的outbound target,因为在没有解析的时候只能得到IP,用来匹配outbound,但如果有了域名、协议,就可以做更精确的路由,比如解析出了bt协议,可以选择禁止bt还是其他怎样。

outbound

没啥特别,第一个是proxy的配置

{
      "tag": "proxy",
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "your proxy address",
            "port": your proxy port,
            "users": [
              {
                "id": "your id",
                "alterId": your alert id
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "tls",
        "sockopt": {
          "mark": 255
        }
      },
      "mux": {
        "enabled": false
      }
    }

streamSettings根据需要填写networkingsecurity,但需要注意sockopt设置mark是透明代理需要。

mux配置默认关闭就好,文档没看清楚,以为开着看视频啥的更好,结果并不是,是更差。开着的话看youtube视频能到10W,关闭后能到20W。。。同一时间同一个server。

另外几个outbound

{
      "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
        }
      }
    }

directdns-out没啥特别,要mark

DNS

v2ray内建的dns配置

{
    "servers": [
      "8.8.8.8",
      "1.1.1.1",
      {
        "address": "114.114.114.114",
        "port": 53,
        "domains": [
          "geosite:cn",
          "domain:ntp.org",
          "domain:zoom.us",
          "domain:slack.com",
          "domain:slack-core.com"
        ]
      },
      "223.5.5.5"
    ]
  }

国内域名、ntp、其他需要用国内DNS解析的可以继续添加。其他域名就从8.8.8.8开始解析了。

routing

这是最复杂的一部分,几乎能和上面各个部分联系起来。

{
    "domainStrategy": "IPIfNonMatch",
    "rules": [
      { "type": "field", "inboundTag": [ "transparent" ], "port": 53, "network": "udp", "outboundTag": "dns-out" },
      { "type": "field", "inboundTag": [ "transparent" ], "port": 123, "network": "udp", "outboundTag": "direct" },
      { "type": "field", "ip": [ "223.5.5.5", "114.114.114.114" ], "outboundTag": "direct" },
      { "type": "field", "ip": [ "8.8.8.8", "1.1.1.1" ], "outboundTag": "proxy" },
      { "type": "field", "ip": [ "geoip:private", "geoip:cn" ], "outboundTag": "direct" }, 
      { "type": "field", "domain": [ "vali.cp31.ott.cibntv.net" ], "outboundTag": "direct" },
      { "type": "field", "domain": [ "geosite:category-ads-all" ], "outboundTag": "block" },
      { "type": "field", "domain": [ "geosite:cn" ], "outboundTag": "direct" }
    ]
}

Routing控制流入v2ray的流量该如何流出,主要目的就是让该走代理的流量走代理,不该走的不走。

目前的规则:

  • 进入的udp流量,端口是53的,走dns-out, dns-out就是mark一下
  • 进入的udp流量,端口是123的,走direct,直连
  • 114DNS和aliDNS走direct
  • GoogleDNS和CloudflareDNS走proxy
  • 内网地址IP、国内IP走direct
  • vali.cp31.ott.cibntv.net这个域名是youku播放视频前需要访问的,默认被放在了广告域名中,会被下一条规则block,但在使用投影仪投屏时,如果访问不到这个域名,视频播放就有问题,于是放行了
  • geosite:category-ads-all表示所有的广告域名,会被block
  • 最后,国内网站域名,直连
  • 匹配不到的,走outbound第一个,即proxy

domainStrategy可选值:"AsIs" | "IPIfNonMatch" | "IPOnDemand”。

这三个可选值,不管选哪一个,要求都是能解析出域名,解析不出的话,我猜测还是用IP匹配。

默认AsIs,只用域名选择路由。猜测意思是能解析出域名的,会用域名在rules中匹配规则;没有域名的,用IP匹配。

进一步,IPIfNonMatch,如果有一个域名匹配不了,那么会解析域名,解析出来的IPs,会再次匹配。

最后,IPOnDemand,如果rules中有IP相关的规则,就会把域名解析成IP再匹配规则。

让udp流量不要进入v2ray:

sudo iptables -t mangle -A V2RAY -p udp -m multiport --dport 3478,3479,8801:8810 -j RETURN

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

v2ray 结合 iptables 做透明代理

有些软件或者游戏没法设置代理,又需要走 v2ray 的时候,用 dokodemo-door 这个 protocol 配合 iptables 可以实现局域网其他机器无感知透明代理。

很简单,先修改 config.json,多加一个 dokodemo-door 协议的 inboundDetour,然后给代理的 outbound 加个 tag 比如 proxy

"inboundDetour": [
    {
    	"protocol": "dokodemo-door",
    	"port": 8081,
    	"domainOverride": ["tls","http"],
    	"settings": {
    		"network": "tcp,udp",
    		"followRedirect": true
    	},
    	"tag":"forwardingport"
    }
]

在 routing.settings.rules 里多加一个策略

"routing": {
    {
        "type": "field",
        "inboundTag": ["forwardingport"],
        "outboundTag": "proxy"
    }
}

重启 v2ray,在命令行下 root 身份执行

iptables -t nat -N V2RAY
iptables -t nat -A V2RAY -d 192.168.2.0/16 -j RETURN # 局域网网段
iptables -t nat -A PREROUTING -p tcp -j V2RAY # tcp 全部走 V2RAY 这个链
iptables -t nat -A PREROUTING -p udp -j V2RAY # udp 全部走这个链

这样准备工作就做好了,这只是流量都走到 V2RAY 这个 nat chain,现在这个 chain 还没有任何规则,跟没加是一样的。

然后把需要代理的流量转发到本地 dokodemo-door 端口,比如目标全部 tcp 21 端口的流量

iptables -t nat -A V2RAY -p tcp --dport 21 -j REDIRECT --to-ports 8081

比如全部来自 192.168.2.100 的流量

iptables -t nat -A V2RAY -p tcp -s 192.168.2.100 -j REDIRECT --to-ports 8081

全部流量都透明代理

iptables -t nat -A V2RAY -j REDIRECT --to-ports 8081

删除某条规则的话直接把 -A 换成 -D,或者 iptables -t nat -F V2RAY 全清空。


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

一文玩转openwrt路由器上的V2ray透明代理


本文主要是自己配置过程的流水帐,也算作是一个备份,防止以后再弄的时候忘了流程。本文使用openwrt-v2rayluci-app-v2ray作为配置工具,前者为V2ray的Openwrt的二进制文件,后者为可视化的配置工具。经过一段时间的摸索,发现该配置工具比原版V2ray+手动配置透明代理要简单的多,希望能够帮助到正在研究V2ray+透明代理的玩家。


更换源

1.安装支持https的工具

如果你想快速安装软件,那么必须换成国内的openwrt的源,但是目前国内好用的源都是https的。

opkg update
opkg install ca-certificates luci-ssl-openssl


2.替换系统源(推荐)

编辑源文件/etc/opkg/customfeeds.conf,我是软路由,所以是x86_64,请根据自己实际情况替换这部分。最后的kmod那条可加可不加,这个自己随意。编辑好自定义源文件后,还要把同目录下系统自带的源文件distfeeds.conf里的内容全部注释掉或删除。自定义源文件内容如下:

src/gz openwrt_core https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/19.07.1/targets/x86/64/packages
src/gz openwrt_base https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/19.07.1/packages/x86_64/base
src/gz openwrt_luci https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/19.07.1/packages/x86_64/luci
src/gz openwrt_packages https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/19.07.1/packages/x86_64/packages
src/gz openwrt_routing https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/19.07.1/packages/x86_64/routing
src/gz openwrt_telephony https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/19.07.1/packages/x86_64/telephony
src/gz openwrt_kmods https://mirrors.tuna.tsinghua.edu.cn/openwrt/releases/19.07.1/targets/x86/64/kmods/4.14.167-1-e1dd7676581672f6f0bdb1363506dee1


如果不知道自己的架构,可以使用下面的命令查询

opkg print-architecture | awk '{print $2}'


3.安装openwrt-v2ray

openwrt-v2ray作者非常良心,提供了多种架构的编译文件,以及可以在线更新的源,方便日后更新使用,但我们要先把第三方源的key导入才能正常使用。

wget -O kuoruan-public.key http://openwrt.kuoruan.net/packages/public.key
opkg-key add kuoruan-public.key


然后在上面提到的自定义源里增加下面的内容,注意替换架构目录。

src/gz kuoruan_packages https://openwrt.kuoruan.net/packages/releases/x86_64/
src/gz kuoruan_universal https://openwrt.kuoruan.net/packages/releases/all


4.添加Shadowsocks源(可跳过)

方法同上,这里就不赘述了。需要说明的是,这个源是安装ss和dns-forward这类工具的源,有需要可以添加,没需要就跳过吧,我下面的教程没有用到这里的相关程序。

wget http://openwrt-dist.sourceforge.net/openwrt-dist.pub
opkg-key add openwrt-dist.pub


注意替换为自己的架构

src/gz openwrt_dist http://openwrt-dist.sourceforge.net/packages/base/x86_64/
src/gz openwrt_dist_luci http://openwrt-dist.sourceforge.net/packages/luci


安装软件

5.替换dnsmasq

系统自带的dnsmasq功能不全,需要替换掉,而dnsmasq又是系统重要的组成部分(负责分配ip的dhcp和dns解析那部分工作),所以一旦删除,可能会短暂的无法联网,需要先下载到本地再安装,下面的方法和顺序都是用血泪探索出来的。

注意,我下面的代码没有写错,第一条是下载ipk文件到本地,第二条dnsmasq-full安装一定会失败,但目的是让系统自动安装所需要的依赖。

opkg download dnsmasq-full
opkg install dnsmasq-full
opkg remove dnsmasq
opkg install dnsmasq-full_2.80-15_x86_64.ipk
rm dnsmasq-full_2.80-15_x86_64.ipk 


6.全面更新(可以跳过)

我是更新强迫党,要尽量保持软件最新。

opkg update
opkg list-upgradable | cut -f 1 -d ' ' | xargs opkg upgrade


7.安装全部插件

下面包含了必要软件、系统工具和部分中文翻译补丁,建议全部安装,如果路由器空间实在不够,可以看看我写的Extroot教程。

opkg update
opkg install luci-i18n-base-zh-cn uhttpd libuhttpd-openssl luci-app-uhttpd luci-i18n-uhttpd-zh-cn ip-full ipset iptables-mod-tproxy iptables-mod-nat-extra libpthread coreutils-base64 ca-bundle curl vim-full vim-runtime v2ray-core luci-app-v2ray luci-i18n-v2ray-zh-cn


安装好openwrt-v2ray后需要为geo文件增加执行权限,不然启动可能报错, 即使你用不到这两个文件。

chmod a+x /usr/bin/geoip.dat 
chmod a+x /usr/bin/geosite.dat


可视化配置V2ray

配置v2ray其实是一个很痛苦的过程,一旦按照官方弄好就懒得再去折腾。我开始使用luci-app-v2ray的时候就觉得摸不着头脑,只想直接加载配置好的json文件,但是这样弄真是问题多多,需要手动下载更新中国CIDR文件和GFWlist文件,还需要配置dnsmasq和ipset,然后再配合iptables等等,一旦重装,这些东西想想就头疼。后来经过几次尝试,发现luci-app-v2ray其实非常的好用,上面说的问题只要配置好,都是一键启动,这里把我的配置步骤一步步贴出来,供还没有找到门路的各位参考,而且luci-app-v2ray的官方文档也做的不好,这里就当为他们做个推广教程。


入站连接

按照上面安装好所有软件后,我们要从服务—V2Ray进入主界面,先不看全局设置,来看看入站连接,如图

openwrt-v2ray_2.png这是默认就带的一个配置,没有的话就按照我图中的完整复制一份也可以,你只需要修改一个你喜欢的端口号就可以了,注意别把协议改了。


出站连接

然后来看看出站连接,下图是整体配置,绿色框内是我把默认配置删除后留下的两个,完全没有修改。黄色框内是我新增的三台服务器,因为之后我要用到负载均衡,当然你就一台也没关系,不使用负载均衡就可以了。

openwrt-v2ray_3.png


随便看一个服务器的配置里,按照你自己服务器的连接方式配置好,相信搞过官方json配置文件的都能看明白这些配置的含义,我这里是用的WS+TLS,所以必须要用443端口。黄色框内是标识名,用于负载均衡,用不到的话可以不写。

openwrt-v2ray_4.png


路由

路由部分稍微复杂一点,同样,绿色框内是默认配置就带的,我没有修改。下方黄色框内是我自己添加的一个配置,用于真实的代理,这个之后再看,上方黄色框内打勾的是最后启动的配置,所以我只是启用了个别的路由配置。蓝色框内是负载均衡的配置,最下方的标识匹配要和你出站连接里的标识一致。

openwrt-v2ray_5.png


下面这个图就是我新增的真实代理路由规则,配置非常简单,含义就是所有TCP和UDP连接都走负载均衡器,当然如果你没有设置负载均衡,那么就要设置好出站连接标识。

openwrt-v2ray_6.png


上面我的代理配置为什么这么简单,还要把我的代理架构简单说一下。所有路由条件其实都在路由器的iptables/ipset层面完成了,能够被输送到v2ray的连接一定是被屏蔽了的,所以可以理解为只要进入v2ray路由系统的一定要走代理,我这里是在v2ray的路由系统中新增了BT下载和去广告的过滤,把这两个规则去掉其实就更好理解了。

v2ray透明代理架构.png


透明代理

搞懂了路由路线,再来看看最重要的透明代理设置,就是由于luci-app-v2ray有这部分的配置,省下玩家去搞iptables、ipset和dnsmasq这部分的工作。整体也是非常的简单,但是需要注意的是红框内的部分,我没有开启任何UDP相关的转发,因为一旦开启转发DNS,再配合我上面的路由,那么所有的DNS都要走国外,这让访问国内的网站非常的慢;如果开启UDP转发,那么所有网络都访问不通,这个原因我还没有弄清,不知道是bug还是我没有完全搞明白透明代理。蓝色框内就是帮你完成iptables、ipset和dnsmasq要完成的所有工作,弥补了上面有几率被DNS污染的缺点,也算是在简单和完美之间找到了一个平衡。

openwrt-v2ray_7.png


全局设置

最后全部设置完成再回过头来看看全局设置,黄色框内勾选好你要启用的各组件的配置,然后勾上已启用,最后保存并应用,不出意外,你的透明代理就搭建好了。

openwrt-v2ray_1.png


结语

V2ray的玩法很多,配套工具也很丰富,这里只是抛砖引玉,希望大家布不局限于此。我这套东西前前后后折腾了几个月才弄明白,现在已经在家里三个路由器上实验成功,最短也只需要1小时左右就能完全搞定,现在路由器重装起来也不发愁了。另外这套配置不能够代理Telegram手机客户端,添加IP也不行,不知道是不是与UDP有关系,我在入站、出站和路由上搞过MTproxy协议,但是一旦使用,v2ray程序就会崩溃,所以就没再尝试了。最后还有一个小技巧,luci-app-v2ray的配置文件在/etc/config/v2ray,每次只要保存了这个文件,在新的路由器上覆盖配置就能快速配置好哦。

from https://www.solarck.com/openwrt-v2ray.html

-----

http://gsoc-blog.ecklm.com/iptables-redirect-vs.-dnat-vs.-tproxy/

No comments:

Post a Comment