ss-redir 透明代理

shadowsocks-libev,基于 libev 库开发的 shadowsocks 代理套件;主要组件:ss-localss-redirss-tunnelss-server。2018/07/11 更新,ss-tproxy 代理脚本(合并了 ss-tun2socks 项目)支持 global、gfwlist、chnroute 三种代理模式,共 12 种子模式。

组件区别

ss-server

shadowsocks 服务端程序,核心部件之一,各大版本均提供 ss-server 程序。

ss-local

shadowsocks 客户端程序,核心部件之一,各大版本均提供 ss-local 程序。
ss-local 是运行在本地的 socks5 代理服务器,根据 OSI 模型,socks5 是会话层协议,支持 TCP 和 UDP 的代理。

但是现在只有少数软件直接支持 socks5 代理协议,绝大多数都只支持 http 代理协议。好在我们可以利用 privoxy 将 socks5 代理转换为 http 代理,使用 privoxy 还有一个好处,那就是可以实现 gfwlist 代理模式,如果你对这种方式感兴趣,可以看看 ss-local 全局代理

ss-redir

shadowsocks-libev 提供的socks5 透明代理工具,也就是今天这篇文章的主题 - 实现透明代理!

正向代理
正向代理,即平常我们所说的代理,比如 http 代理、socks5 代理等,都属于正向代理。
正向代理的特点就是:如果需要使用正向代理访问互联网,就必须在客户端进行相应的代理设置

透明代理
透明代理和正向代理的作用是一样的,都是为了突破某些网络限制,访问网络资源。
但是透明代理对于客户端是透明的,客户端不需要进行相应的代理设置,就能使用透明代理访问互联网

反向代理
当然,这个不在本文的讨论范畴之内,不过既然提到了前两种代理,就顺便说说反向代理。
反向代理是针对服务端来说的,它的目的不是为了让我们突破互联网限制,而是为了实现负载均衡。

举个栗子:
ss-local 提供 socks5 正向代理,要让软件使用该代理,必须对软件进行相应的代理配置,否则不会走代理;
ss-redir 提供 socks5 透明代理,配置合适网络规则后,软件会在不知情的情况下走代理,不需要额外配置。

ss-tunnel

shadowsocks-libev 提供的本地端口转发工具,通常用于解决 dns 污染问题。

假设 ss-tunnel 监听本地端口 53,转发的远程目的地为 8.8.8.8:53;系统 dns 为 127.0.0.1。
去程:上层应用请求 dns 解析 -> ss-tunnel 接收 -> ss 隧道 -> ss-server 接收 -> 8.8.8.8:53;
回程:8.8.8.8:53 响应 dns 请求 -> ss-server 接收 -> ss 隧道 -> ss-tunnel 接收 -> 上层应用。

方案说明

一直以来,Linux 中的 SS/SSR 客户端(尤其是命令行界面)远不如 Windows、Android 中的好用(没用过苹果),不要说什么 PAC、gfwlist、绕过大陆地址段,能不能用上它的代理都还不一定。先说说我自己在 Linux 上琢磨 SS/SSR 的经历吧,刚开始装的都是 Python 版的 SS/SSR(最初就是使用 Python 写的),有两个命令 ssserver、sslocal。ssserver 是服务端,sslocal 是客户端,按照网上的步骤,成功运行 sslocal,然后不知道怎么就断片了,稀里糊涂的认为直接将软件的代理设为 sslocal 的 127.0.0.1:1080 地址就行,结果显而易见,curl 没有反应,wget 也没有反应,然后又开始上网看看前辈们都是怎么用的,才知道原来 sslocal 提供的不是 http 代理,而是 socks5 代理。但是要在终端环境中使用 socks5 代理并不是那么容易的事(当然也不难),网上大多数的解决方案是再安装个 privoxy,用它来转发我们的 http 代理到 sslocal 的 socks5 代理。依葫芦画瓢后,终于成功让 curl、wget 等工具走了 sslocal 的代理(通过命令行参数),没多久我就觉得这样指定命令行参数的用法太麻烦,于是用 alias 别名来隐式的包含 proxy 参数(后来知道可以使用 http_proxy、https_proxy 等环境变量来设置代理),没多久又发现,这样做会让 baidu 等国内网站也走代理,速度会很慢,而且有时候还会出现比较奇怪的页面(B 站很多番看不了,说什么地区限制),不甘心的我又开始上网搜索,想知道如何在 privoxy 中使用 PAC 代理自动配置,当然没有找到所谓的 PAC,不过发现了 gfwlist.txt 这东西,于是又琢磨能不能将它用在 privoxy 上,实现所谓的 gfwlist.PAC 模式,一搜果然有,@snachx 提供了 gfwlist2privoxy python 脚本,不知道怎么搞的,运行出错,当时以为是脚本很久没更新导致的(后来才知道不是脚本的问题),所以索性自己写了个简陋的 shell 转换脚本,也叫 gfwlist2privoxy,用的 sed 和 grep,简单测试了能用之后,就放到了 github 上。第一阶段的折腾总算是结束了,用了几天后发现还不错,所以写了篇 ss-local 全局代理的博客,防止日后忘记。

ss-local + privoxy 方案用了有个把月吧,入手了一个树莓派 3B,计划用树莓派换掉家里的 TL-WR886N 渣路由,不过树莓派自带的 WiFi 真不咋地,信号很差,链路速度也只有 72M,所以又买了一个 USB-RJ45 转接器,让 TP-Link 发射 WiFi,树莓派拿来做网关,拨号用。顺便把 ss-local + privoxy 放到树莓派上,这样就不用在电脑和手机上运行 SS/SSR 了。想法很好,但是刚动手就发现还不如在每个设备上运行 SS/SSR 来的直接,因为要在每台设备上指定 http 代理服务器,也挺麻烦的。想着要是能够像 OpenWrt 路由器一样无缝翻墙多好啊,OpenWrt 应该也是 Linux,那么同样是 Linux 系统的树莓派应该也可以吧,按照这个思路,我开始谷歌,发现的确可以,不过要 shadowsocks-libev、shadowsocksr-libev 版本才行,因为要用到里面的 ss-redir 工具。一番折腾后(因为网上的教程太凌乱,有些还不全),也总算是弄好了,当然这里面踩的坑可一点都不少,这里就不多说了。最初用的是 chnroute 模式(绕过大陆地址段模式),因为 gfwlist 模式要用到 dnsmasq,而且也要像 privoxy 那样进行格式的转换,所以当前并没有考虑。因为透明代理需要配置 iptables 规则,每次重启后都要设置一些东西,所以写了个 ss-tproxy 管理脚本。这应该算第二个阶段了,ss-tproxy 方案一直使用到现在,期间也有不少人对它感兴趣(少数几个有人 star、fork 的项目),当然 ss-tproxy 也还有很多问题以及需要改进的地方,比如很多人要求的 gfwlist 模式,以及不使用 UDP Relay 的代理方案等等。由于各种原因,直到几天前我才开始着手重构 ss-tproxy 脚本(也就是现在的 v2 版本)。

重构后的 ss-tproxy 有点小复杂,因为 ss-tproxy 脚本快有 1000 行了(功力有限,只能这么长了),我也是第一次写这么长的 shell 脚本。单单写这个脚本就花了我一天的时间,而且还不能正常运行,各种 bug,逼的我又学会了 shell 脚本的调试,经过一天的调教后,总算可以正常使用了(还有很多未发现的问题还请你及时反馈哦),于是更新到了 github 上。但是还没完,因为我还要修改博客,对就是你看的这篇。我想这次更新后应该可以安稳一点了吧,因为写脚本和写博客真的很累的!

ss-tproxy v2 的核心理念是实现 Windows、Android 客户端中的常用模式,即全局模式、gfwlist 模式、chnroute 模式,因为分为 iptables 实现版本和 tun2socks 实现版本,所以有 6 种代理模式,而每种模式又可细分为支持 UDP Relay 版和不支持 UDP Relay 版,所以一共有 12 种子代理模式,以下是简要介绍(部署 ss-tproxy 的主机在这里称为本机,通过 ss-tproxy 主机上网的主机称为内网主机):

  • tproxy_global:代理 TCP/UDP(本机 UDP 除外),iptables/global 模式。
  • tproxy_global_tcp:仅代理 TCP(DNS 使用 TCP 方式查询),iptables/global 模式。
  • tproxy_gfwlist:代理 TCP/UDP(本机 UDP 除外),iptables/gfwlist 模式。
  • tproxy_gfwlist_tcp:仅代理 TCP(DNS 使用 TCP 方式查询),iptables/gfwlist 模式。
  • tproxy_chnroute:代理 TCP/UDP(本机 UDP 除外),iptables/chnroute 模式。
  • tproxy_chnroute_tcp:仅代理 TCP(DNS 使用 TCP 方式查询),iptables/chnroute 模式。
  • tun2socks_global:代理 TCP/UDP(包括本机 UDP),tun2socks/global 模式。
  • tun2socks_global_tcp:仅代理 TCP(DNS 使用 TCP 方式查询),tun2socks/global 模式。
  • tun2socks_gfwlist:代理 TCP/UDP(包括本机 UDP),tun2socks/gfwlist 模式。
  • tun2socks_gfwlist_tcp:仅代理 TCP(DNS 使用 TCP 方式查询),tun2socks/gfwlist 模式。
  • tun2socks_chnroute:代理 TCP/UDP(包括本机 UDP),tun2socks/chnroute 模式。
  • tun2socks_chnroute_tcp:仅代理 TCP(DNS 使用 TCP 方式查询),tun2socks/chnroute 模式。

tproxy 开头的模式使用 iptables 实现(非 tcp 结尾的模式需要 xt_TPROXY 内核模块),tun2socks 开头的模式使用 tun2socks + iproute2 实现。它们之间的主要区别有:tproxy 模式必须使用 ss-libev 或 ssr-libev,tun2socks 模式允许任意 SS/SSR 客户端,只要能提供 socks5 代理;tproxy 模式不能代理本机的 UDP(做网关时不重要,但是如果在普通主机上运行,可能就有点用处了),tun2socks 模式能够代理任意 TCP 和 UDP 流量,无论来自内网还是本机,但是它会损失一点儿性能。以 tcp 结尾的模式表示只代理 TCP 流量,UDP 一概不处理,主要适用于不支持 UDP Relay 的 SS/SSR 帐号,tcp 模式依赖 dnsforwarder(将 udp/53 的 dns 查询转换为 tcp/53),非 tcp 模式没有此项依赖。为了随心所欲的切换模式,建议把所有可选依赖都安装(也不需要多少空间),每个模式具体的依赖在下一节中会进行详细介绍。

安装依赖

  • tproxy_global: ss/ssr-libev, haveged, xt_TPROXY, iproute2, dnsmasq
  • tproxy_gfwlist: ss/ssr-libev, haveged, xt_TPROXY, iproute2, ipset, perl, dnsmasq
  • tproxy_chnroute: ss/ssr-libev, haveged, xt_TPROXY, iproute2, ipset, chinadns, dnsmasq
  • tproxy_global_tcp: ss/ssr-libev, haveged, dnsforwarder
  • tproxy_gfwlist_tcp: ss/ssr-libev, haveged, ipset, perl, dnsmasq, dnsforwarder
  • tproxy_chnroute_tcp: ss/ssr-libev, haveged, ipset, chinadns, dnsforwarder
  • tun2socks_global: ss/ssr(版本不限), haveged, tun2socks, iproute2, dnsmasq
  • tun2socks_gfwlist: ss/ssr(版本不限), haveged, tun2socks, iproute2, ipset, perl, dnsmasq
  • tun2socks_chnroute: ss/ssr(版本不限), haveged, tun2socks, iproute2, ipset, chinadns, dnsmasq
  • tun2socks_global_tcp: ss/ssr(版本不限), haveged, tun2socks, iproute2, dnsforwarder
  • tun2socks_gfwlist_tcp: ss/ssr(版本不限), haveged, tun2socks, iproute2, ipset, perl, dnsmasq, dnsforwarder
  • tun2socks_chnroute_tcp: ss/ssr(版本不限), haveged, tun2socks, iproute2, ipset, chinadns, dnsforwarder
  • haveged 依赖项是可选的,主要用于防止系统的熵过低,从而导致 ss-redir、ss-tunnel、ss-local 启动失败等问题
  • *gfwlist* 模式更新列表时依赖 curl、base64;*chnroute* 模式更新列表时依赖 curl;建议都安装(反正迟早要装)
  • *gfwlist* 模式中的 perl 其实可以使用 sed 替代,但由于更新 gfwlist 列表依赖 perl5 v5.10.0+,所以直接使用了 perl

curl

perl5

Perl5 的版本最好 v5.10.0+ 以上(使用 perl -v 命令可查看)

ipset

TPROXY

TPROXY 是一个 Linux 内核模块,在 Linux 2.6.28 后进入官方内核。一般正常的发行版都没有裁剪 TPROXY 模块,TPROXY 模块缺失问题主要出现在无线路由固件上。使用以下方法可以检测当前内核是否包含 TPROXY 模块,如果没有,请自行解决。

iproute2

haveged

如果有时候启动 ss-redir、ss-tunnel 会失败,且错误提示如下,则需要安装 haveged 或 rng-utils/rng-tools。虽然这个依赖是可选的,但强烈建议大家安装(并设为开机自启状态)。

这里以 haveged 为例,当然,你也可以选择安装 rng-utils/rng-tools,都是一样的:

dnsmasq

chinadns

dnsforwarder

ss-libev

shadowsocks-libevshadowsocksr-libev 二选一,也可一并安装

ArchLinux:建议使用 pacman -S shadowsocks-libev 安装,方便快捷,更新也及时。
CentOS/RHEL 及其它 Linux 发行版,强烈建议使用 编译安装 方式,仓库安装的有些问题(CentOS 7.4 实测)。
这里只简单记录 CentOS 的安装细节,其它发行版的编译方式是相同的,只不过安装依赖的方式不一样。详见 README.md

ssr-libev

shadowsocks-libevshadowsocksr-libev 二选一,也可一并安装

github 项目地址:shadowsocksr-backup/shadowsocksr-libev,安装细节如下:

tun2socks

一种方法是从 https://github.com/yinghuocho/gotun2socks 手动编译(不建议)
另一种方法是从 https://zfl9.github.io/index.html 中下载对应的二进制程序(建议)

一键脚本

脚本简介

ss-tproxy 脚本运行于 Linux 系统,用于实现类似 Windows SS/SSR 客户端的代理功能。目前实现的模式有 global(全局代理模式)、gfwlist(黑名单模式)、chnroute(白名单模式,绕过大陆地址段),考虑到部分用户没有支持 UDP-Relay 的 SS/SSR 节点,所以代理模式又分为 tcp&udp 和 tcponly 两类。Linux 系统下实现透明代理有两种思路:一是利用 iptables 进行重定向(DNAT),二是利用 tun 虚拟网卡进行代理(路由);因此代理模式又分为 tproxy、tun2socks 两大类,所以一共存在 12 种代理模式(下文中的“本机”指运行 ss-tproxy 的主机):

  • tproxy_global:代理 TCP/UDP(本机 UDP 除外),iptables/global 模式
  • tproxy_global_tcp:仅代理 TCP(DNS 使用 TCP 方式查询),iptables/global 模式
  • tproxy_gfwlist:代理 TCP/UDP(本机 UDP 除外),iptables/gfwlist 模式
  • tproxy_gfwlist_tcp:仅代理 TCP(DNS 使用 TCP 方式查询),iptables/gfwlist 模式
  • tproxy_chnroute:代理 TCP/UDP(本机 UDP 除外),iptables/chnroute 模式
  • tproxy_chnroute_tcp:仅代理 TCP(DNS 使用 TCP 方式查询),iptables/chnroute 模式
  • tun2socks_global:代理 TCP/UDP(包括本机 UDP),route/global 模式
  • tun2socks_global_tcp:仅代理 TCP(DNS 使用 TCP 方式查询),route/global 模式
  • tun2socks_gfwlist:代理 TCP/UDP(包括本机 UDP),route/gfwlist 模式
  • tun2socks_gfwlist_tcp:仅代理 TCP(DNS 使用 TCP 方式查询),route/gfwlist 模式
  • tun2socks_chnroute:代理 TCP/UDP(包括本机 UDP),route/chnroute 模式
  • tun2socks_chnroute_tcp:仅代理 TCP(DNS 使用 TCP 方式查询),route/chnroute 模式

