给路由器搭建纸飞机(防和谐)透明代理环境

前言

为了防和谐我这里把某个图标为纸飞机的著名开源代理软件的名字简称作:“飞机”,你们懂就好。原因是我撤销了博客站的 HTTPS ,如果涉及敏感词可能会直接被重置链接。

如果你还在用客户端(无论是那个平台那种语言)监听端口的方式配合 gfwlist 等 PAC 规则上网的话,非常应该认识一下透明代理。它会大大提高配置和管理代理的效率。

透明代理的意思简而言之就是,让客户端设备忽略代理的相关配置和其存在,网络流量自动应用规则。例如你连接一个 A WIFI,发现可以直接上谷歌。而 B WIFI 不行,需要手动开启代理软件。那么 A 网络的代理相对客户端设备来说就是透明的。

而通常那个“网络总线”设备是路由器,所以代理自然就是配置在它上面,达到对客户端透明的效果。

飞机相关组建介绍(libev 版本)

  • ss-local: 飞机客户端
  • ss-server: 飞机服务端
  • ss-redir: 透明代理相关
  • ss-tunnel: 本地端口转发相关

这里主要使用了 ss-redir,飞机搭建和配置过程略过。详细讲路由器上(或者说路由器发行版 Linux 上)的相关软件和 ss-redir 配合的配置过程。

注意:我的环境是 DD-WRT 。

Entware-ng-3x

首先得确定你包管理环境是正确和达标的:

  1. iptables ~v1.4
  2. ipset ~v6.3
  3. chinadns

上面有标注版本的必须达到该版本或以上,否则不行。但这个不行并不是绝对不行,而是少了一定的优化能力,最终效果还是可以达到的。但是通常没必要将就,只要你的系统内核在 2.6 以上(3.x+)就可以用上面的软件,否则只能将就。

如果你是 DD-WRT、Merlin 这类(可以)使用 Entware 的系统,请确保你是 Entware-ng-3x ,因为这个是给高版本(3.x+)内核准备的。而 Entware-ng 是给 2.6 内核使用的。它们直接决定了上述软件版本是否达标。

如果你是 Openwrt、LEDE 之类的系统,相信也不必看这种文章了,因为现成的东西都有,不需要自己配置。

ChinaDNS

