Skip to content

Instantly share code, notes, and snippets.

@laris
Created October 26, 2018 03:06
Show Gist options
  • Save laris/b702b9b8c278ed1b513a791ad34fcdc3 to your computer and use it in GitHub Desktop.
Save laris/b702b9b8c278ed1b513a791ad34fcdc3 to your computer and use it in GitHub Desktop.

ss/ssr/v2ray/socks5 透明代理

先说 ss/ssr 透明代理吧,ss-redir 是 ss-libevssr-libev 中的一个工具,配合 iptables 可以在 Linux 上实现 ss、ssr 透明代理,ss-redir 的 TCP 透明代理是通过 REDIRECT 方式实现的,而 UDP 透明代理是通过 TPROXY 方式实现的。强调一点,利用 ss-redir 实现透明代理必须使用 ss-libev 或 ssr-libev,python、go 等版本没有 ss-redir、ss-tunnel 程序。
当然,ss、ssr 透明代理并不是只能用 ss-redir 来实现,使用 ss-local + redsocks2/tun2socks 同样可以实现 socks5(ss-local 是 socks5 服务器)全局透明代理;ss-local + redsocks2 实际上是 ss-redir 的分体实现,TCP 使用 REDIRECT 方式,UDP 使用 TPROXY 方式;ss-local + tun2socks 则相当于 Android 版 SS/SSR 的 VPN 模式,因为它实际上是通过一张虚拟的 tun 网卡来进行代理的。
最后说一下 v2ray 的透明代理,其实原理和 ss/ssr-libev 一样,v2ray 可以看作是 ss-local、ss-redir、ss-tunnel、ss-server 四者的合体,因为同一个 v2ray 程序既可以作为 server 端,也可以作为 client 端。所以 v2ray 的透明代理也有两种实现方式,一是利用对应的 ss-redir/ss-tunnel + iptables,二是利用对应的 ss-local + redsocks2/tun2socks(redsocks2/tun2socks 可以与任意 socks5 代理组合,实现透明代理)。

组件区别

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-tproxy 脚本也可以了),如果你对它感兴趣,可以看看 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 中的 SS/SSR 客户端难用多了,安装好就只有一个 ss-local(libev 版还有 ss-redir、ss-tunnel,但我相信大部分人装得都是 python 版的),启动 ss-local 后并不会像 Windows/Android 那样自动配置系统代理,此时它仅仅是一个本地 socks5 代理服务器,默认监听 127.0.0.1:1080,如果需要利用该 socks5 代理上外网,必须在命令中指定对应的代理,如 curl -4sSkL -x socks5h://127.0.0.1:1080 [https://www.google.com](https://www.google.com/)