脚本依赖

  • ss-tproxy 脚本相关依赖的安装参考
  • tproxy_global: ss/ssr-libev, haveged, xt_TPROXY, iproute2, dnsmasq
  • tproxy_gfwlist: ss/ssr-libev, haveged, xt_TPROXY, iproute2, ipset, perl, dnsmasq
  • tproxy_chnroute: ss/ssr-libev, haveged, xt_TPROXY, iproute2, ipset, chinadns, dnsmasq
  • tproxy_global_tcp: ss/ssr-libev, haveged, dnsforwarder
  • tproxy_gfwlist_tcp: ss/ssr-libev, haveged, ipset, perl, dnsmasq, dnsforwarder
  • tproxy_chnroute_tcp: ss/ssr-libev, haveged, ipset, chinadns, dnsforwarder
  • tun2socks_global: ss/ssr(版本不限), haveged, tun2socks, iproute2, dnsmasq
  • tun2socks_gfwlist: ss/ssr(版本不限), haveged, tun2socks, iproute2, ipset, perl, dnsmasq
  • tun2socks_chnroute: ss/ssr(版本不限), haveged, tun2socks, iproute2, ipset, chinadns, dnsmasq
  • tun2socks_global_tcp: ss/ssr(版本不限), haveged, tun2socks, iproute2, dnsforwarder
  • tun2socks_gfwlist_tcp: ss/ssr(版本不限), haveged, tun2socks, iproute2, ipset, perl, dnsmasq, dnsforwarder
  • tun2socks_chnroute_tcp: ss/ssr(版本不限), haveged, tun2socks, iproute2, ipset, chinadns, dnsforwarder
  • haveged 依赖项是可选的,主要用于防止系统的熵过低,从而导致 ss-redir、ss-tunnel、ss-local 启动失败等问题
  • *gfwlist* 模式更新列表时依赖 curl、base64;*chnroute* 模式更新列表时依赖 curl;建议都安装(反正迟早要装)
  • *gfwlist* 模式中的 perl 其实可以使用 sed 替代,但由于更新 gfwlist 列表依赖 perl5 v5.10.0+,所以直接使用了 perl

端口占用

  • 请确保相关端口未被其它进程占用,如果有请自行解决
  • tproxy_global: ss-redir=60080, ss-tunnel=60053, dnsmasq=53
  • tproxy_gfwlist: ss-redir=60080, ss-tunnel=60053, dnsmasq=53
  • tproxy_chnroute: ss-redir=60080, ss-tunnel=60053, chinadns=65353, dnsmasq=53
  • tproxy_global_tcp: ss-redir=60080, dnsforwarder=53
  • tproxy_gfwlist_tcp: ss-redir=60080, dnsforwarder=60053, dnsmasq=53
  • tproxy_chnroute_tcp: ss-redir=60080, dnsforwarder=60053, chinadns=65353, dnsforwarder=53
  • tun2socks_global: dnsmasq=53
  • tun2socks_gfwlist: dnsmasq=53
  • tun2socks_chnroute: chinadns=60053, dnsmasq=53
  • tun2socks_global_tcp: dnsforwarder=53
  • tun2socks_gfwlist_tcp: dnsforwarder=60053, dnsmasq=53
  • tun2socks_chnroute_tcp: dnsforwarder=60053, chinadns=65353, dnsforwarder=53

脚本用法

获取

  • git clone https://github.com/zfl9/ss-tproxy.git --single-branch

安装

  • cd ss-tproxy
  • cp -af ss-tproxy /usr/local/bin
  • chmod 0755 /usr/local/bin/ss-tproxy
  • chown root:root /usr/local/bin/ss-tproxy
  • mkdir -m 0755 -p /etc/tproxy
  • cp -af ss-tproxy.conf gfwlist.txt chnroute.* /etc/tproxy
  • chmod 0644 /etc/tproxy/* && chown -R root:root /etc/tproxy

配置

  • 脚本的配置文件为 /etc/tproxy/ss-tproxy.conf,修改后重启脚本才能生效
  • 默认模式为 tproxy_chnroute,这也是 v1 版本中的模式,根据自己的需要更改
  • 如果使用 tproxy* 模式,则修改 ss/ssr 配置 段中的相关 SS/SSR 服务器信息
  • 如果使用 tun2socks* 模式,则修改 socks5 配置 段中的相关 socks5 代理信息
  • dns_remote 用于指定代理状态下的 DNS,默认为 8.8.8.8:53,根据自己的需要修改
  • dns_direct 用于指定直连状态下的 DNS,默认为 114、119 DNS,根据自己的需要修改
  • iptables_intranet 用于指定要代理的内网网段,默认为 192.168.0.0/16,根据需要修改

自启(Systemd)

  • cp -af ss-tproxy.service /etc/systemd/system
  • systemctl daemon-reload
  • systemctl enable ss-tproxy.service

自启(SysVinit)

  • touch /etc/rc.d/rc.local
  • chmod +x /etc/rc.d/rc.local
  • echo '/usr/local/bin/ss-tproxy start' >>/etc/rc.d/rc.local

配置 ss-tproxy 开机自启后容易出现一个问题,那就是必须再次运行 ss-tproxy restart 后才能正常代理(这之前查看运行状态可能看不出任何问题,因为都是 running 状态),这是因为 ss-tproxy 启动过早了,且 server_addr/socks5_remote 为 hostname 形式,且没有将 server_addr/socks5_remote 中的 hostname 加入 /etc/hosts 文件而导致的。因为 ss-tproxy 启动时,网络还没准备好,此时根本无法解析这个 hostname。要避免这个问题,可以采取一个非常简单的方法,那就是将 hostname 加入到 /etc/hosts 中,如 hostname 为 node.proxy.net,对应的 IP 为 11.22.33.44,则只需执行 echo "11.22.33.44 node.proxy.net" >>/etc/hosts。不过得注意个问题,那就是假如这个 IP 变了,别忘了修改 /etc/hosts 文件哦。命令行获取某个域名对应的 IP 地址的方法:dig +short HOSTNAME。如果你使用的是 ArchLinux 发行版,也可以利用 netctl 的 hook 钩子脚本来启动 ss-tproxy(比如拨号成功后启动 ss-tproxy),具体配置可参考 Arch 官方文档

用法

  • ss-tproxy help:查看帮助
  • ss-tproxy start:启动代理
  • ss-tproxy stop:关闭代理
  • ss-tproxy restart:重启代理
  • ss-tproxy status:代理状态
  • ss-tproxy check-depend:检查依赖
  • ss-tproxy flush-cache:清空 DNS 缓存
  • ss-tproxy update-gfwlist:更新 gfwlist
  • ss-tproxy update-chnroute:更新 chnroute

日志

脚本默认关闭了日志输出,如果需要,请修改 ss-tproxy.conf,打开相应的 log/verbose 选项

  • ss-redir:/var/log/ss-redir.log
  • ss-tunnel:/var/log/ss-tunnel.log
  • tun2socks:/var/log/tun2socks.log
  • dnsmasq:/var/log/dnsmasq.log
  • chinadns:/var/log/chinadns.log
  • dnsforwarder:/var/log/dnsforwarder.log

代理测试

简单测试
所谓的简单测试就是看能不能上网(这里使用 curl 进行测试,你可以用浏览器直观的测试),如果 ss-tproxy 运行在网关上,那么建议在网关及内网中都测试一遍,如果只是在普通主机上使用,只要在这台主机上进行测试就行了。如果测试成功,足以说明 DNS 解析和 TCP 代理正常工作,如果测试失败,请参考后面的 调试方法,进行调试。

调试方法
开始调试前,请先打开 ss-tproxy.conf 中相应的 log/verbose 选项。这里只说一下简要步骤,不会详细介绍每个细节,因为模式太多了,说不完,也懒得打字。重在理解,如果不理解,说再多也没用。我建议你开启多个终端,每个终端都 tail -f 跟踪一个日志文件(为了好分辨每次调试,可以人为的敲几个空行),日志文件的路径在上一节中列出来了,当然还有 tun2socks 模式中的 socks5 代理的日志,日志文件在哪问你自己。

调试之前,先弄清楚每个模式中的流量走向,以及每个组件的作用:

无论什么模式,首先要关注的都是 DNS,因为 iptables、iproute 这些东西只认 IP,只有获得了对应的 IP,才有下一步的可能。比如 curl https://www.baidu.com,curl 首先进行的是 DNS 解析,将 www.baidu.com 转换为 IP 地址,然后再向这个 IP 地址发起 TCP 连接。如果 curl、wget 或浏览器访问网页失败,记得先排除 DNS 问题。以 tproxy_chnroute 模式为例,开 3 个窗口,分别 tail -f dnsmasq、chinadns、ss-tunnel 的 log,然后使用 dig 进行测试,先测试 ss-tunnel/60053,再测试 chinadns/65353,最后测试 dnsmasq/53。

  • dig @127.0.0.1 -p60053 www.google.com:测试 ss-tunnel,并观察 ss-tunnel 的日志。正常情况下,dig 是能够解析的,且日志有成功的 INFO。如果没有,那就不用下一步了,先解决 ss-tunnel 的问题吧。
  • dig @127.0.0.1 -p65353 www.baidu.com:测试 chinadns(国内),并观察 chinadns、ss-tunnel 的日志。正常情况下,dig 是能够解析的,从日志中可以看到:filter 来自 127.0.0.1:60053 的响应,pass 来自 114.114.114.114:53 的响应。
  • dig @127.0.0.1 -p65353 www.google.com:测试 chinadns(国外),并观察 chinadns、ss-tunnel 的日志。正常情况下,dig 是能够解析的,从日志中可以看到:filter 来自 114.114.114.114:53 的响应,pass 来自 127.0.0.1:60053 的响应。
  • dig @127.0.0.1 -p53 www.baidu.com:测试 dnsmasq(国内),并观察 dnsmasq、chinadns、ss-tunnel 的日志。正常情况下,除了第一次解析,其它的 DNS 查询时间都是 0ms(DNS 缓存的缘故)。
  • dig @127.0.0.1 -p53 www.google.com:测试 dnsmasq(国外),并观察 dnsmasq、chinadns、ss-tunnel 的日志。正常情况下,除了第一次解析,其它的 DNS 查询时间都是 0ms(DNS 缓存的缘故)。

确定没问题之后,说明 DNS 正常解析,那么问题可能出现在 TCP 代理中。使用 iptables -t nat -nvL --line-numbers 查看 nat 表的规则,可以的话,在 raw 表中使用 TRACE 跟踪 80/tcp、443/tcp 的数据包,然后使用 dmesg -w 查看数据包的走向。当然,如果真的是代理规则的问题,请在评论区反馈,或者直接邮件反馈,因为这是 ss-tproxy 脚本的问题。

关于 UDP 的测试
如果是 tproxy 模式,那么请在内网主机中测试 UDP,因为本机的 UDP 代理不了;如果是 tun2socks 模式,本机或内网都可以,无所谓。仍以 tproxy_chnroute 模式为例,在某台内网主机中,使用 dig @208.67.222.222 -p443 www.google.com,如果正常解析,则观察 ss-redir 的日志(verbose 模式),应该能看到对应的 udp relay 信息。

常见问题

ss-tproxy 启动报错 iptables: No chain/target/match by that name.
先使用 modprobe xt_TPROXY 尝试加载 TPROXY 模块,如果提示未找到,则说明内核缺少 xt_TPROXY 模块。这个问题多出现在精简版、定制版 Linux 上,比如部分无线路由的固件。如果遇到这个问题,请联系固件厂商,或者自行编译 TPROXY 模块。

ss-tproxy start 后部分组件未启动成功,显示 stopped
如果是 dns 相关的组件,请检查 /var/log 下面的日志,看看是不是地址被占用了(具体有哪些端口,可以看前面的端口列表),如果是其它问题,请先尝试自行解决,如果仍未成功,请评论区,或者邮件反馈。

关于网关的 DHCP 配置细节
基本配置不用变动,不过还是得注意一下 DNS 问题,分配给内网主机的 DNS 建议设为网关 IP(即运行 ss-tproxy 的主机)。假设整个内网的网段是 192.168.1.0/24,网关(即运行 ss-tproxy 的主机)的 IP 为 192.168.1.1,则将 DHCP 分配的 DNS 设为 192.168.1.1。不过这只是个建议,没有强制要求,你设为 114DNS、GoogleDNS、OpenDNS 也没问题。但千万别设置为 127.0.0.1 这样的地址,或者是内网中的其它主机,这种情况下 ss-tproxy 的 DNS 强制重定向就失效了,会导致内网主机的 DNS 解析失败,无法正常联网。

关于 ss-tproxy 的一些疑问
1、ss-tproxy 支持什么 linux 系统?
一般都支持,没有特定的限制,因为没有使用与特定发行版相关的命令、功能。

2、ss-tproxy 只能运行在网关上吗?
显然不是,你可以在一台普通的 linux 主机上运行(把它当作 SS/SSR 客户端用)。如果在网关上运行 ss-tproxy,建议选择 tproxy 模式(性能稍好一些);如果在普通主机上运行 ss-tproxy,建议选择 tun2socks 模式(可以代理本机的 UDP)。

3、ss-tproxy 可以运行在副路由上吗?
可以。假设你有两个路由器,一主一副,主路由通过 PPPOE 拨号上网,其它设备连接到主路由可以上外网(无科学上网),副路由的 WAN 口连接到主路由的 LAN 口,副路由的 WAN 网卡 IP 可以动态获取,也可以静态分配,此时,副路由自己也是能够上外网的。然后,在副路由上运行 ss-tproxy,此时,副路由已经能够科学上网了,然后,我们在副路由上配置一个 LAN 网段(不要与主路由的 LAN 网段一样),假设主路由的 LAN 网段是 192.168.1.0/24,副路由的 LAN 网段是 10.10.10.0/24。然后指定一个网关 IP 给副路由的 LAN 口,假设为 10.10.10.1,开启副路由上的 DHCP,分配的地址范围为 10.10.10.100-200,分配的网关地址为 10.10.10.1,分配的 DNS 服务器为 10.10.10.1。现在,修改 ss-tproxy.conf 的内网网段为 10.10.10.0/24,重启 ss-tproxy,然后连接到副路由的设备应该是能够科学上网的。你可能会问,为什么不直接在主路由上安装 ss-tproxy 呢?假设这里的副路由是一个树莓派,那么我不管在什么网络下(公司、酒店),只要将树莓派插上,然后我的设备(手机、笔记本)只需要连接树莓派就能无缝上网了,同时又不会影响内网中的其它用户,一举两得。

4、ss-tproxy 可以运行在内网主机上吗(代理网关)?
可以。先解释一下这里的“代理网关”(不知道叫什么好),由网友 @feiyu 启发。他的意思是,将 ss-tproxy 部署在一台普通的内网主机上(该内网主机的网关不变),然后将其他内网主机的网关和 DNS 指向这台部署了 ss-tproxy 的主机,进行代理。方案是可行的,我在 VMware 环境中测试通过。