ChinaDNS 是一个可以分流出国内外 ip 的 DNS 软件,它的作用是至关重要的,它可以判定那些是域名解析结果是国外 ip 或者被 DNS 污染的 ip 。我们让它做的,就是将那些域名解析有问题的查询请求发给另一个无污染的 DNS 解析。所以它的核心作用就是解决 DNS 污染问题。
因为即使你透明代理是成功的,但是无法获取到被屏蔽网站的正确 ip 也是无用的。DNS 污染这种屏蔽方法虽然原始低端,但是现在看来还是挺能造成麻烦的(偷笑 。

安装 chinadns 包:

opkg install chinadns
          

你可能还要编辑一下 chinadns 的服务脚本,因为有些包默认的配置有点过时。假设你看到的 ARGS 内容不是这样:

root@DD-WRT:~# cat /opt/etc/init.d/S56chinadns 
          #!/bin/sh
          
          ENABLED=yes
          PROCS=chinadns
          ARGS="-c /opt/etc/chinadns_chnroute.txt -s 114.114.114.114,127.0.0.1:5300 -p 5353 -m"
          PREARGS=""
          DESC=$PROCS
          PATH=/opt/sbin:/opt/bin:/opt/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
          
          . /opt/etc/init.d/rc.func
          

请改成这样子,即 ARGS(参数):

-c /opt/etc/chinadns_chnroute.txt -s 114.114.114.114,127.0.0.1:5300 -p 5353 -m
          

-c 参数是中国大陆 ip 资源的记录文件,一般默认加上了不用管。-s 是使用的 DNS 列表,这里的 127.0.0.1:5300 下面会有解释,-p 即监听的端口,-m 表示开启用特殊手段判断 DNS 污染的功能。其余参数全部扔掉。

TCP DNS Forwarder

这里需要用到的工具是:hev-dns-forwarder ,并没有列在上面,因为包管理仓库并没有它,需要自行编译。
它是一个很简单的 C 语言程序,用发行版官方 toolchains 提供的交叉编译器就可以编译完成,不会遇到困难。它的作用是将 DNS 默认的 UDP 查询请求用 TCP 协议转发一遍再将结果返回。也就是上面 ChinaDNS 配置的第二个 DNS,看 ip 和端口就知道它是一个本地程序,也就是这里的 hev-dns-forwarder 。

那么,我们为什么要把 UDP 的查询请求再用 TCP 转发一遍呢?别忘了 chinadns 的作用,它需要得到正确无污染的 ip 。而显然第一个国内 DNS (例如 114)是不可能的,准确的说在墙内用 UDP 查询域名是不可能的。那么我们将 UDP 查询用 TCP 查一次,并且用国外的 DNS 服务器,例如默认的谷歌 DNS: 8.8.8.8,那么这个请求就可以走代理流量,因为相当与和 8.8.8.8 进行 TCP 通信。利用代理服务器返回查询结果,自然就是无污染的正确 ip 了(同时还有 CDN 就近代理服务器的地域优势) 。

注意:我们的透明代理只代理 TCP 流量,不代理 UDP 流量。所以默认的 DNS 查询流量是不会走代理的,这也是 hev-dns-forwarder 存在的意义。
至于 UDP 和 TCP 做 DNS 协议有什么区别,答案是没有多大的区别。当然这里也没必要讨论,如果你有兴趣可以去阅读:RFC1034、RFC1035

运行方式:直接在路由器上执行编译后的可执行文件。默认参数监听的就是 ChinaDNS 上配置的 5300 端口,默认 DNS 服务器是 8.8.8.8 ,默认绑定到 0.0.0.0 ,所以局域网内的机器都可以将它当作 DNS。当然这是没必要的,即使用也是用上游的 ChinaDNS 。
因为是非安装包形式的,所以需要手动写个脚本加入开机启动,如果你看过我上篇 DD-WRT 的文章,那么:

cat << EOF > /jffs/scripts/startup/S40dnsforwarder
          #!/usr/bin/env sh
          
          /opt/data/ss-redir/hev-dns-forwarder &
          EOF
          chmod +x /jffs/scripts/startup/S40dnsforwarder
          

请自行修改 hev-dns-forwarder 可执行文件的位置,后缀 & 表示后台运行。

配置路由器 DNS 程序

一般路由器发行版默认用的是 DNSMasq 作 DNS 软件,我们需要配置它的 DNS 服务器为 ChinaDNS 。假如你是 DD-WRT 系统:

Services - DNSMasq -> Additional DNSMasq Options 下面填上:

no-resolv
          server=127.0.0.1#5353
          

no-resolv 表示不读取系统的 resolv.conf ,从命令行参数或者 DNSMasq 的配置中获取上游 DNS 服务器,也就是 127.0.0.1 的 5353 端口,即 chinadns 进程。

如果你的路由器系统的管理界面上没有 DNSMasq 的相关配置选项,没关系,也可以通过修改配置文件的方式:

root@DD-WRT:~# ps | grep dnsmasq
            957 root      2416 S    dnsmasq -u root -g root --conf-file=/tmp/dnsmasq.conf --cache-size=1500 --dhcp-option=252," "
          

上面是找出 dnsmasq 进程,注意 BasyBox 版本的 ps 是没有 -A 参数的。可以看到一个 –conf-file=/tmp/dnsmasq.conf 参数,那就是配置文件路径,将配置内容添加到配置文件中即可,效果是一模一样的。
但是这样做在某些系统上可能是不完善的,因为内置的很多软件的配置文件每重启一次就会根据路由器管理页面的配置重新生成一次(一般是储存在 nvram),这也是为什么这些配置文件往往放在 tmp 目录的原因。

配置系统 DNS 软件非常简单,作用也很简单,那就是让它将 ChinaDNS 作为默认 DNS 。

ss-redir

ss-redir 是 ss-libev 的一个组件,所以直接安装 libev 版本即可:

opkg install shadowsocks-libev
          

编辑 init.d 中的相关服务脚本,将默认启动的 ss-local 改成 ss-redir 。参数是一样的,-c 接一个配置文件路径。过程略。

简而言之,你正确启动 ss-redir 即可。在启动了 ss-redir 以后,不需要 ss-local 。

iptables

注意:很多发行版默认带了 iptables ,但是你仍然要安装最新的 iptables:

opkg install iptables
          

找到你刚才安装的 iptables 的可执行文件路径:

root@DD-WRT:~# find / -name 'iptables'
          /opt/lib/iptables
          /opt/sbin/iptables
          /usr/lib/iptables
          /usr/sbin/iptables
          

由上面可以得知,类似 DD-WRT 这样用 Entware 环境的系统的话:

  • 系统自带的 iptables 在: /usr/sbin/iptables
  • 安装的 iptables 在: /opt/sbin/iptables

我们不用系统内置的 iptables ,我们用安装的新版本。所以需要用绝对路径调用 iptables,避免直接使用 iptalbs 时优先搜索到的是内置的 /usr/sbin/ 目录中的 iptables 。如果你的系统在 bin 或者 sbin 中只存在一个 iptables 那就不用管了。

安装 ipset :

opkg install ipset
          

iptables 需要和 ipset 配合定义流量转发的规则,而透明代理的原理其实就是靠 iptables 的规则决定当与那些 ip 通信时转发流量到代理端口,那些不转发。所以 iptables 其实是核心。创建 ssrouter.sh 脚本:

#!/usr/bin/env sh
          
          IPT14=/opt/sbin/iptables
          IP_FILE=/opt/etc/chinadns_chnroute.txt
          
          # 创建并应用代理规则
          create_all_route(){
              # 创建 ipset 集合
              ipset -N china_routes hash:net maxelem 99999
              # 将国内 ip 段全部添加至集合中
              ( while read ip; do ipset add china_routes "$ip"; done ) < "$IP_FILE"
              # 创建 nat chain
              ${IPT14} -t nat -N SHADOWSOCKS
              # 过滤内网以及代理服务器通信流量
              ${IPT14} -t nat -A SHADOWSOCKS -d 192.168.0.0/16 -j RETURN
              ${IPT14} -t nat -A SHADOWSOCKS -d <你的代理服务器 ip> -j RETURN
              # 添加 ipset 集合规则(有比 iptables 一条条规则的添加和线性查找方式 速度更快优势)
              ${IPT14} -t nat -A SHADOWSOCKS -p tcp -m set --match-set china_routes dst -j RETURN
              # 规则之外的所有 ip 的 tcp 流量重定向到 1080 端口(ss 端口)
              ${IPT14} -t nat -A SHADOWSOCKS -p tcp -j REDIRECT --to-port 1080
              # 包括路由器本身流量
              ${IPT14} -t nat -A OUTPUT -p tcp -j SHADOWSOCKS
              # 应用规则
              ${IPT14} -t nat -A PREROUTING -p tcp -j SHADOWSOCKS
          }
          
          # 清空代理规则
          clean_all_route(){
              ${IPT14} -t nat -D OUTPUT -p tcp -j SHADOWSOCKS
              ${IPT14} -t nat -F SHADOWSOCKS
              ${IPT14} -t nat -X SHADOWSOCKS
              ipset destroy china_routes
          }
          "$1"_all_route
          

启用和关闭透明代理:

sh ssrouter.sh create | clean
          

脚本中的两个变量 IPT14 和 IP_FILE 分别是自己安装的 iptables 路径(版本是 1.4 所以后缀 14,DD-WRT 内置的是 1.3 版本并不行)和所有的中国大陆 ip 段,可以直接使用 chinadns 自带的那个。

注意,下面两条:

${IPT14} -t nat -A SHADOWSOCKS -d 192.168.0.0/16 -j RETURN
          ${IPT14} -t nat -A SHADOWSOCKS -d <你的代理服务器 ip> -j RETURN
          

分别是我自己的环境的内网 192.168.0.0/16 和代理服务器。首先明确的指出来,第一条内网是必须添加的,否则连你访问路由器都会走代理,不可能正常通信的。如果不加上最重要的这一条那你可能永远无法控制路由器了,严重的话只能重置路由器。所以我建议你先不要加入开机启动,先确认下脚本执行效果是完美的。
后面的 <你的代理服务器 ip> 规则条数是不限制的,把这段括号替换成你自己的代理服务器 ip 即可,你有几个会用到的服务器就添加几条。因为唯独和代理服务器这种海外 ip 通信需要是直连的。

注意:iptables 和 ipset 版本不达标就不用试了,你们需要看后面的脚本内容。

启用代理

确保:DNSMasq 配置正确、ChinaDNS 参数和运行正常、hev-dns-forwarder 正在运行、ss-redir 正在运行并且确保配置文件 json 中的服务器是可用的。
那么你就可以执行启用命令了:

sh ssrouter.sh create
          

它可能需要几十秒的时间完全执行完毕,没有输出错误,正常执行完成以后透明代理就启用了。这时候内网的所有客户端就无需再使用代理软件了,它们的所有通信流量都由路由器来决定走代理或者不走代理。
关掉你 Chrome 上的 SwitchyOmega 扩展,试试访问谷歌,看看成功没有。再访问一下 http://www.ip.cn 看看你的位置是在中国吗?如果谷歌能访问、国内网站又没走代理,那么就成功了。然后你就可以开机启动这个脚本添加 create 参数,让透明代理开机自动生效。

注意:在我这里开机自启动脚本调用 ipset 无法创建和添加集合内容。准确的说是添加的内容是临时的,并没有在外部生效,就像在 shell 脚本中定义的变量并不能在外部生效那样。即使脚本完全无误的执行完毕,我的 ipset list 仍然是空的,代理自然不正确。所以我通常是路由器启动以后手动执行一下这个脚本,ipset 才能正常的创建集合和添加集合内容。
原因未知,包括子进程、延时等方法都试过了,必须要手动执行脚本 ipset 才正常,无法理解。所以我暂时就是手动,毕竟路由器基本上也重启不了几次。当然你们可以开机自启试试,也许仅仅是我这边的系统问题。

本文的教程和目的也到此结束。

更新 chinadns_chnroute.txt

 wget -O- 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest' \
          | awk -F\| '/CN\|ipv4/ { printf("%s/%d\n", $4, 32-log($5)/log(2)) }' > \
          /opt/etc/chinadns_chnroute.txt
          

可以隔一段时间更新一下这个文件,不需要太频繁。我隔了十多天更新的也才多了两条。它被 chinadns 和 ipset 使用,目前接近 8000 条。

低版本 iptables 使用方法

低版本的 iptables 是没有 -m set 参数的,没有办法和 ipset 配合。那么所有国内 ip 只能跟添加内网和代理服务器 ip 那样,作为一条条的规则添加进来。

# 过滤内网以及代理服务器通信流量
          iptables -t nat -A SHADOWSOCKS -d 192.168.0.0/16 -j RETURN
          iptables -t nat -A SHADOWSOCKS -d <你的代理服务器 ip> -j RETURN
          # # 将国内 ip 段全部添加成规则
          ( while read ip; do iptables -t nat -A SHADOWSOCKS -d "$ip" -j RETURN; done ) < "$IP_FILE"
          

可以看到,这样的话 iptables 就有了大量的规则,而使用 ipset 只需要一条规则。优势我在注释里边也写了,主要是添加和规则匹配速度更快。这里用 iptables 添加了好几千条规则,虽然数量庞大,但是通常性能影响并不会慢到可见的程度。主要缺点只是添加速度很慢,如果是 ipset 将几千条 ip add 进集合只需要几十秒,而 iptables 添加几千条规则可能要十几分钟。 这样就将就的完成了 iptables 的配置,虽然没有 ipset 的优化效果,但是并不要太过在意。

为什么在能使用 ipset 的情况下,还要用 iptables 多添加几条规则呢(例如内网和代理服务器)?我这么做的原因是为了安全,如果 ipset 出现问题,能确保路由器访问,代理能继续。

最后

因为是 Linux 的原因,所以上面的所有内容在流行的 GNU/Linux 发行版上都通用。如果没有路由器,在本机配置这样的环境,本机也是有透明代理的效果的(所以路由器自身的网络也有)。

如果你想在访问国外某些网站时不走代理,那么可以将该网站 ip 添加到 ipset 的 china_routes 集合中或者加入 iptables 的规则中。当然,通常是没有必要这么做的,因为现在基本上海外网站不是被墙就是半墙的状态。直连速度过得去的已经不多了。