Total Pageviews

Saturday 17 December 2016

用树莓派做翻墙网关

在网上搜了很多文章来看,很多都是把树莓派当WIFI AP来用的,但我这里不是。我的树莓派只有1个自带的eth网卡,我把它做成一个网关,让所有的设备在网络设置的网关一项都填树莓派的IP,基本实现其他设备无配置无缝自动翻墙。
其实过程非常简单。
首先,当然是要有能用的代理,无论是http proxy还是socks proxy,甚至只是一个shadowsocks server也行,现有的工具都能适配使用。
然后,用redsocks或ss-redir在树莓派上开端口,一定要监听在0.0.0.0上,我之前就是开在127.0.0.1上,然后纠结了大半天,一直是树莓派自己能翻过去,其他把它当网关的机器翻不出去。redsocks目前我看到有两个不同版本,一个原版,从2012年以来就很少更新了,可以将本地tcp流量封装到http connect/http relay/socks协议里,配合iptables等机制实现系统全局代理,另一个是国人修改版, 在原版的基础上又增加了一些功能,比如后端协议还支持了shadowsocks(就跟ss-redir一样)和直连,以及auto proxy自动检测是否需要代理,还有自称修正了一些原版中的bug等等。但我昨天试用下来,发现这新增的功能很不稳定,基本经不起日常使用。我最后就直 接用了ss-redir,这是shadowsocks-libev中的一个工具,功能与redsocks类似,但它只提供了shadowsocks的协议作为后端,不过胜在稳定,又不用多走一次socks5代理,所以估计效率也会好一点。
接着,把树莓派设置流量转发。打开/etc/sysctl.conf,设置net.ipv4.ip_forward=1,让更新实时生效: sysctl -p /etc/sysctl.conf。这样树莓派就已经可以做为网关使用了。还要把流量转发到ss-redir开的端口(我用58100)上去,Linux 上用iptables:
iptables -F
iptables -X
iptables -Z

iptables -P INPUT DROP
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT

iptables -A INPUT -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A INPUT -p icmp -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A INPUT -p tcp --dport 58100 -m state --state NEW,ESTABLISHED -j ACCEPT

# dnat
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j MASQUERADE
iptables -t nat -N SS
# 过滤私有地址,vps地址
iptables -t nat -A SS -d 127.0.0.1 -j RETURN
iptables -t nat -A SS -d 10.0.0.0/8 -j RETURN
iptables -t nat -A SS -d 172.16.0.0/21 -j RETURN
iptables -t nat -A SS -d 192.168.0.0/16 -j RETURN
iptables -t nat -A SS -d shadowsocks-server-ip -j RETURN

# 过滤中国ip地址,网上搜一下“中国ip段"
iptables -t nat -A SS -d 58.14.0.0/15 -j RETURN
iptables -t nat -A SS -d 58.16.0.0/13 -j RETURN
iptables -t nat -A SS -d 58.24.0.0/15 -j RETURN

# 所有其他的ip,都提交给shadowsocks
iptables -t nat -A SS -p tcp -j REDIRECT --to-port 58100

# 使用SS链,其他设备走PREROUTING链
iptables -t nat -A PREROUTING -p tcp -j SS
# 使用SS链,树莓派自己走OUTPUT链
iptables -t nat -A OUTPUT -p tcp -j SS

中间有一段中国IP地址的,有个叫chnroute的项目,简单的做法就只有一条命令:
curl http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest | grep 'apnic|CN|ipv4' | awk -F\| '{ printf("iptables -t nat -A SS -d %s/%d -j RETURN\n", $4, 32-log($5)/log(2)) }' > cn_rules.conf
,再把文件cn_rules.conf的内容执行一下。
最后,把家里所有要翻墙的设备都在网关一项里填成树莓派的IP。比较方便的做法是,在DHCP那里就直接返回树莓派的IP当网关。比较郁闷的是我的 Netgear R6300v2貌似没找到怎么在DHCP里设置自定义的网关地址。当然树莓派一定要用静态设置上级路由器的IP为网关。这样所有连入的设备就自动能翻墙 了。
上面还有一点没提到的是DNS污染的问题,现在也有很多方案了,我的做法是在树莓派上建了个dnsmasq,上游走OpenDNS的5353端口,国内常用域名有个列表,走114DNS.
---------

在树莓派上搭建全局透明代理网关


有的程序本身不支持设置代理,所以需要在网关上动手。正好一直使用树莓派当路由器,所以尝试把树莓派打造成一个带有智能转发功能的透明网关,即国内的 IP 直连,海外的 IP 走代理加速。

配置无线网络

本文关注点不在此,所以一笔带过。

可以通过一键安装脚本 pi-setup-wifi.sh 来安装和配置树莓派的无线。该脚本会创建一个名为SSID,密码为PASSWORD的无线网络,使用的网段为 192.168.68/24

启动 DoH 服务来避免 DNS 污染

目前 GFW 大概有这么几个级别的干扰:

  1. 随机丢包、限速
  2. 域名阻断、IP阻断
  3. DNS 干扰

一般的网站都基本上处于1或2,一般来说只要 TCP/UDP 流量不直连就行,但是 Google 家的产品比较强,直接上了最高规格的屏蔽,即 DNS 解析污染。

出于性能和速度的考虑, DNS 协议使用了 UDP 这种不可靠的协议,这使得 DNS 解析污染出现,即被中间路由截获并返回了虚假的 IP 地址,因此我们需要建立一个可靠的 DNS 解析服务。

目前已经有很多解决方案,如基于 TCP 的 DNS 解析,但是由于 TCP 流量仍然能够被嗅探,所以出现了 DoH (DNS over Https),这样只要服务器没问题,返回的结果就一定是正确的。

本文权衡各工具,最后选择了 Cloudflare 家的 DoH服务

首先到 下载页 下载 Binary: ARMv6这一栏的压缩包,然后解压。
(被屏蔽了,需要用代理下载)

1
tar -xzvf cloudflared-stable-linux-amd64.tgz

然后把 cloudflared 移动到 /usr/local 目录下,并启动。

1
/usr/local/cloudflared proxy-dns --address 0.0.0.0 --port 15353

只监听本地的话wifi客户端会有问题
5353 端口被 avahi 占用了,所以使用15353

你可以在 /etc/rc.local 文件中配置开机启动。

1
nohup /usr/local/cloudflared proxy-dns --port 15353 &

不同网络层之间协议转换

安装 redsocks 来把无线网卡的流量转发到 socks5 代理,这本质上是转换两个不同网络层的流量。

1
sudo apt install -y redsocks

编辑配置文件 /etc/redsocks.conf

1
2
3
4
5
6
redsocks {
local_ip = 0.0.0.0;
local_port = 12345;
ip = 127.0.0.1;
port = 1080;
}

这里的ip必须改成0.0.0.0,实测如果保留127.0.0.1会让wifi客户端无法上网

这里假设已经有一个开放在 1080 端口的 socks5 服务,可以使用 Shadowsocks 来创建一个。

重启 redsocks 以使配置生效

1
sudo service redsocks restart

忽略国内地址

安装 ipset 来生成国内地址列表

1
2
3
4
5
6
7
8
9
10
11
12
13
sudo apt install -y ipset

# 下载分配给国内运营商的 IP 段
curl 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest' | grep ipv4 | \
grep CN | awk -F\| '{ printf("%s/%d\n", $4, 32-log($5)/log(2)) }' > chnroute.txt

# 创建一个集合并把上述IP段加进去
sudo ipset create chnroute hash:net

cat chnroute.txt | sudo xargs -I ip ipset add chnroute ip

# 上一条命令执行的非常慢,所以把结果保存下来,下次直接从文件恢复
sudo bash -c "ipset save chnroute > /etc/chnroute.ipset"

在系统重启后,使用如下命令来恢复ipset

1
sudo ipset restore < /etc/chnroute.ipset

使用 iptables 来转发流量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
sudo iptables -t nat -N SHADOWSOCKS

# 注意这里要把 $server_IP 改成你自己的 socks5 远程IP,即你的VPS IP,不然无法正常工作
sudo iptables -t nat -A SHADOWSOCKS -d $server_IP -j RETURN

# 忽略局域网地址
sudo iptables -t nat -A SHADOWSOCKS -d 0.0.0.0/8 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 10.0.0.0/8 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 127.0.0.0/8 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 169.254.0.0/16 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 172.16.0.0/12 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 192.168.0.0/16 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 224.0.0.0/4 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 240.0.0.0/4 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -m set --match-set chnroute dst -j RETURN

# 把流量转发到 12345 端口,即redsocks
sudo iptables -t nat -A SHADOWSOCKS -p tcp -j REDIRECT --to-ports 12345
sudo iptables -t nat -A OUTPUT -p tcp -j SHADOWSOCKS
sudo iptables -t nat -A PREROUTING -p tcp -j SHADOWSOCKS

覆盖设备自身的 DNS 服务器

因为 Google Home 内置了 DNS 服务器,而不是网关分配的,所以需要把流量截获然后转发到可靠的 DNS 解析。

1
2
sudo iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 15353
sudo iptables -t nat -A PREROUTING -p tcp --dport 53 -j REDIRECT --to-ports 15353

至此,搭建完毕,将 Google Home 连接到 wifi 上,就可以开始欢快的使用各种服务啦^_^。

Hey, Google, nice to meet you ~

参考链接及资源

Running a DNS over HTTPS Client

求推荐靠谱的防 dns 污染方案

请教如何修改让dns强制重定向到路由

利用shadowsocks打造局域网翻墙透明网关

用树莓派打造无线中继科学上网路由器

ss-redir 透明代理

Dnsmasq 介绍与使用

利用Dnsmasq部署DNS服务

DNSmasq 安装&配置详解

dnsforwarder

Tcp-DNS-proxy

dns-over-https

https_dns_proxy

Pcap_DNSProxy