【Network】Shadowsocks + OpenWRT + dnsmasq-full + ipset + gfwList 实现路由器(小米路由器mini)自动翻墙

Posted by 西维蜀黍 on 2019-01-30, Last Modified on 2023-11-26

注明:本 blog 仅作为技术研究讨论学习信息安全相关技术,请勿用于任何违法用途!并请严格遵命各国和地区法律!

翻墙思路

本方案基于gfwlist,gfwlist( https://github.com/gfwlist/gfwlist )中记录了已经被gfw封锁的域名(https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt)。

为了绕过GFW的DNS污染,访问处于gfwlist中列表的域名时,我们可以借助ss-tunnel通过 Shadowsocks Server 请求国外公共 DNS(如8.8.8.8,4.4.4.4)以获得干净的DNS解析结果。当然,我们也可以在国外或不被GFW投毒的地方架设DNS服务器,通过访问自己的这个DNS服务器,以获得干净的DNS解析结果。

在访问处于gfwlist中列表的域名时,在获取干净的DNS解析结果后,我们还需要通过ss-redir来与这个目标主机建立Socket连接,以逃避GFW的TCP RST重置。

gfwlist中的域名站点走代理,不在List中的域名不走代理。本质上依然是根据目标主机的IP来进行判断。dnsmasq-full可以将解析域名得到的IP加到一个ipset中,最终利用这个ipset来判断走不走代理。实际是完成了gfwlist(域名列表)到dnsmasq的ipset规则再到IP地址的转换。

本方案的优点明确,只有被墙的站点才走代理。但是,虽然gfwlist每天都在不停的更新,但是gfwlist并不能100%涵盖被墙站点,而且有些国外站点直连速度远不如走代理,特别是你代理服务器速度较快,希望通过代理加速国外访问时,此方案就存在一定的不足。


作为本方案的弥补,我选择在PC上安装SwitchyOmega。当访问被墙而又不在gfwlist列表中网站,或者直接访问非常非常慢的国外站点时,使用手动切换为全局走ss的策略。

因此,对于在gfwlist中的网站走ss(黑名单机制),通过http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest 实现一定在国内的网站直接访问(白名单机制)。

分析

iptables只能根据IP地址定义规则进行转发,不能识别域名,而dnsmasq-full不仅可以实现域名-IP的映射,还可以把这个映射关系存储在ipset中,所以使用dnsmasq-full+ipset就可以实现iptables对域名的转发,可以实现很多功能。

具体来说,当dnsmasq-full接收到一个DNS查询请求,首先匹配配置文件中的域名列表(对应于gfwlist),如果匹配成功某域名,就把IP的查询结果存储在一个ipset集合中,

此后iptables就可以使用ipset中的这个IP集合,即若客户端访问这个集合中的某个IP时,就进行相应的处理(比如DROP或者REDIRECT)。

在我们的翻墙场景中,比如用户访问www.google.com ,dnsmasq-full查询gfwlist,发现这个域名在其中,则在(通过ss-tunnel)进行DNS解析后(绕过了GFW的DNS污染),把其对应的IP存入ipset中。当此后用户尝试与www.google.com 对应的IP建立连接时,iptables将这个TCP连接重定向到ss中,最终绕过了GFW的TCP RST攻击。

刷入最新的OpenWrt

这里踩坑了,本来我的小米路由器mini一致使用PandoraBox固件(PandoraBox-ralink-mt7620-xiaomi-mini-squashfs-sysupgrade-r1024-20150608)。下载地址:http://downloads.openwrt.org.cn/PandoraBox/Xiaomi-Mini-R1CM/stable/ 。而由于PandoraBox不支持opkg-key命令(在下面会使用到),shadowsocks不能自动安装。最终折腾上了最新的OpenWrt。

首先路由器型号需要在OpenWrt支持的列表中:http://wiki.openwrt.org/toh/start/ (可以ctrl+F搜索匹配型号),并记录所用路由器CPU的型号。

下载并安装OpenWrt

从downloads.openwrt.org,下载最新的OpenWrt ROM,当前(2019.1.29)最新的OpenWrt版本为18.06.1。

下载地址:https://downloads.openwrt.org/releases/18.06.1/targets/ramips/mt7620/openwrt-18.06.1-ramips-mt7620-miwifi-mini-squashfs-sysupgrade.bin。

并刷入这个OpenWrt固件到路由器中。

查询信息

查看OpenWrt版本

[root@PandoraBox:/root]#cat /proc/version
Linux version 3.14.44 (lintel@PandoraBox-Server) (gcc version 4.8.3 (PandoraBox/Linaro GCC 4.8-2014.04 r969) ) #7 Mon Jun 8 22:23:15 CST 2015

查看CPU信息

我使用的是小米路由器mini,其CPU为MTK MT7620A 单核580MHz

[root@PandoraBox:/mnt/sda1]#cat /proc/cpuinfo
system type		: MediaTek MT7620
machine			: Xiaomi mini Board
processor		: 0
cpu model		: MIPS 24KEc V5.0
BogoMIPS		: 385.84
wait instruction	: yes
microsecond timers	: yes
tlb_entries		: 32
extra interrupt vector	: yes
hardware watchpoint	: yes, count: 4, address/irw mask: [0x0ffc, 0x0ffc, 0x0ffb, 0x0ffb]
isa			: mips1 mips2 mips32r2
ASEs implemented	: mips16 dsp
shadow register sets	: 1
kscratch registers	: 0
core			: 0
VCED exceptions		: not available
VCEI exceptions		: not available

查看当前路由器的架构

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

安装Shadowsocks及依赖应用

建议所有安装与配置操作都在控制台下完成,如通过OpenWrt的luci操作,有可能出现未获得预期效果后不好调试的问题。

说明

shadowsocks-libev:官方原版,包含 ss-{local,redir,tunnel} 默认启动 ss-local, 以对外提供 SOCKS5 代理,其中

  • ss-redir 建立透明代理
  • ss-rules 生成代理规则
  • ss-tunnel 提供 UDP 转发

shadowsocks-libev-spec:针对 OpenWrt 的优化版本,包含 ss-{redir,rules,tunnel}。从 v1.5.2 开始可以使用 LuCI 配置界面。

SSL依赖说明

shadowsocks-libev-spec依赖于SSL,根据依赖的 SSL 库可分为 OpenSSLPolarSSL 两种版本:

  • OpenSSL 版依赖 libopenssl, 支持加密方式多, 体积大
  • PolarSSL 版依赖 libpolarssl, 体积小, 加密方式少

两种版本使用上并无差异,根据ROM中预装的 SSL 库选择即可。

添加第三方源(openwrt-dist)

添加 openwrt-dist.pub 到opkg的 keys 中。只有这样,第三方的包才能通过签名验证:

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

获取路由器对应的架构(architecture):

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

请根据自己的CPU架构(可以执行 opkg print-architecture 查看),将厦门的{architecture}替换成相应的文本。

添加以下源到 /etc/opkg/customfeeds.conf

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

更新最新opkg源信息:

$ opkg update

卸载dnsmasq并安装dnsmasq-full

这里踩坑了!!之前我安装的是最新的dnsmasq(2.80)版本,通过dnsmasq -v也能看到其已经支持了ipset。

然而,若在/etc/dnsmasq.d/dnsmasq_gfwlist_ipset.conf 中放入ipset=/xx.com/gfwlist时,则会出现"dnsmasq: recompile with HAVE_IPSET defined to enable ipset directives"的错误。显然在重新对dnsmasq进行编译前,dnsmasq仍然不能有效支持ipset。

最终决定卸载dnsmasq并安装dnsmasq-full。

执行:

$ opkg remove dnsmasq && opkg install dnsmasq-full

注意,由于dnsmasq默认负责路由器的DNS解析和DHCP分配工作。因此在卸载了dnsmasq后,opkg install 可能会因为无法获得DNS解析工作而无法正常执行。

一个解决办法可以是,进入OpenWrt的GUI界面,进入[Network] - [Interfaces] - [WAN Edit],进入[Advanced Settings],修改Use custom DNS servers114.114.114.114

在路由器的控制台中ping一下baidu.com(ping baidu.com),若可以正常ping通,则说明当前DNS工作正常。opkg install也当然可以正常工作。

还有一个方法,在网络正常的机器上,执行 ping 到一个源(比如,downloads.openwrt.org或者mirrors.ustc.edu.cn)。在获得其IP后,修改 /etc/opkg/distfeeds.conf将其域名替换为对应IP,以避免无法获得DNS解析而导致的opkg install无法正常执行的情况。

安装shadowsocks-libev-spec

$ opkg install shadowsocks-libev luci-app-shadowsocks
$ opkg install ipset libpthread
# ChinaDNS在此方案中不会被使用到
$ opkg install ChinaDNS luci-app-chinadns

以上为直接从第三方源(openwrt-dist)下载并安装shadowsocks-libev及其依赖。


但事实上,有可能在已经添加了第三方源(openwrt-dist)后,进行opkg update时,提示以下连接错误:

$ opkg update
Downloading http://openwrt-dist.sourceforge.net/packages/base/mipsel_24kc/Packages.gz
Failed to establish connection
*** Failed to download the package list from http://openwrt-dist.sourceforge.net/packages/base/mipsel_24kc/Packages.gz

Downloading http://openwrt-dist.sourceforge.net/packages/luci/Packages.gz
Failed to establish connection
*** Failed to download the package list from http://openwrt-dist.sourceforge.net/packages/luci/Packages.gz

个人主观推测,有可能是GFW对openwrt-dist.sourceforge.net的访问进行的干扰。因此,你也可以直接访问https://dl.bintray.com/aa65535/opkg/shadowsocks-libev/或者https://github.com/shadowsocks/openwrt-shadowsocks/releases下载最新的二进制版(binary)。当然,你也可以下载源代码来直接编译。

然后通过scp命令从本机将.ipa拷贝到路由器上,再通过opkg install shadowsocks-libev_3.2.5-1_mipsel_24kc.ipk完成安装。

配置Shadowsocks

配置文件

配置 /etc/shadowsocks.json ,格式如下:

{
    "server": "...",
    "server_port": 21122,
    "local_address": "0.0.0.0",
    "local_port": 1080,
    "password": "...",
    "method": "chacha20-ietf-poly1305",
    "mode": "tcp_and_udp",
    "fast_open": true
}

请自行修改好服务器IP、端口号、密码、加密方式。

新建执行文件

新建文件: /etc/init.d/shadowsocks :

#!/bin/sh /etc/rc.common
 
START=95
 
SERVICE_DAEMONIZE=1
CONFIG=/etc/shadowsocks.json
DNS=8.8.8.8:53
TUNNEL_PORT=5353
 
start() {
	# 由于在每次路由器重启后,ipset都会被清空,因而创建一个 iphash 类型的 set, the equivalent command is ipset create gfwlist hash:ip
	ipset -N gfwlist iphash
	iptables -t nat -A PREROUTING -p tcp -m set --match-set gfwlist dst -j REDIRECT --to-port 1080
	iptables -t nat -A OUTPUT -p tcp -m set --match-set gfwlist dst -j REDIRECT --to-port 1080
	iptables -t nat -A OUTPUT -p udp --dport 443 -j REDIRECT --to-ports 1080
	iptables -t nat -A PREROUTING -p udp --dport 443 -j REDIRECT --to-ports 1080

	# Proxy Mode
	service_start /usr/bin/ss-redir -c $CONFIG -b 0.0.0.0 -u
	# Tunnel
	service_start /usr/bin/ss-tunnel -c $CONFIG -b 0.0.0.0 -u -l $TUNNEL_PORT -L $DNS
}
 
stop() {
  # Solve "Set cannot be destroyed: it is in use by a kernel component"
	service firewall stop
	ipset destroy gfwlist
	
	# Proxy Mode
	service_stop /usr/bin/ss-redir
	# Tunnel
	service_stop /usr/bin/ss-tunnel
	
	# Restart the firewall
	service firewall start
}

修改文件权限:

$ chmod +x /etc/init.d/shadowsocks

然后启动shadowsocks,并设置开机运行:

$ /etc/init.d/shadowsocks enable
$ /etc/init.d/shadowsocks start

最后检查一下是否正常启动了:

$ netstat -lnp | grep ss-redir

如果未能正确启动,尝试手动执行,看看报什么错:

$ ss-redir -c /etc/shadowsocks.json -b 0.0.0.0 -v

开启 TCP Fast Open (TCP快速打开,缩略为TFO)

要求: 系统内核版本≥3.7,shadowsocks-libev≥3.0.4,shadowsocks服务端开启tcp fast open。

通过echo "net.ipv4.tcp_fastopen = 3" > /etc/sysctl.conf修改 /etc/sysctl.conf ,以加入如下一行:

net.ipv4.tcp_fastopen = 3

执行如下命令使之生效:

$ sysctl -p

配置ipset、iptables和dnsmasq-full

配置ipset和iptables

如果希望深入了解iptables,可以访问【Linux】iptables 防火墙

在控制台输入以添加如下规则(–to-port后的1080是我设置的路由器提供shadowsocks服务的本地端口,你需要根据具体情况修改):

# 在ipset中建立一个名为gfwlist的ip哈希表
$ ipset -N gfwlist iphash

# 在 nat table 创建一个叫 SHADOWSOCKS 的 chain
$ iptables -t nat -N SHADOWSOCKS

# 若tcp请求位于gfwlist中的ip,则重定向到shadowsocks走代理
$ iptables -t nat -A PREROUTING -p tcp -m set --match-set gfwlist dst -j REDIRECT --to-port 1080
$ iptables -t nat -A OUTPUT -p tcp -m set --match-set gfwlist dst -j REDIRECT --to-port 1080
$ iptables -t nat -A OUTPUT -p udp --dport 443 -j REDIRECT --to-ports 1080
$ iptables -t nat -A PREROUTING -p udp --dport 443 -j REDIRECT --to-ports 1080

# x.x.x.x为shadowsocks服务器地址
$ iptables -t nat -A shadowsocks -d x.x.x.x -j RETURN 

$ iptables -t nat -A shadowsocks -d 0.0.0.0/8 -j RETURN
$ iptables -t nat -A shadowsocks -d 10.0.0.0/8 -j RETURN
$ iptables -t nat -A shadowsocks -d 127.0.0.0/8 -j RETURN
$ iptables -t nat -A shadowsocks -d 169.254.0.0/16 -j RETURN
$ iptables -t nat -A shadowsocks -d 172.16.0.0/12 -j RETURN
$ iptables -t nat -A shadowsocks -d 192.168.0.0/16 -j RETURN
$ iptables -t nat -A shadowsocks -d 224.0.0.0/4 -j RETURN
$ iptables -t nat -A shadowsocks -d 240.0.0.0/4 -j RETURN

配置dnsmasq-full

执行

$ echo "conf-dir=/etc/dnsmasq.d" >> /etc/dnsmasq.conf

以在 /etc/dnsmasq.conf 的最后加入 conf-dir=/etc/dnsmasq.d

添加gfwlist

新建并进入目录 /etc/dnsmasq.d ,从 https://cokebar.github.io/gfwlist2dnsmasq/dnsmasq_gfwlist_ipset.conf 下载 dnsmasq_gfwlist_ipset.conf 后放入该目录。

cokebar大神写了一个将gfwlist转换成包含ipset的dnsmasq-full格式的工具(https://github.com/cokebar/gfwlist2dnsmasq)。

我们也可以根据自己的情况自行修改这个文件,格式如下:

#使用不受污染干扰的DNS解析该域名 可以将此IP改为自己使用的DNS服务器
server=/google.com/127.0.0.1#5353
#将解析出来的IP保存到名为gfwlist的ipset表中
ipset=/google.com/gfwlist

设置默认的DNS

进入OpenWrt的GUI界面,进入[Network] - [Interfaces] - [WAN Edit],进入[Advanced Settings],修改Use custom DNS servers127.0.0.1

这意味着,连接该OpenWrt路由器的所有设备发出的DNS请求都会由该路由器的dnsmasq来响应(当然,前提是设备没有手动去修改默认的DNS服务器IP,而使用路由器默认提供的DNS服务器IP)。

在[Network] - [DHCP and DNS]中,设置DNS forwardings为114.114.114.114。注意,此为路由器默认查询的DNS服务器,你可以根据你的实际情况选择一个较快的DNS服务器(在我的情况中,我认为114.114.114.114最快)。

当这个配置修改生效后,通过 cat /var/etc/dnsmasq.conf.* 可以看到对应的 dnsmasq 的配置文件也发生了变化,如下图:

自动更新gfwlist

gfwlist2dnsmasq需要通过SSL下载gfwlist,而OpenWrt安装的busybox wget,并不支持SSL,因此我们需要安装libustream-mbedtlsca-certificatesca-bundle。除此之外,gwflist使用BASE64编码,因此base64需要被安装:

$ opkg update
$ opkg install libustream-mbedtls coreutils-base64
$ opkg install ca-certificates ca-bundle

下载gfwlist2dnsmasq.sh

$ wget https://raw.githubusercontent.com/cokebar/gfwlist2dnsmasq/master/gfwlist2dnsmasq.sh

gfwlist2dnsmasq.sh增加执行权限:

$ chmod 777 /root/gfwlist2dnsmasq.sh

执行crontab –e建立一个定期执行任务,并将以下代码复制。

0 1 * * * sh /root/gfwlist2dnsmasq.sh -p 5353 -s gfwlist -o /root/dnsmasq_gfwlist_ipset_`date "+%Y-%m-%d"`.conf \
&& rm -rf /etc/dnsmasq.d/* \
&& cp /root/dnsmasq_gfwlist_ipset_`date "+%Y-%m-%d"`.conf /etc/dnsmasq.d/ \
&& /etc/init.d/dnsmasq restart

常见诊断

防火墙设置

  • 使用 service firewall stop命令关闭服务器的防火墙排查是否防火墙挡住了请求。
  • 使用 iptables -t nat -L -n查看nat表中的所有规则。

dnsmasq配置

dnsmasq --test 查看dnsmasq配置是否正确。

/etc/init.d/dnsmasq restart重启dnsmasq服务。

使用top查看当前dnsmasq是否启动,且正常使用的配置文件路径。由于默认情况下,Openwrt允许我们通过Luci在界面上配置dnsmasq,因此Luci会自动生成一个dnsmasq配置文件(默认位于/var/etc/dnsmasq.conf.*)。通过top可以看到这个生成的配置文件的准确路径

ipset

ipset list gfwlist查看ipset中我们定义的gfwlist中包含的所有记录。

Shadowsocks

Shadowsocks是否已经启动

检查ss是否正常运行netstat -anptl |grep ss|grep LISTEN

Shadowsocks输出log

使用-v以让Shadowsocks记录log到指定文件:

$ /usr/bin/ss-redir -c /etc/shadowsocks.json -b 0.0.0.0 -u -v 1>>/var/log/ss-redir &
$ /usr/bin/ss-tunnel -c /etc/shadowsocks.json -b 0.0.0.0 -u -l 5353 -L 8.8.8.8:53 -v 1>>/var/log/ss-tunnel.log &

检查Shadowsocks中的ss-tunnel是否提供正确的DNS解析

你可以通过以下方式,在连接到Openwrt的主机上测试,是否可以正常通过ss-tunnel连接到Shadowsocks Server以获得未被污染的DNS解析结果:

首先在Openwrt上手动启动ss-tunnel:

$ /usr/bin/ss-tunnel -c /etc/shadowsocks.json -b 192.168.16.1 -u -l 5353 -L 8.8.8.8:53 -v 1>>/var/log/ss-tunnel.log &

在主机上向Openwrt上的ss-tunnel请求DNS解析服务,注意我的Openwrt IP为192.168.16.1,ss-tunnel运行在5353端口下,你要根据实际情况修改:

$ dig @192.168.16.1 -p 5353 www.google.com 

以下是ss-tunnel的日志:

 2019-06-19 10:45:00 INFO: initializing ciphers... chacha20-ietf-poly1305
 2019-06-19 10:45:00 INFO: listening at 192.168.16.1:5353
 2019-06-19 10:45:00 INFO: UDP relay enabled
 2019-06-19 10:45:04 INFO: [udp] server receive a packet
 2019-06-19 10:45:04 INFO: [53] [udp] cache miss: 8.8.8.8:53 <-> 192.168.16.222:49491
 2019-06-19 10:45:09 INFO: [udp] server receive a packet
 2019-06-19 10:45:09 INFO: [53] [udp] cache hit: 8.8.8.8:53 <-> 192.168.16.222:49491
 2019-06-19 10:45:14 INFO: [udp] server receive a packet
 2019-06-19 10:45:14 INFO: [53] [udp] cache hit: 8.8.8.8:53 <-> 192.168.16.222:49491

DNS配置

增加以下内容到/etc/dnsmasq.conf的尾部:

# For debugging purposes, log each DNS query as it passes through dnsmasq.
log-queries

# Log to this syslog facility or file. (defaults to DAEMON)
log-facility=/var/log/dnsmasq.log

重启dnsmasq:

$ /etc/init.d/dnsmasq restart

使用以下命令可以实时查看dnsmasq的工作日志:

$ tail -f /var/log/dnsmasq.log

Openwrt的dig安装

$ opkg update
$ opkg install bind-dig
$ dig baidu.com

可用于检测当前路由器上的DNS服务是否正常。

shadowsocks测速

https://lvii.gitbooks.io/outman/content/speed_test.html

Reference