注明:本 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 库可分为 OpenSSL
和 PolarSSL
两种版本:
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 servers
为114.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 servers
为127.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-mbedtls
、 ca-certificates
和ca-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
- Shaowsocks + GfwList 实现 OpenWRT / LEDE 路由器自动翻墙 - https://cokebar.info/archives/962
- https://github.com/softwaredownload/openwrt-fanqiang
- 打造一台翻墙路由器 - https://github.com/MichaelXue/blog
- 最好的 OpenWrt 路由器 shadowsocks 自动翻墙、科学上网教程 - https://fanqiang.software-download.name/
- 小米路由器mini折腾之自动翻墙篇 - https://blog.phpgao.com/carzy_router.html
- Shadowsocks + GfwList 实现 OpenWRT / LEDE 路由器自动翻墙 - https://cokebar.info/archives/962
- 在路由器上部署 shadowsocks - https://zzz.buzz/zh/gfw/2016/02/16/deploy-shadowsocks-on-routers/
- 小米路由器mini刷PandoraBox使用Shadowsocks - https://blog.nex3z.com/2015/12/20/%E4%BD%BF%E7%94%A8%E5%B0%8F%E7%B1%B3%E8%B7%AF%E7%94%B1%E5%99%A8mini/
- [经验技巧] 小米路由器mini配置SS+ChinaDNS实现自动XX - http://www.miui.com/thread-3067556-1-1.html
- 小米路由器mini折腾之配置opkg篇 - https://blog.phpgao.com/xiaomi_router_opkg.html
- OpenWrt-dist - http://openwrt-dist.sourceforge.net/
- 小米路由器 mini 刷 OpenWrt/PandoraBox/LEDE - https://leamtrop.com/2017/05/11/flash-openwrt-squashfs/
- OpenWrt Xiaomi Mi WiFi Mini - https://openwrt.org/toh/xiaomi/mini
- 小米mini安装openwrt - https://kknews.cc/zh-hk/tech/a8plgj.html
- 安装dnsmasq-full的时候先卸载了openwrt自带的dnsmasq,一直连接不上 - https://github.com/bettermanbao/openwrt-shadowsocksR-libev-full/issues/20