但我想大部分人要的代理效果都不是这种的,太原始了。那能不能配置所谓的“系统代理”呢,可以是可以,但是好像只支持 http 类型的代理,即在当前 shell 中设置 http_proxyhttps_proxy 环境变量,假设存在一个 http 代理(支持 CONNECT 请求方法),监听地址是 127.0.0.1:8118,可以这样做:export http_proxy=[http://127.0.0.1:8118](http://127.0.0.1:8118/); export https_proxy=$http_proxy。执行完后,git、curl、wget 等命令会自动从环境变量中读取 http 代理信息,然后通过 http 代理连接目的服务器。

那问题来了,ss-local 提供的是 socks5 代理,不能直接使用怎么办?也简单,Linux 中有很多将 socks5 包装为 http 代理的工具,比如 privoxy。只需要在 /etc/privoxy/config 里面添加一行 forward-socks5 / 127.0.0.1:1080 .,启动 privoxy,默认监听 127.0.0.1:8118 端口,注意别搞混了,8118 是 privoxy 提供的 http 代理地址,而 1080 是 ss-local 提供的 socks5 代理地址,发往 8118 端口的数据会被 privoxy 处理并转发给 ss-local。所以我们现在可以执行 export http_proxy=[http://127.0.0.1:8118](http://127.0.0.1:8118/); export https_proxy=$http_proxy 来配置当前终端的 http 代理,这样 git、curl、wget 这些就会自动走 ss-local 出去了。

当然我们还可以利用 privoxy 灵活的配置,实现 Windows/Android 中的 gfwlist 分流模式。gfwlist.txt 其实是对应的 Adblock Plus 规则的 base64 编码文件,显然不能直接照搬到 privoxy 上。这个问题其实已经有人解决了,利用 snachx/gfwlist2privoxy python 脚本就可轻松搞定。但其实我也重复的造了一个轮子:zfl9/gfwlist2privoxy,我并不是吃饱了没事干,是因为我当时运行不了他的脚本(也不知道什么原因),所以花了点时间用 shell 脚本实现了一个 gfwlist2privoxy(但其实我是用 perl 转换的,只不过用 shell 包装了一下)。脚本转换出来的是一个 gfwlist.action 文件,我们只需将该 gfwlist.action 文件放到 /etc/privoxy 目录,然后在 config 中添加一行 actionsfile gfwlist.action(当然之前 forward-socks5 那行要注释掉),重启 privoxy 就可以实现 gfwlist 分流了。

但仅仅依靠 http_proxyhttps_proxy 环境变量实现的终端代理效果不是很好,因为有些命令根本不理会你的 http_proxyhttps_proxy 变量,它们依旧走的直连。但又有大神想出了一个巧妙的方法,即 rofl0r/proxychains-ng,其原理是通过 LD_PRELOAD 特殊环境变量提前加载指定的动态库,来替换 glibc 中的同名库函数。这个 LD_PRELOAD 指向的其实就是 proxychains-ng 实现的 socket 包装库,这个包装库会读取 proxychains-ng 的配置文件(这里面配置代理信息),之后执行的所有命令调用的 socket 函数其实都是 proxychains-ng 动态库中的同名函数,于是就实现了全局代理,而命令对此一无所知。将 proxychains-ng 与 privoxy 结合起来基本上可以完美实现 ss/ssr 的本地全局 gfwlist 代理(小技巧,在 shell 中执行 exec proxychains -q bash 可以实现当前终端的全局代理,如果需要每个终端都自动全局代理,可以在 bashrc 文件中加入这行)。

但是很多人对此依然无法满足,因为他们想实现 OpenWrt 那种路由级别的全局透明代理(并且还有 gfwlist、绕过大陆地址段这些分流模式可选择),这样只要设备连到 WiFi 就能直接无缝上网,完全感觉不到“墙”的存在。如果忽略分流模式(即全部流量都走代理出去),那么实现是很简单的(几条 iptables 就可以搞定,但是这太简单粗暴了,很多国内网站走代理会非常慢,体验很不好);但是如果要自己实现 gfwlist、绕过大陆地址段这些模式,恐怕很多人都会望而却步,因为确实复杂了一些。但这种透明代理的模式的确很诱人,毕竟只要设置一次就可以让所有内网设备上 Internet,所以我开始摸索如何在 Linux 软路由中(我用的是树莓派 3B,目前还凑合,当然也可以直接在 Windows 中利用 VMware/VirtualBox 建一个桥接模式的虚拟机,做所谓的 代理网关)实现类似 OpenWrt 的代理模式,而我摸索出来的成果就是 ss-tproxy 透明代理脚本。

ss-tproxy 脚本经过几次大改进,现在已经支持 ss/ssr/v2ray/socks5 4 种类型的透明代理了,具体的原理在开头也提了一些,这里并不打算讲述 ss-tproxy 实现各种代理模式、分流模式的原理,因为我快编不下去了(没办法写的太烂了)。脚本目前实现了 4 种分流模式:global(全局模式,不分流)、gfwlist(仅代理 gfwlist 域名)、chnonly(仅代理大陆域名,国外翻回国内)、chnroute(绕过大陆地址段,其余均走代理),18 个 mode:

mode='v2ray_global'            # v2ray  global   模式
mode='v2ray_gfwlist'           # v2ray  gfwlist  模式
mode='v2ray_chnroute'          # v2ray  chnroute 模式
mode='tlspxy_global'           # tlspxy global   模式
mode='tlspxy_gfwlist'          # tlspxy gfwlist  模式
mode='tlspxy_chnroute'         # tlspxy chnroute 模式
mode='tproxy_global'           # ss/ssr global   模式
mode='tproxy_gfwlist'          # ss/ssr gfwlist  模式
mode='tproxy_chnroute'         # ss/ssr chnroute 模式
mode='tproxy_global_tcp'       # ss/ssr global   模式 (tcponly)
mode='tproxy_gfwlist_tcp'      # ss/ssr gfwlist  模式 (tcponly)
mode='tproxy_chnroute_tcp'     # ss/ssr chnroute 模式 (tcponly)
mode='tun2socks_global'        # socks5 global   模式
mode='tun2socks_gfwlist'       # socks5 gfwlist  模式
mode='tun2socks_chnroute'      # socks5 chnroute 模式
mode='tun2socks_global_tcp'    # socks5 global   模式 (tcponly)
mode='tun2socks_gfwlist_tcp'   # socks5 gfwlist  模式 (tcponly)
mode='tun2socks_chnroute_tcp'  # socks5 chnroute 模式 (tcponly)

Bash

Copy

标识了 tcponly 的 mode 表示只代理 tcp 流量(dns 通过 tcp 方式解析),udp 不会被处理,主要用于不支持 udp relay 的 ss/ssr/socks5 代理。除了 tun2sockstlspxy mode 外,其它 mode 均不会代理 ss-tproxy 本机的 udp 流量(dns 会另外处理,见下面的说明)。另外,chnonly 模式并未单独分出来,因为它本质上与 gfwlist 模式相同,只不过域名列表不一样而已,所以如果要使用 chnonly 分流模式,请选择对应的 gfwlist mode(当然还需要几个步骤,后面会说明)。

为什么除了 tun2sockstlspxy mode 外(tlspxy 即 tls-proxy,是我自己写的一个代理工具),其他 mode 都不代理本机的 udp 流量呢?这里面其实是有历史原因的,怎么说呢?因为在我自己写 tls-proxy 之前,我一直以为本机的 udp 是无法通过 TPROXY 方式代理的,当初的理由很简单,因为当我尝试在 OUTPUT 链添加 TPROXY target 时,会提示这个错误:x_tables: ip_tables: TPROXY target: used from hooks OUTPUT, but only usable from PREROUTING(意思是 TPROXY 只能用于 PREROUTING 链),只能用于 PREROUTING 链的话,不就只能代理来自”内网”的流量了吗?当时也是因为这个原因,我才弄了个 tun2socks 模式,那为什么 tun2socks 模式就能代理本机 UDP?因为 tun2socks 会创建一张 tun 虚拟网卡,只要我们将要代理的流量通过 iproute2 策略路由,送入这张 tun 虚拟网卡就行,也就是和 VPN 差不多。

直到前段时间,我因为 v2ray 对 QUIC 的支持问题而不爽,决定自己写一个 C 语言版的 v2ray(当然我只实现了 v2ray 的 websocket + tls + web 方案,毕竟这是所谓的最安全方案),经过自己的不断努力,终于写了个成品出来,也就是上面提到的 tls-proxy,编写 tls-proxy 期间,我也对 TPROXY 透明代理的原理有了更多深刻的理解,才知道原来 TPROXY 是可以代理本机 TCP、UDP 流量的,虽然 iptables 不允许我们直接在 OUTPUT 链上设置 TPROXY target,但是我们可以在 OUTPUT 链上打上 fwmark,然后在经过 iproute2 策略路由时,将打了指定 fwmark 的数据路由到本机(从 loopback 网卡进去),正是因为这个从 loopback 网卡流入的步骤,允许我们在 PREROUTING 链(数据包流入某张网卡之后,首先被 PREROUTING 链处理,然后再进行路由)上设置相应的 TPROXY target,然后我们就可以代理本机的 TCP 和 UDP 了。

同理,其它支持 UDP Relay(TPROXY 方式)的 mode 都可以这么做。你现在又要问了,那为什么现在还是只有 tlspxytun2socks mode 才代理本机 UDP 呢?原因很简单,因为我最近真的没时间,等有时间折腾的时候,我就会更新 v3 版本的 ss-tproxy,将这些功能加上去,并且将无关紧要的 mode 去掉,比如 tun2socks,因为 tun2socks 模式已经没有任何优势了。

另外,v2ray 最新版也支持纯 TPROXY 方式的代理了(TCP 和 UDP 都使用 TPROXY,以前 TCP 是使用 REDIRECT),也就是和 tls-proxy 一样了,所以,如果你愿意,你也可以在 tlspxy_* mode 上使用这种配置的 v2ray。

安装依赖

  • v2ray_global: v2ray-core, xt_TPROXY, iproute2, dnsmasq
  • v2ray_gfwlist: v2ray-core, xt_TPROXY, iproute2, ipset, perl, dnsmasq
  • v2ray_chnroute: v2ray-core, xt_TPROXY, iproute2, ipset, chinadns, dnsmasq
  • tlspxy_global: tls-proxy, xt_TPROXY, iproute2, dnsmasq
  • tlspxy_gfwlist: tls-proxy, xt_TPROXY, iproute2, ipset, perl, dnsmasq
  • tlspxy_chnroute: tls-proxy, xt_TPROXY, iproute2, ipset, chinadns, dnsmasq
  • 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: socks5 代理, tun2socks, iproute2, dnsmasq
  • tun2socks_gfwlist: socks5 代理, tun2socks, iproute2, ipset, perl, dnsmasq
  • tun2socks_chnroute: socks5 代理, tun2socks, iproute2, ipset, chinadns, dnsmasq
  • tun2socks_global_tcp: socks5 代理, tun2socks, iproute2, dnsforwarder
  • tun2socks_gfwlist_tcp: socks5 代理, tun2socks, iproute2, ipset, perl, dnsmasq, dnsforwarder
  • tun2socks_chnroute_tcp: socks5 代理, 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

请检查 curl 是否支持 HTTPS 协议,使用 curl --version 可查看(Protocols)

# CentOS
yum -y install curl

# ArchLinux
pacman -S curl

Bash

Copy

perl5

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

# CentOS
yum -y install perl

# ArchLinux
pacman -S perl

Bash

Copy

ipset

# CentOS
yum -y install ipset

# ArchLinux
pacman -S ipset

Bash

Copy

TPROXY

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

# 查找 TPROXY 模块
find /lib/modules/$(uname -r) -type f -name '*.ko*' | grep 'xt_TPROXY'

# 正常情况下的输出
/lib/modules/4.16.8-1-ARCH/kernel/net/netfilter/xt_TPROXY.ko.xz

Bash

Copy

iproute2

# CentOS
yum -y install iproute

# ArchLinux
pacman -S iproute2

Bash

Copy

haveged

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

This system doesn't provide enough entropy to quickly generate high-quality random numbers
Installing the rng-utils/rng-tools or haveged packages may help.
On virtualized Linux environments, also consider using virtio-rng.
The service will not start until enough entropy has been collected.

该系统不能提供足够的熵来快速生成高质量的随机数,
安装 rng-utils/rng-tools 或 haveged 软件包可能会有所帮助。
在虚拟化的 Linux 环境中,请考虑使用 virtio-rng。
shadowsocks 服务只有在收集到足够的熵后才会启动。

None

Copy

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

# ArchLinux
pacman -S haveged
systemctl enable haveged
systemctl start haveged

# CentOS
yum -y install haveged
## CentOS 6.x
chkconfig haveged on
service haveged start
## CentOS 7.x
systemctl enable haveged
systemctl start haveged

Bash

Copy

dnsmasq

# CentOS
yum -y install dnsmasq

# ArchLinux
pacman -S dnsmasq

Bash

Copy

chinadns

# 获取
wget https://github.com/shadowsocks/ChinaDNS/releases/download/1.3.2/chinadns-1.3.2.tar.gz

# 解压
tar xf chinadns-1.3.2.tar.gz

# 安装
cd chinadns-1.3.2/
./configure
make && make install

Bash

Copy

dnsforwarder

## 获取 dnsforwarder
git clone https://github.com/holmium/dnsforwarder.git

## 编译 dnsforwarder
cd dnsforwarder/
autoreconf -f -i
./configure --enable-downloader=no
make && make install

Bash

Copy

如果 make 报错,提示 undefined reference to rpl_malloc,请编辑 config.h.in 文件,把里面的 #undef malloc#undef realloc 删掉,然后再编译,即:./configure --enable-downloader=nomake && make install

v2ray

安装很简单,直接使用 v2ray 官方提供的 shell 脚本即可,默认配置开机自启

# 官方安装脚本
bash <(curl -4sSkL https://install.direct/go.sh)

# 安装的文件有
/etc/v2ray/config.json      配置文件
/usr/bin/v2ray/v2ray        V2Ray 程序
/usr/bin/v2ray/v2ctl        V2Ray 工具
/usr/bin/v2ray/geoip.dat    IP 数据文件
/usr/bin/v2ray/geosite.dat  域名数据文件

# 是否安装成功
/usr/bin/v2ray/v2ray -help  # 查看帮助

Bash

Copy

ss-libev

ArchLinux 建议使用 pacman -S shadowsocks-libev 安装,方便快捷,更新也及时。
CentOS/RHEL 或其它发行版,强烈建议 编译安装,仓库安装的可能会有问题(版本太老或者根本用不了)。
下面的代码完全摘自 ss-libev 官方 README.md,随着时间的推移可能有变化,最好照着最新 README.md 来做。

# In general, you need the following build dependencies:
# - autotools (autoconf, automake, libtool)
# - gettext
# - pkg-config
# - libmbedtls
# - libsodium
# - libpcre3 (old pcre library)
# - libev
# - libc-ares
# - asciidoc (for documentation only)
# - xmlto (for documentation only)
#
# Notes: 
# Fedora 26 libsodium version >= 1.0.12, so you can install via dnf install libsodium instead build from source.
# If your system is too old to provide libmbedtls and libsodium (later than v1.0.8), you will need to either install those libraries manually or upgrade your system.
# If your system provides with those libraries, you should not install them from source. You should jump this section and install them from distribution repository instead.
# 
# For some of the distributions, you might install build dependencies like this:
#
# Installation of basic build dependencies
## Debian / Ubuntu
sudo apt-get install --no-install-recommends gettext build-essential autoconf libtool libpcre3-dev asciidoc xmlto libev-dev libc-ares-dev automake libmbedtls-dev libsodium-dev
## CentOS / Fedora / RHEL
sudo yum install gettext gcc autoconf libtool automake make asciidoc xmlto c-ares-devel libev-devel
## Arch
sudo pacman -S gettext gcc autoconf libtool automake make asciidoc xmlto c-ares libev

# Installation of Libsodium
export LIBSODIUM_VER=1.0.13
wget https://download.libsodium.org/libsodium/releases/old/libsodium-$LIBSODIUM_VER.tar.gz
tar xvf libsodium-$LIBSODIUM_VER.tar.gz
pushd libsodium-$LIBSODIUM_VER
./configure --prefix=/usr && make
sudo make install
popd
sudo ldconfig

# Installation of MbedTLS
export MBEDTLS_VER=2.6.0
wget https://tls.mbed.org/download/mbedtls-$MBEDTLS_VER-gpl.tgz
tar xvf mbedtls-$MBEDTLS_VER-gpl.tgz
pushd mbedtls-$MBEDTLS_VER
make SHARED=1 CFLAGS=-fPIC
sudo make DESTDIR=/usr install
popd
sudo ldconfig

# Start building
git clone https://github.com/shadowsocks/shadowsocks-libev.git
cd shadowsocks-libev
git submodule update --init --recursive
./autogen.sh && ./configure && make
sudo make install

Bash

Copy

ssr-libev

shadowsocksr-backup/shadowsocksr-libev(貌似已停止更新,但目前使用没问题,只是一些新特性不支持,如有更好的源请告诉我~)
https://github.com/shadowsocksrr/shadowsocksr-libev/tree/Akkariiin/master,另一个 ssr-libev 源,Akkariiin-* 分支目前仍在更新
本文仍以 shadowsocksr-backup/shadowsocks-libev 为例,毕竟另一个源我没试过,但是这个源我自己用了大半年,没有任何问题,很稳定

# Installation of basic build dependencies
## Debian / Ubuntu
sudo apt-get install --no-install-recommends gettext build-essential autoconf libtool libpcre3-dev asciidoc xmlto libev-dev libc-ares-dev automake libmbedtls-dev libsodium-dev
## CentOS / Fedora / RHEL
sudo yum install gettext gcc autoconf libtool automake make asciidoc xmlto c-ares-devel libev-devel
## Arch
sudo pacman -S gettext gcc autoconf libtool automake make asciidoc xmlto c-ares libev

# Installation of Libsodium
export LIBSODIUM_VER=1.0.13
wget https://download.libsodium.org/libsodium/releases/old/libsodium-$LIBSODIUM_VER.tar.gz
tar xvf libsodium-$LIBSODIUM_VER.tar.gz
pushd libsodium-$LIBSODIUM_VER
./configure --prefix=/usr && make
sudo make install
popd
sudo ldconfig

# Installation of MbedTLS
export MBEDTLS_VER=2.6.0
wget https://tls.mbed.org/download/mbedtls-$MBEDTLS_VER-gpl.tgz
tar xvf mbedtls-$MBEDTLS_VER-gpl.tgz
pushd mbedtls-$MBEDTLS_VER
make SHARED=1 CFLAGS=-fPIC
sudo make DESTDIR=/usr install
popd
sudo ldconfig

# Start building
git clone https://github.com/shadowsocksr-backup/shadowsocksr-libev.git
cd shadowsocksr-libev
./configure --prefix=/usr/local/ssr-libev && make && make install
cd /usr/local/ssr-libev/bin
mv ss-redir ssr-redir
mv ss-local ssr-local
ln -sf ssr-local ssr-tunnel
mv ssr-* /usr/local/bin/
rm -fr /usr/local/ssr-libev

Bash

Copy

tls-proxy

tls-server 依赖项

tls-client 依赖项

编译方法,这里以 linux x86_64 为例,其它平台请酌情修改

# uthash
cd /tmp
git clone https://github.com/troydhanson/uthash

# base64
cd /tmp
git clone https://github.com/aklomp/base64
cd base64
make

# openssl
cd /tmp
wget https://www.openssl.org/source/openssl-1.1.0i.tar.gz
tar xvf openssl-1.1.0i.tar.gz
cd openssl-1.1.0i
./Configure linux-x86_64 --prefix=/tmp/openssl --openssldir=/tmp/openssl no-shared # for linux x86_64
make -j`nproc` && make install -j`nproc`

# libevent
cd /tmp
git clone https://github.com/libevent/libevent libevent-sources
cd libevent-sources
./autogen.sh
./configure --prefix=/tmp/libevent --enable-shared=no --enable-static=yes --disable-samples --disable-debug-mode --disable-malloc-replacement CPPFLAGS='-I/tmp/openssl/include' LDFLAGS='-L/tmp/openssl/lib' LIBS='-lssl -lcrypto -ldl'
make && make install

# tls-proxy
cd /tmp
git clone https://github.com/zfl9/tls-proxy
cd tls-proxy
gcc -I/tmp/uthash/include -I/tmp/base64/include -I/tmp/libevent/include -std=c11 -Wall -Wextra -Wno-format-overflow -O3 -s -pthread -o tls-server tls-server.c /tmp/base64/lib/libbase64.o /tmp/libevent/lib/libevent.a
gcc -I/tmp/uthash/include -I/tmp/base64/include -I/tmp/libevent/include -I/tmp/openssl/include -std=c11 -Wall -Wextra -Wno-format-overflow -O3 -s -pthread -o tls-client tls-client.c /tmp/base64/lib/libbase64.o /tmp/libevent/lib/libevent.a /tmp/libevent/lib/libevent_openssl.a /tmp/openssl/lib/libssl.a /tmp/openssl/lib/libcrypto.a -ldl
cp -af tls-client tls-server /usr/local/bin

# delete files
cd /
rm -fr /tmp/uthash
rm -fr /tmp/base64
rm -fr /tmp/openssl*
rm -fr /tmp/libevent*
rm -fr /tmp/tls-proxy

Bash

Copy

tun2socks

请选择与 CPU 相对应的二进制程序,下载后请去掉后缀名,放到 /usr/local/bin,添加可执行权限。

代理脚本

脚本简介

ss-tproxy 脚本运行于 Linux 系统(网关、软路由、虚拟机、普通 PC),用于实现 SS/SSR/V2Ray/Socks5 全局透明代理功能。普遍用法是将 ss-tproxy 部署在 Linux 软路由(或位于桥接模式下的 Linux 虚拟机),透明代理内网主机的 TCP、UDP 数据流(具体玩法可参考 ss-tproxy 常见问题)。脚本目前实现了 4 种分流模式:global(全局模式,不分流)、gfwlist(仅代理 gfwlist 域名)、chnonly(仅代理大陆域名,国外翻回国内)、chnroute(绕过大陆地址段,其余均走代理)。脚本目前定义了 18 种代理模式,如下(下文中的“本机”指运行 ss-tproxy 的主机):

mode='v2ray_global'            # v2ray  global   模式
mode='v2ray_gfwlist'           # v2ray  gfwlist  模式
mode='v2ray_chnroute'          # v2ray  chnroute 模式
mode='tlspxy_global'           # tlspxy global   模式
mode='tlspxy_gfwlist'          # tlspxy gfwlist  模式
mode='tlspxy_chnroute'         # tlspxy chnroute 模式
mode='tproxy_global'           # ss/ssr global   模式
mode='tproxy_gfwlist'          # ss/ssr gfwlist  模式
mode='tproxy_chnroute'         # ss/ssr chnroute 模式
mode='tproxy_global_tcp'       # ss/ssr global   模式 (tcponly)
mode='tproxy_gfwlist_tcp'      # ss/ssr gfwlist  模式 (tcponly)
mode='tproxy_chnroute_tcp'     # ss/ssr chnroute 模式 (tcponly)
mode='tun2socks_global'        # socks5 global   模式
mode='tun2socks_gfwlist'       # socks5 gfwlist  模式
mode='tun2socks_chnroute'      # socks5 chnroute 模式
mode='tun2socks_global_tcp'    # socks5 global   模式 (tcponly)
mode='tun2socks_gfwlist_tcp'   # socks5 gfwlist  模式 (tcponly)
mode='tun2socks_chnroute_tcp'  # socks5 chnroute 模式 (tcponly)

Bash

Copy

标识了 tcponly 的 mode 表示只代理 tcp 流量(dns 通过 tcp 方式解析),udp 不会被处理,主要用于不支持 udp relay 的 ss/ssr/socks5 代理。除了 tun2sockstlspxy mode 外,其它 mode 均不会代理 ss-tproxy 本机的 udp 流量(dns 会另外处理,见下面的说明)。另外,chnonly 模式并未单独分出来,因为它本质上与 gfwlist 模式相同,只不过域名列表不一样而已,所以如果要使用 chnonly 分流模式,请选择对应的 gfwlist mode(当然还需要几个步骤,后面会说明)。

为什么除了 tun2sockstlspxy mode 外(tlspxy 即 tls-proxy,是我自己写的一个代理工具),其他 mode 都不代理本机的 udp 流量呢?这里面其实是有历史原因的,怎么说呢?因为在我自己写 tls-proxy 之前,我一直以为本机的 udp 是无法通过 TPROXY 方式代理的,当初的理由很简单,因为当我尝试在 OUTPUT 链添加 TPROXY target 时,会提示这个错误:x_tables: ip_tables: TPROXY target: used from hooks OUTPUT, but only usable from PREROUTING(意思是 TPROXY 只能用于 PREROUTING 链),只能用于 PREROUTING 链的话,不就只能代理来自”内网”的流量了吗?当时也是因为这个原因,我才弄了个 tun2socks 模式,那为什么 tun2socks 模式就能代理本机 UDP?因为 tun2socks 会创建一张 tun 虚拟网卡,只要我们将要代理的流量通过 iproute2 策略路由,送入这张 tun 虚拟网卡就行,也就是和 VPN 差不多。

直到前段时间,我因为 v2ray 对 QUIC 的支持问题而不爽,决定自己写一个 C 语言版的 v2ray(当然我只实现了 v2ray 的 websocket + tls + web 方案,毕竟这是所谓的最安全方案),经过自己的不断努力,终于写了个成品出来,也就是上面提到的 tls-proxy,编写 tls-proxy 期间,我也对 TPROXY 透明代理的原理有了更多深刻的理解,才知道原来 TPROXY 是可以代理本机 TCP、UDP 流量的,虽然 iptables 不允许我们直接在 OUTPUT 链上设置 TPROXY target,但是我们可以在 OUTPUT 链上打上 fwmark,然后在经过 iproute2 策略路由时,将打了指定 fwmark 的数据路由到本机(从 loopback 网卡进去),正是因为这个从 loopback 网卡流入的步骤,允许我们在 PREROUTING 链(数据包流入某张网卡之后,首先被 PREROUTING 链处理,然后再进行路由)上设置相应的 TPROXY target,然后我们就可以代理本机的 TCP 和 UDP 了。

同理,其它支持 UDP Relay(TPROXY 方式)的 mode 都可以这么做。你现在又要问了,那为什么现在还是只有 tlspxytun2socks mode 才代理本机 UDP 呢?原因很简单,因为我最近真的没时间,等有时间折腾的时候,我就会更新 v3 版本的 ss-tproxy,将这些功能加上去,并且将无关紧要的 mode 去掉,比如 tun2socks,因为 tun2socks 模式已经没有任何优势了。

另外,v2ray 最新版也支持纯 TPROXY 方式的代理了(TCP 和 UDP 都使用 TPROXY,以前 TCP 是使用 REDIRECT),也就是和 tls-proxy 一样了,所以,如果你愿意,你也可以在 tlspxy_* mode 上使用这种配置的 v2ray。

脚本依赖

  • ss-tproxy 脚本相关依赖的安装参考
  • v2ray_global: v2ray-core, xt_TPROXY, iproute2, dnsmasq
  • v2ray_gfwlist: v2ray-core, xt_TPROXY, iproute2, ipset, perl, dnsmasq
  • v2ray_chnroute: v2ray-core, xt_TPROXY, iproute2, ipset, chinadns, dnsmasq
  • tlspxy_global: tls-proxy, xt_TPROXY, iproute2, dnsmasq
  • tlspxy_gfwlist: tls-proxy, xt_TPROXY, iproute2, ipset, perl, dnsmasq
  • tlspxy_chnroute: tls-proxy, xt_TPROXY, iproute2, ipset, chinadns, dnsmasq
  • 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: socks5 代理, tun2socks, iproute2, dnsmasq
  • tun2socks_gfwlist: socks5 代理, tun2socks, iproute2, ipset, perl, dnsmasq
  • tun2socks_chnroute: socks5 代理, tun2socks, iproute2, ipset, chinadns, dnsmasq
  • tun2socks_global_tcp: socks5 代理, tun2socks, iproute2, dnsforwarder
  • tun2socks_gfwlist_tcp: socks5 代理, tun2socks, iproute2, ipset, perl, dnsmasq, dnsforwarder
  • tun2socks_chnroute_tcp: socks5 代理, 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
  • tun2socks 依赖 net-tools 包中的 ifconfig 命令,如果启动 tun2socks 失败,可能是没有安装 net-tools 的原因

端口占用

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

脚本用法

安装

  • git clone https://github.com/zfl9/ss-tproxy.git
  • 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.* chnroute.* /etc/tproxy
  • chmod 0644 /etc/tproxy/* && chown -R root:root /etc/tproxy

卸载

  • ss-tproxy stop
  • rm -fr /etc/tproxy /usr/local/bin/ss-tproxy

简介

  • ss-tproxy:脚本文件
  • ss-tproxy.conf:配置文件
  • ss-tproxy.service:服务文件
  • gfwlist.txt:gfwlist 域名文件,不可配置
  • gfwlist.ext:gfwlsit 黑名单文件,可配置
  • chnroute.set:chnroute for ipset,不可配置
  • chnroute.txt:chnroute for chinadns,不可配置

配置

  • 脚本的配置文件为 /etc/tproxy/ss-tproxy.conf,修改后重启脚本才能生效
  • 默认模式为 tproxy_chnroute,这也是 v1 版本中的模式,根据你的需要更改
  • 如果使用 v2ray* 模式,则修改 v2ray 配置 段中的相关 V2Ray 客户端的信息
  • 如果使用 tlspxy* 模式,则修改 tlspxy 配置 段中的相关 tlspxy 客户端信息
  • 如果使用 tproxy* 模式,则修改 ss/ssr 配置 段中的相关 SS/SSR 服务器信息
  • 如果使用 tun2socks* 模式,则修改 socks5 配置 段中的相关 socks5 代理信息
  • dns_remote 为远程 DNS 服务器(走代理),默认为 Google DNS,根据需要修改
  • dns_direct 为直连 DNS 服务器(走直连),默认为 114 公共DNS,根据需要修改
  • iptables_intranet 指定要代理的内网网段,默认为 192.168.0.0/16,根据需要修改
  • 如需配置 gfwlist 黑名单,请编辑 /etc/tproxy/gfwlist.ext,修改后需重启脚本生效

如果你需要使用 chnonly 模式(国外翻进国内),请选择 *gfwlist* 代理模式,比如 tproxy_gfwlist。chnonly 模式下,你必须修改 ss-tproxy.conf 中的 dns_remote 为国内的 DNS,如 dns_remote='114.114.114.114:53',并将 dns_direct 改为本地 DNS(国外的),如 dns_direct='8.8.8.8';因为 chnonly 模式与 gfwlist 模式共享 gfwlist.txt、gfwlist.ext 文件,所以在第一次使用时你必须先运行 ss-tproxy update-chnonly 将默认的 gfwlist.txt 内容替换为大陆域名(更新列表时,也应使用 ss-tproxy update-chnonly),并且注释掉 gfwlist.ext 中的 Telegram IP 段,因为这是为正常翻墙设置的,反之亦然。

如果使用 v2ray 模式,你必须配置 v2ray 客户端的 dokodemo-door 传入协议,并且需要两个 dokodemo-door 配置,一个用于实现类似 ss-redir 的透明代理,另一个用于实现类似 ss-tunnel 的端口转发(解析 DNS)。具体配置可参考(原有的 inbound 可以不动,通常该 inbound 是一个 socks5 传入协议,你只要保证不同的 inbound 的监听端口不冲突就行):

{
    "inbound": { ... },
    "inboundDetour": [
        // as ss-redir
        {
            "port": 60080,
            "protocol": "dokodemo-door",
            "settings": {
                "network": "tcp,udp",
                "followRedirect": true,
                "domainOverride": ["quic"]
            }
        },
        // as ss-tunnel
        {
            "port": 60053,
            "protocol": "dokodemo-door",
            "settings": {
                "address": "8.8.8.8",
                "port": 53,
                "network": "udp",
                "followRedirect": false
            }
        },
        ...
    ],
    "outbound": { ... },
    "outboundDetour": [ ... ],
    "routing": { ... }
}

JavaScript

Copy

自启(Systemd)

  • mv -f 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 主机使用 PPPoE 拨号上网(或其它耗时较长的方式),配置自启后,可能导致 ss-tproxy 在网络还未完全准备好的情况下先运行。此时如果 ss-tproxy.conf 中的 v2ray_servertlspxy_serversocks5_remoteserver_addr 为域名,则会导致 ss-tproxy 启动失败(甚至卡一段时间,因为它一直在尝试解析这些域名,直到超时为止)。要恢复正常的代理,只能在启动完成后手动进行 ss-tproxy restart。如果你想避免这种情况,请尽量将 ss-tproxy.conf 中的 v2ray_servertlspxy_serversocks5_remoteserver_addr 替换为 IP 形式,或者将这些域名添加到 ss-tproxy 主机的 /etc/hosts 文件,如果你和我一样使用的 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 flush-gfwlist:清空 ipset-gfwlist IP 列表
  • ss-tproxy update-gfwlist:更新 gfwlist(restart 生效)
  • ss-tproxy update-chnonly:更新 chnonly(restart 生效)
  • ss-tproxy update-chnroute:更新 chnroute(restart 生效)
  • ss-tproxy dump-ipts:显示 iptables 的 mangle、nat 表规则
  • ss-tproxy flush-ipts:清空 raw、mangle、nat、filter 表规则

ss-tproxy flush-gfwlist 的作用:因为 *gfwlist* 模式下 ss-tproxy restartss-tproxy stop; ss-tproxy start 并不会清空 ipset-gfwlist 列表,所以如果你进行了 ss-tproxy update-gfwlistss-tproxy update-chnonly 操作,或者修改了 /etc/tproxy/gfwlist.ext 文件,建议在 start 前执行一下此步骤,防止因为之前遗留的 ipset-gfwlist 列表导致奇怪的问题。注意,如果执行了 ss-tproxy flush-gfwlist 那么你可能需要还清空内网主机的 dns 缓存,并重启浏览器等被代理的应用。

如果需要切换代理 mode,必须先执行 ss-tproxy stop,然后再修改配置文件,改完之后再执行 ss-tproxy start。你可能会问为什么不能简单的改完文件直接 ss-tproxy restart?因为 ss-tproxy 只是一个 shell 脚本,它的一切状态都是靠 ss-tproxy.conf 文件保持的,如果你在修改 mode 之前没有先执行 stop 操作,那么 ss-tproxy 是不可能知道脚本之前用的是什么 mode,这样就会导致待会的 restart 操作出现问题(restart 在内部其实就是先调用 stop 函数,再调用 start 函数),执行 stop 函数时,脚本获取的 mode 其实是改了之后的 mode,所以执行清理操作时(主要是 kill 相关进程),不会清理先前那个 mode 的相关进程,然后在调用 start 函数时,新启动的进程可能会与之前的进程产生监听端口冲突,导致代理配置失败。当然,如果你没有修改 mode,那么是完全可以先改配置文件再执行 ss-tproxy restart 的。

日志

脚本默认关闭了详细日志,如果需要,请修改 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

已知问题

  • 由于 v2ray 的 dokodemo-door 传入协议无法正确处理 QUIC(Quick UDP Internet Connections)协议,导致访问支持 QUIC 协议的网站极其不稳定,具体表现为:网页加载不全、Google Play 图片无法显示、Google Play 软件无法下载及更新,虽然可以在 Chrome 浏览器中关闭 QUIC 协议的支持,但是 Google Play 没这个选项。搜索一番后,找到一个解决方法(讨论此问题的 Issue:https://github.com/v2ray/v2ray-core/issues/819):在 dokodemo-door 配置段中添加 "domainOverride": ["quic"]。但是实际测试并没有多好的效果,时好时坏,而且 Google Play 的加载速度比 SS/SSR 慢多了。

更新计划

  • 内网主机黑名单(全走代理)、白名单(全走直连)支持,方便部分游戏用户
  • 精简 ss-tproxy 脚本,特别是 check_dependstart_dnsstatus 这几个函数
  • 自写 v2ray websocket + tls 模式的 C 语言版(已实现,也即上面说的 tls-proxy

代理测试

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

# 访问网站,有网页源码输出说明 OK
curl -4sSkL https://www.baidu.com
curl -4sSkL https://www.google.com
curl -4sSkL https://www.google.co.jp
curl -4sSkL https://www.google.com.hk
curl -4sSkL https://www.youtube.com
curl -4sSkL https://www.facebook.com
curl -4sSkL https://www.wikipedia.org

# 查看当前 IP(其实说明不了什么)
# global 模式: 理论显示代理 IP
# gfwlist 模式: 理论显示本地 IP
# chnroute 模式: 理论显示本地 IP
curl -4sSkL http://ip.cn
curl -4sSkL http://myip.ipip.net
curl -4sSkL http://ip.chinaz.com/getip.aspx

Bash

Copy

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

调试之前,先弄清楚每个模式中的流量走向以及每个组件的作用(chnonly 模式与 gfwlist 模式原理相同,只不过 gfwlist.txt 列表不同):

v2ray_global
# DNS: dnsmasq/53@udp  -> v2ray/60053@udp -> vmess(8.8.8.8:53@udp via tcp)
# TCP: iptables-DNAT   -> v2ray/60080@tcp -> vmess(dest_addr:dest_port@tcp)
# UDP: iptables-TPROXY -> v2ray/60080@udp -> vmess(dest_addr:dest_port@udp via tcp)

v2ray_gfwlist
# DNS(=gfwlist): dnsmasq/53@udp  -> v2ray/60053@udp -> vmess(8.8.8.8:53@udp via tcp)
# DNS(!gfwlist): dnsmasq/53@udp  -> 原网络直连       -> 114.114.114.114:53@udp
# TCP(=gfwlist): iptables-DNAT   -> v2ray/60080@tcp -> vmess(dest_addr:dest_port@tcp)
# TCP(!gfwlist): iptables-PASS   -> 原网络直连       -> dest_addr:dest_port@tcp
# UDP(=gfwlist): iptables-TPROXY -> v2ray/60080@udp -> vmess(dest_addr:dest_port@udp via tcp)
# UDP(!gfwlist): iptables-PASS   -> 原网络直连       -> dest_addr:dest_port@udp

v2ray_chnroute
# DNS(=chnroute): dnsmasq/53@udp  -> chinadns/65353@udp -> 114.114.114.114:53@udp
# DNS(!chnroute): dnsmasq/53@udp  -> chinadns/65353@udp -> v2ray/60053@udp -> vmess(GoogleDNS)
# TCP(=chnroute): iptables-PASS   -> 原网络直连          -> dest_addr:dest_port@tcp
# TCP(!chnroute): iptables-DNAT   -> v2ray/60080@tcp    -> vmess(dest_addr:dest_port@tcp)
# UDP(=chnroute): iptables-PASS   -> 原网络直连          -> dest_addr:dest_port@udp
# UDP(!chnroute): iptables-TPROXY -> v2ray/60080@udp    -> vmess(dest_addr:dest_port@udp via tcp)

tlspxy_global
# DNS: iptables-DNAT   -> dnsmasq/60053@udp -> 8.8.8.8:53@udp (via udp tproxy)
# TCP: iptables-TPROXY -> tls-client@tcp    -> https -> nginx/443 -> tls-server
# UDP: iptables-TPROXY -> tls-client@udp    -> https -> nginx/443 -> tls-server

tlspxy_gfwlist
# DNS(=gfwlist): iptables-DNAT   -> dnsmasq/60053@udp -> 8.8.8.8:53@udp (via udp tproxy)
# DNS(!gfwlist): iptables-DNAT   -> dnsmasq/60053@udp -> 114.114.114.114:53@udp (走直连)
# TCP(=gfwlist): iptables-TPROXY -> tls-client@tcp    -> https -> nginx/443 -> tls-server
# TCP(!gfwlist): iptables-PASS   -> 原网络直连         -> dest_addr:dest_port@tcp
# UDP(=gfwlist): iptables-TPROXY -> tls-client@udp    -> https -> nginx/443 -> tls-server
# UDP(!gfwlist): iptables-PASS   -> 原网络直连         -> dest_addr:dest_port@udp

tlspxy_chnroute
# DNS(=chnroute): iptables-DNAT   -> dnsmasq/60053@udp -> chinadns/65353@udp -> 114.114.114.114:53@udp
# DNS(!chnroute): iptables-DNAT   -> dnsmasq/60053@udp -> chinadns/65353@udp -> 8.8.8.8:53@udp (via udp tproxy)
# TCP(=chnroute): iptables-PASS   -> 原网络直连         -> dest_addr:dest_port@tcp
# TCP(!chnroute): iptables-TPROXY -> tls-client@tcp    -> https -> nginx/443 -> tls-server
# UDP(=chnroute): iptables-PASS   -> 原网络直连         -> dest_addr:dest_port@udp
# UDP(!chnroute): iptables-TPROXY -> tls-client@udp    -> https -> nginx/443 -> tls-server

tproxy_global
# DNS: dnsmasq/53 -> ss-tunnel/60053
# TCP: ss-redir/60080
# UDP: ss-redir/60080
# dnsmasq: 监听 53/udp,DNS 缓存
# ss-tunnel: 监听 60053/udp,远程 DNS 解析
# ss-redir: 监听 60080/tcp+udp,代理 TCP/UDP

tproxy_global_tcp
# DNS: dnsforwarder/53 -> 8.8.8.8:53/tcp -> ss-redir/60080
# TCP: ss-redir/60080
# dnsforwarder: 监听 53/udp,DNS 缓存
# ss-redir: 监听 60080/tcp,代理 TCP

tproxy_gfwlist
# DNS(=gfwlist): dnsmasq/53 -> ss-tunnel/60053
# DNS(!gfwlist): dnsmasq/53 -> 114.114.114.114
# TCP(=gfwlist): ss-redir/60080
# TCP(!gfwlist): 原网络直连
# UDP(=gfwlist): ss-redir/60080
# UDP(!gfwlist): 原网络直连
# dnsmasq: 监听 53/udp,DNS 缓存、gfwlist 分流
# ss-tunnel: 监听 60053/udp,远程 DNS 解析
# ss-redir: 监听 60080/tcp+udp,代理 TCP/UDP

tproxy_gfwlist_tcp
# DNS(=gfwlist): dnsmasq/53 -> dnsforwarder/60053 -> 8.8.8.8:53/tcp -> ss-redir/60080
# DNS(!gfwlist): dnsmasq/53 -> 114.114.114.114
# TCP(=gfwlist): ss-redir/60080
# TCP(!gfwlist): 原网络直连
# dnsmasq: 监听 53/udp,DNS 缓存,gfwlist 分流
# dnsforwarder: 监听 60053/udp,将 udp 的 dns 查询转换为 tcp
# ss-redir: 监听 60080/tcp,代理 TCP

tproxy_chnroute
# DNS(=chnroute): dnsmasq/53 -> chinadns/65353 -> 114.114.114.114
# DNS(!chnroute): dnsmasq/53 -> chinadns/65353 -> ss-tunnel/60053
# TCP(=chnroute): 原网络直连
# TCP(!chnroute): ss-redir/60080
# UDP(=chnroute): 原网络直连
# UDP(!chnroute): ss-redir/60080
# dnsmasq: 监听 53/udp,DNS 缓存
# chinadns: 监听 65353/udp,chnroute 分流
# ss-tunnel: 监听 60053/udp,远程 DNS 解析
# ss-redir: 监听 60080/tcp+udp,代理 TCP/UDP

tproxy_chnroute_tcp
# DNS(=chnroute): dnsforwarder/53 -> chinadns/65353 -> 114.114.114.114
# DNS(!chnroute): dnsforwarder/53 -> chinadns/65353 -> dnsforwarder/60053 -> ss-redir/60080 
# TCP(=chnroute): 原网络直连
# TCP(!chnroute): ss-redir/60080
# dnsforwarder: 监听 53/udp,DNS 缓存
# chinadns: 监听 65353/udp,chnroute 分流
# dnsforwarder: 监听 60053/udp,将 udp 的 dns 查询转换为 tcp
# ss-redir: 监听 60080/tcp,代理 TCP

tun2socks_global
# DNS: dnsmasq/53 -> tun2socks -> socks5/udp
# TCP: tun2socks -> socks5/tcp
# UDP: tun2socks -> socks5/udp
# dnsmasq: 监听 53/udp,DNS 缓存
# tun2socks: tun 虚拟网卡,ip -> socks5
# socks5/tcp: 代理 TCP
# socks5/udp: 代理 UDP

tun2socks_global_tcp
# DNS: dnsforwarder/53 -> 8.8.8.8:53/tcp -> socks5/tcp
# TCP: tun2socks -> socks5/tcp
# dnsforwarder: 监听 53/udp,DNS 缓存,通过 socks5 解析 DNS
# tun2socks: tun 虚拟网卡,ip -> socks5
# socks5/tcp: 代理 TCP

tun2socks_gfwlist
# DNS(=gfwlist): dnsmasq/53 -> tun2socks -> socks5/udp
# DNS(!gfwlist): dnsmasq/53 -> 114.114.114.114
# TCP(=gfwlist): tun2socks -> socks5/tcp
# TCP(!gfwlist): 原网络直连
# UDP(=gfwlist): tun2socks -> socks5/udp
# UDP(!gfwlist): 原网络直连
# dnsmasq: 监听 53/udp,DNS 缓存,gfwlist 分流
# tun2socks: tun 虚拟网卡,ip -> socks5
# socks5/tcp: 代理 TCP
# socks5/udp: 代理 UDP

tun2socks_gfwlist_tcp
# DNS(=gfwlist): dnsmasq/53 -> dnsforwarder/60053 -> tun2socks -> socks5/tcp
# DNS(!gfwlist): dnsmasq/53 -> 114.114.114.114
# TCP(=gfwlist): tun2socks -> socks5/tcp
# TCP(!gfwlist): 原网络直连
# dnsmasq: 监听 53/udp,DNS 缓存,gfwlist 分流
# dnsforwarder: 监听 60053/udp,将 udp 的 dns 查询转换为 tcp
# tun2socks: tun 虚拟网卡,ip -> socks5
# socks5/tcp: 代理 TCP

tun2socks_chnroute
# DNS(=chnroute): dnsmasq/53 -> chinadns/60053 -> 114.114.114.114
# DNS(!chnroute): dnsmasq/53 -> chinadns/60053 -> tun2socks -> socks5/udp
# TCP(=chnroute): 原网络直连
# TCP(!chnroute): tun2socks -> socks5/tcp
# UDP(=chnroute): 原网络直连
# UDP(!chnroute): tun2socks -> socks5/udp
# dnsmasq: 监听 53/udp,DNS 缓存
# chinadns: 监听 60053,chnroute 分流
# tun2socks: tun 虚拟网卡,ip -> socks5
# socks5/tcp: 代理 TCP
# socks5/udp: 代理 UDP

tun2socks_chnroute_tcp
# DNS(=chnroute): dnsforwarder/53 -> chinadns/65353 -> 114.114.114.114
# DNS(!chnroute): dnsforwarder/53 -> chinadns/65353 -> dnsforwarder/60053 -> tun2socks -> socks5/tcp
# TCP(=chnroute): 原网络直连
# TCP(!chnroute): tun2socks -> socks5/tcp
# dnsforwarder: 监听 53/udp,DNS 缓存
# chinadns: 监听 65353,chnroute 分流
# dnsforwarder: 监听 60053,将 udp 的 dns 查询转换为 tcp
# tun2socks: tun 虚拟网卡,ip -> socks5
# socks5/tcp: 代理 TCP

Bash

Copy

无论什么模式,首先要关注的都是 DNS,因为 iptables、iproute 这些东西只认 IP,只有获得了对应的 IP,才有下一步的可能。比如 curl [https://www.baidu.com](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](http://www.google.com/):测试 ss-tunnel,并观察 ss-tunnel 的日志。正常情况下,dig 是能够解析的,且日志有成功的 INFO。如果没有,那就不用下一步了,先解决 ss-tunnel 的问题吧。
  • dig @127.0.0.1 -p65353 [www.baidu.com](http://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](http://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](http://www.baidu.com/):测试 dnsmasq(国内),并观察 dnsmasq、chinadns、ss-tunnel 的日志。正常情况下,除了第一次解析,其它的 DNS 查询时间都是 0ms(DNS 缓存的缘故)。
  • dig @127.0.0.1 -p53 [www.google.com](http://www.google.com/):测试 dnsmasq(国外),并观察 dnsmasq、chinadns、ss-tunnel 的日志。正常情况下,除了第一次解析,其它的 DNS 查询时间都是 0ms(DNS 缓存的缘故)。

进行 UDP 的测试
如果是 v2ray/tproxy 模式,那么请在内网主机中测试 UDP,因为本机的 UDP 没有被代理;如果是 tlspxy/tun2socks 模式,本机或内网都可以,无所谓。仍以 tproxy_chnroute 模式为例,在某台内网主机中,使用 dig @208.67.222.222 -p443 [www.google.com](http://www.google.com/),如果正常解析,则观察 ss-redir 的日志(verbose 模式),应该能看到对应 udp relay 成功的信息(有 udp relay 只能说明 UDP 代理规则没问题,能不能用是另一回事,因为有些 ISP 会针对 UDP 进行恶意丢包)。

反馈代理脚本的问题
如果某个环节总是无法成功,或者你认为应该是 ss-tproxy 脚本的问题,请附带对应的 log(详细日志),以及 bash -x /usr/local/bin/ss-tproxy start 的启动调试日志(敏感信息请自行替换),并且说明你的网络环境,如果还是不懂,我强烈建议您先看一遍 提问的智慧,然后提问也不迟。

常见问题

ss-tproxy 开机自启失败
将 ss-tproxy.conf 中的 v2ray_server、tlspxy_server、socks5_remote、server_addr 改为 IP,或者将相关域名加到 ss-tproxy 主机的 hosts 文件。

start 时部分组件 stopped
请先检查 /var/log 下面的日志,看看是不是地址被占用了(具体有哪些端口,可以看前面的端口列表),如果是,请自行解决。分析日志是个好习惯。

切换模式后不能正常代理了
从其它模式切换到 gfwlist 模式时可能出现这个问题,原因还是因为内网主机的 DNS 缓存。在访问被墙网站时,比如 www.google.com,客户机首先会进行 DNS 解析,由于存在 DNS 缓存,这个 DNS 解析请求并不会被 ss-tproxy 主机的 dnsmasq 处理(因为根本没从客户机发出来),所以对应 IP 不会添加到 ipset-gfwlist 列表中,导致客户机发给该 IP 的数据包不会被 ss-tproxy 处理,也就是走直连出去了,GFW 当然不会让它通过了,也就出现了连接被重置等问题。解决方法也很简单,对于 Windows,请先关闭浏览器,然后打开 cmd.exe,执行 ipconfig /flushdns 来清空 DNS 缓存,然后重新打开浏览器,应该正常了;对于 Android,可以先打开飞行模式,然后再关闭飞行模式,或许可以清空 DNS 缓存,实在清不了就重启吧。

有时会无法访问代理服务器
如果你在 ss-tproxy 中使用的是自己的 VPS 的 SS/SSR 服务,那么在除 ss-tproxy 本机外的其他主机上会可能会出现无法访问这台 VPS 的情况(比如 SSH 连不上,但是 ping 没问题),具体表现为连接超时。起初怀疑是 ss-tproxy 主机的 iptables 规则设置不正确,然而使用 TRACE 追踪后却什么都没发现,一切正常;在 VPS 上使用 tcpdump 抓包后,发现一个很奇怪的问题:VPS 在收到来自客户端的 SYN 请求后并没有进行 SYN+ACK 回复,客户端在尝试了几次后就会显示连接超时。于是断定这肯定是 VPS 的问题,谷歌后才知道,原来是因为两个内核参数设置不正确导致的,这两个内核参数是:net.ipv4.tcp_tw_reusenet.ipv4.tcp_tw_recycle,将它们都设为 0(也就是禁用)即可解决此问题。其实这两个内核参数默认都是为 0 的,也就是说,只要你没动过 VPS 的内核参数,那么基本不会出现这种诡异的问题。我是因为盲目的照搬照抄网上所谓的内核参数优化导致的,所以没事还是别优化什么内核参数了,除非你真的知道你在做什么!

关于网关的 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 解析失败,无法正常联网。另外,网关的 DHCP 服务器建议使用 dhcpd。

ss-tproxy 脚本的运行环境
1、ss-tproxy 支持什么 linux 系统?
一般都支持,没有限制,因为没有使用与特定发行版相关的命令、功能(我自己用的是 ArchLinux)。

2、ss-tproxy 只能运行在网关上吗?
显然不是,你可以在一台普通的 linux 主机上运行(此时你不用管 ss-tproxy.conf 中的内网网段配置)。

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 环境中测试通过。注意,这个“代理网关”可以是一台真机,也可以是一台虚拟机(桥接模式),比如在 Windows 系统中运行一个 VMware 或 VirtualBox 的 Linux 虚拟机,在这个虚拟机上跑 ss-tproxy 当然也是可以的。

切换模式、切换节点不方便?
这确实是个不人性化的地方,切换节点需要修改 ss-tproxy.conf,切换模式需要修改 ss-tproxy.conf,有没有更加简便一些的方式?抱歉,目前没有。不过因为 ss-tproxy.conf 是一个 shell 脚本,所以我们可以在这里面做些文章。如果你有很多个节点(付费帐号一般都是),可以这样做:

# 执行以下命令,在 /etc/tproxy/ss-tproxy.conf 中追加一行
echo "source /etc/tproxy/node.conf" >>/etc/tproxy/ss-tproxy.conf

# 在 /etc/tproxy 下创建节点文件,如 node-1.conf、node-2.conf
cd /etc/tproxy
touch node-1.conf node-2.conf

# 编辑 node-1.conf、node-2.conf,每个文件都代表一个不同的节点
# 以 node-1.conf 为例,tproxy_chnroute 模式,节点为 ss-node-1
mode='tproxy_chnroute'
server_addr='ss-node-1'
server_port='12345'
server_method='rc4-md5'
server_passwd='password'
server_use_ssr='true'
server_protocol='auth_aes128_md5'
server_protocol_param=''
server_obfs='tls1.2_ticket_auth'
server_obfs_param='www.example.com'

# node-N.conf 中可以包含 ss-tproxy.conf 中的所有变量,如 node2
mode='tproxy_gfwlist'
server_addr='ss-node-2'
server_port='54321'
server_method='rc4-md5'
server_passwd='password'
server_use_ssr='false'
dns_remote='208.67.222.222:443'

# 那么现在如何切换节点呢?只需三个步骤(不用改文件了)
# 其中 node-N.conf 表示要使用的节点,比如 node-1.conf
ss-tproxy stop
ln -sf /etc/tproxy/node-N.conf /etc/tproxy/node.conf
ss-tproxy start

Bash

Copy

[\# 科学上网](https://www.zfl9.com/tags/%E7%A7%91%E5%AD%A6%E4%B8%8A%E7%BD%91/) [\# ss-redir透明代理](https://www.zfl9.com/tags/ss-redir%E9%80%8F%E6%98%8E%E4%BB%A3%E7%90%86/)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment