基于Asus Merlin+RaspberryPI打造无感知科学上网路由

in #gfw7 years ago

在上一篇文章《基于无预置IP list的GFW IP解锁方法》中讲述了如何通过Iptables自动检测受干扰的IP地址,在这里将进一步将相关代码整合到实际路由器中。前期个人买了一个Asus Merlin AC-87U,本想将Shadowsocks编译进路由器中,直接实现路由器内的透明代理转发,但尝试半天后,始终未能编译。于是考虑用RaspberryPi+Shadowsocks外挂AC-87U下实现路由转发。

具体实现步骤如下:

1. Asus Merlin AC-87U 干扰IP监测+自动策略路由配置

《基于无预置IP list的GFW IP解锁方法》文中已经提到,实现无IP List的受干扰IP监测和路由转发主要分为两个步骤:

  • 在Iptables中配置GFW干扰IP监测策略
  • 将检测到的干扰IP纳入策略路由中

由于华硕Merlin固件仅支持在特定JFFS分区下进行用户数据读写,同时仅允许在系统启动时在/jffs/scripts/jffs/configs加载指定脚本和配置数据。因此上述两个关键步骤,就需要在/jffs/scripts目录下的指定脚本中进行配置。具体如下:

1.1 在/jffs/scripts/firewall-start脚本中配置iptables过滤策略

为了确保受干扰IP监测策略不被其他系统内置策略覆盖或影响,需要在路由器启动后并配置iptables默认策略后,将我们所需要的监测策略加入到Iptables的FORWARD链中,生效后通过iptables-save查看输出,应该可以看到在FORWARD链中已经增加了我们所需的4条指令。

# Generated by iptables-save v1.4.14 on Thu Sep 21 21:38:26 2017
*filter
:INPUT ACCEPT [7225:1346582]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [7504:1290180]
.....

###################
#一定要确保以下4条FORWARD规则是在FORWARD链的最前端,否则会被后续的规则直接bypass

-A FORWARD -i eth0 -p tcp -m tcp --tcp-flags RST RST -j LOG --log-prefix "GFW_DECT_RST "
-A FORWARD -i br0 -o eth0 -p tcp -m tcp --tcp-flags SYN,ACK SYN -j LOG --log-prefix "GFW_DEBUG "
-A FORWARD -i br0 -o eth0 -p tcp -m tcp --tcp-flags SYN,ACK SYN -m recent --set --name syn_watch_list --rdest
-A FORWARD -i br0 -o eth0 -p tcp -m tcp --tcp-flags SYN,ACK SYN -m recent --rcheck --seconds 15 --hitcount 3 --name syn_watch_list --rdest -j LOG --log-prefix "GFW_DECT_SYN "

####################

-A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
-A FORWARD ! -i br0 -o eth0 -j DROP
-A FORWARD -i eth0 -m state --state INVALID -j DROP
-A FORWARD -i br0 -o br0 -j ACCEPT
-A FORWARD -j NSFW
-A FORWARD -m conntrack --ctstate DNAT -j ACCEPT
-A FORWARD -i br0 -j ACCEPT
.....
COMMIT
# Completed on Thu Sep 21 21:38:26 2017

1.2 在/jffs/scripts/service-start脚本中配置策略路由

由于在本方案中AC-87U路由器并未内置Shadowsocks,无法实现路由器内的透明代理,因此我们需要将经iptables监测到的疑似受干扰目标ip的下一跳路由调整到装了Shadowsocks的路由器中,也就是本文的RaspberryPi中。

为了实现这个目标,需要借助watch命令每秒钟刷新一次内核日志,过滤出需要做策略路由的ip地址,相关的watch指令,写在所有服务启动后的/jffs/service-start脚本中:

#export GFW_LOCAL_GATEWAY=[Replace with your free Internat access gateway ip]
export GFW_LOCAL_GATEWAY=192.168.9.137
export GFW_REMOTE_VPN=[Replace with your remote ss-server ip]

#注意需要避免将到VPN路由地址[GFW_REMOTE_VPN]指向RaspberryPi路由器的地址
watch -n 1 dmesg -c|grep GFW_DECT|grep -v $GFW_REMOTE_VPN|\
        awk '/GFW_DECT_SYN/{print $5};/GFW_DECT_RST/{print $4}'|\
        awk -F= '{if($1=="SRC"){cmd="route -n|grep "$2" >/dev/null 2>&1 || \
                route add "$2" gateway "ENVIRON["GFW_LOCAL_GATEWAY"]" >/dev/null 2>&1"} \
                else{cmd="route -n|grep "$2" || ping -c 1 -W 1 "$2"  >/dev/null 2>&1 || \
                route add "$2" gateway "ENVIRON["GFW_LOCAL_GATEWAY"]" >/dev/null 2>&1"}; \
                print(cmd);system(cmd)}'

配置完重启路由器后,在电脑上浏览几个被屏蔽的网站,然后在AC-87U中通过route命令进行观察,便可以看到被干扰的IP地址,已正确的加入了策略路由中,转发给RaspberryPi路由器(记录略多哈 :-P ):

Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.217.5.65    192.168.9.137   255.255.255.255 UGH   0      0        0 br0
50.19.85.98     192.168.9.137   255.255.255.255 UGH   0      0        0 br0
172.217.5.68    192.168.9.137   255.255.255.255 UGH   0      0        0 br0
17.249.28.69    192.168.9.137   255.255.255.255 UGH   0      0        0 br0
39.155.151.5    192.168.9.137   255.255.255.255 UGH   0      0        0 br0
104.19.192.102  192.168.9.137   255.255.255.255 UGH   0      0        0 br0
104.18.55.167   192.168.9.137   255.255.255.255 UGH   0      0        0 br0
74.125.170.72   192.168.9.137   255.255.255.255 UGH   0      0        0 br0
50.19.240.106   192.168.9.137   255.255.255.255 UGH   0      0        0 br0
172.217.5.74    192.168.9.137   255.255.255.255 UGH   0      0        0 br0
74.125.170.74   192.168.9.137   255.255.255.255 UGH   0      0        0 br0
117.121.27.11   192.168.9.137   255.255.255.255 UGH   0      0        0 br0
23.210.109.43   192.168.9.137   255.255.255.255 UGH   0      0        0 br0
......
169.254.39.0    *               255.255.255.0   U     0      0        0 br0
192.168.1.0     *               255.255.255.0   U     0      0        0 eth0
192.168.9.0     *               255.255.255.0   U     0      0        0 br0
127.0.0.0       *               255.0.0.0       U     0      0        0 lo
default         192.168.1.1     0.0.0.0         UG    0      0        0 eth0

2. RaspberryPi+Shadowsocks透明代理

在已编译好Shadowsocks的RaspberryPi中的/etc/rc.local中配置透明转发规则:


#!/bin/sh -e
#
# rc.local

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

export GFW_LOCAL_GATEWAY=[Replace with your free Internat access gateway ip]
export GFW_REMOTE_VPN=[Replace with your remote ss-server ip]
sudo ss-nat -s $GFW_REMOTE_VPN -l your_ss_redir_local_port -b $GFW_REMOTE_VPN -u
sudo nohup ss-redir -s $GFW_REMOTE_VPN -p your_ss_server_passwd_listening_port \
                    -k your_ss_server_passwd -m your_ss_server_crypto_method \
                    -l your_ss_redir_local_port -b 0.0.0.0 -u

exit 0

至此,AC-87U已经可以与RaspberryPi配合自动监测受干扰的IP地址,并进行策略路由,并进一步进行透明代理转发。

3. Asus Merlin AC-87U DNS策略查询

当然,为了实现自由上网,除对IP地址做策略路由和透明代理外,还需进一步保护DNS不受污染,在这里感谢Felix Yan同学提供的dnsmasq-china-list工具,可以让路由器对不同的域名进行策略查询。

  • 将dnsmasq-china-list压缩包从github上下载后,解压到AC-87U本地的/jffs/dnsmasq目录下

  • 在/jffs/scripts/init-start脚本中配置主机启动后策略DNS解析文件自动拷贝

#!/bin/sh

mkdir /etc/dnsmasq.d
cp /jffs/dnsmasq/*.conf /etc/dnsmasq.d/
  • 配置在WAN口启动后(/jffs/scripts/wan-start),将/etc/dnsmasq.d/中的指定域名DNS解析地址调整为WAN口上游分配的DNS IP地址
#!/bin/sh

/jffs/dnsmasq/dnsmasq-update-china-list `cat /tmp/resolv.conf |awk '{print $2}'`
  • 在/jffs/configs/目录中创建dnsmasq.conf.add文件,对默认dnsmasq启用conf-dir,并将默认DNS查询地址修改为不受干扰的DNS解析服务器
admin@RT-AC87U:/jffs/configs# cat dnsmasq.conf.add
no-resolv
server=YOUR_TRUSTY_DNS_IP#PORT
conf-dir=/etc/dnsmasq.d

至此,大功告成,Asus Merlin AC-87U+RaspberryPi+Shadowsocks的组合实现了自动识别被干扰IP+策略路由+透明代理+策略DNS解析,再次驰骋在广阔无垠的Internet上。