• 热门专题

在OpenWRT路由器上配置基于 ShadowSocks 的透明代理(简易版)

作者:F32  发布日期:2014-12-19 19:37:36
Tag标签:路由器  简易  
  • shadowsocks 是一款开源的 Socks 代理程序,和其它方案相比,一个明显额优势是平台支持相当广泛:

    今天我们就使用 shadowsocks 在路由器上搭建代理。通过本篇日志的介绍,你可以了解:

    如何在 OpenWRT 路由器上实现透明代理,手机无需配置即可访问 Google Play 如何在路由器上配置 Socks 代理(这样客户端就可以统一连接到路由器的 Socks 服务器上,不用分别设置了) DNS 污染的原理是什么,chinadns 是如何解决这个问题的

    考虑到路由器性能、连接稳定性,还有配置更新的便利性,我们仅仅将少量的 Google IP 加入到透明代理中,其它的网站还是交给 AutoProxy 这样的插件来完成,目前支持的服务包括(但不限于):

    Google Search Google Maps Google Code Google Play Gmail

    好了,不废话了,下面开始介绍如何进行配置。

    硬件平台


    我使用的路由器是 WNDR3700v2,参数如下:

    CPU: Atheros AR7161 @ 680 MHz (ar71xx) 无线: Atheros AR9220 + AR9223 (802.11 a/b/g/n, 300 Mbps + 300 Mbps) 有线: RTL8366S 1000 Mbps 可编程交换机芯片 内存: 64 MB 闪存: 16 MB USB: 2.0 x 1

    这个型号目前淘宝上的价格只有¥150左右,还是相当划算的。除了性能强劲外,这个型号还支持 Serial 和 JTAG 调试,实在是不可多得的好机器。CeroWRT 项目的实验平台使用的就是这个 WNDR3700v2。

    软件平台


    我使用的是 OpenWRT 12.09 (Attitude Adjustment)。Backfire 10.03.1 自带的 openssl 库似乎和 shadowsocks-libev 不兼容,所以可能需要手动编译并且解决相关的问题。另外 OpenWRT 的最新版本 14.07 (Barrier Breaker) 已经在 2014 年 7 月 31 日发布了,但是我暂时还没有在新的系统上测试。

    准备工作


    OpenWRT 的官方源在国内连接不太稳定,所以运行 opkg 命令的时候很可能会卡住不动。为了避免这个问题,可以先用迅雷把相关的库文件先下好。下载的地址是 http://downloads.openwrt.org/attitude_adjustment/12.09/,用浏览器打开这个网址,然后根据路由器型号选择对应的平台(比如说我的是 ar71xx)然后进入generic/packages 目录,下载以下几个文件:

    kmod-ipt-nat-extra_3.3.8-1_ar71xx.ipk iptables-mod-nat-extra_1.4.10-4_ar71xx.ipk zlib_1.2.7-1_ar71xx.ipk libopenssl_1.0.1h-1_ar71xx.ipk

    然后我们需要从 SourceForge 上下载 shadowsocks-libev 和 chinadns-c,对于 ar71xx 平台,下载以下两个文件:

    shadowsocks-libev_1.6.1-1_ar71xx.ipk ChinaDNS-C_1.1.8-1_ar71xx.ipk

    然后我们需要能够把这些文件传到 OpenWRT 路由器上,在 Windows 下,可以使用 putty 软件包中提供的 pscp 工具,下载地址是 http://www.putty.org/。

    接下来需要准备一个可用的 shadowsocks 账号。没有远程主机的帮助显然是无法继续的,在网上你可以找到很多免费的和收费的代理服务,甚至你也可以搭建自己的 shadowsocks 服务器。如果你已经成功找到了 shadowsocks 代理服务,把密码服务器端口号,还有加密方式这三项记下来,另外,使用 ping 工具得到服务器的 IP 地址并且记下来,后面会用到。

    最后,把 ISP 提供给你的 DNS 服务器地址记下来。chinadns 在运行的时候也需要一个国内的(已经被污染了的)DNS,至于其中的原理稍后会介绍。

    开始配置


    在上面的准备工作都完成之后,就可以正式开始配置了。首先准备一份刚装好的干净的 OpenWRT 路由器(用过了也没关系,一般情况下不会有冲突),把 6 个软件包复制到 pscp 的目录下,打开命令行,输入以下命令:

    pscp -scp kmod-ipt-nat-extra_3.3.8-1_ar71xx.ipk root@192.168.1.1:/tmp
    pscp -scp iptables-mod-nat-extra_1.4.10-4_ar71xx.ipk root@192.168.1.1:/tmp
    pscp -scp zlib_1.2.7-1_ar71xx.ipk root@192.168.1.1:/tmp
    pscp -scp libopenssl_1.0.1h-1_ar71xx.ipk root@192.168.1.1:/tmp
    pscp -scp shadowsocks-libev_1.6.1-1_ar71xx.ipk root@192.168.1.1:/tmp
    pscp -scp ChinaDNS-C_1.1.8-1_ar71xx.ipk root@192.168.1.1:/tmp

    完成之后,软件包就都上传到路由器的 /tmp 目录下了。然后使用 putty 打开路由器地址 192.168.1.1,运行以下命令:

    cd /tmp
    opkg install kmod-ipt-nat-extra_3.3.8-1_ar71xx.ipk
    opkg install iptables-mod-nat-extra_1.4.10-4_ar71xx.ipk
    opkg install zlib_1.2.7-1_ar71xx.ipk
    opkg install libopenssl_1.0.1h-1_ar71xx.ipk
    opkg install shadowsocks-libev_1.6.1-1_ar71xx.ipk
    opkg install ChinaDNS-C_1.1.8-1_ar71xx.ipk

    建议严格按照顺序执行上面的命令,以避免软件包依赖问题。然后执行下面的命令编辑防火墙规则(也可以在 LuCI Web 界面的 Network – Firewall – Custom Rules 下设置):

    vi /etc/firewall.user

    正常情况下,里面应该只有 3 行以 # 开头的注释。在注释下面加入以下内容

    iptables -t nat -N SHADOWSOCKS
    iptables -t nat -F SHADOWSOCKS
    
    # server
    iptables -t nat -A SHADOWSOCKS -d x.x.x.x -j RETURN
    
    # reserved networks
    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 172.16.0.0/12 -j RETURN
    iptables -t nat -A SHADOWSOCKS -d 192.168.0.0/16 -j RETURN
    
    # google
    iptables -t nat -A SHADOWSOCKS -p tcp -d 74.125.0.0/16 -j REDIRECT --to-ports 8001
    iptables -t nat -A SHADOWSOCKS -p tcp -d 173.194.0.0/16 -j REDIRECT --to-ports 8001
    
    # apply the rules
    iptables -t nat -I PREROUTING -j SHADOWSOCKS
    
    # start china-dns
    chinadns -p 5353 -s y.y.y.y,8.8.4.4 -l /etc/chinadns_iplist.txt  -c /etc/chinadns_chnroute.txt &
    
    # start shadowsocks-local
    ss-local -s server -p port -k password -m aes-256-cfb -l 8000 -u &
    
    # start shadowsocks-redir
    ss-redir -s server -p port -k password -m aes-256-cfb -l 8001 -u &

    这个脚本里有几个需要修改的地方:

    第 5 行的 x.x.x.x 需要修改为 shadowsocks 服务器的 IP 地址 第 21 行的 y.y.y.y 需要修改为原来的(国内的) DNS 服务器的 IP 地址 第 24 和 27 行的 server 需要修改为 shadowsocks 服务器的域名(IP 地址也行),port 需要修改为服务器端口号,password 修改为你的密码,加密方式这里填的是 AES-256-CFB,但是你的服务可能用的并不是这个加密方式,所以需要根据需要进行修改。

    这个脚本完成之后,编辑 dnsmasq 的配置文件:

    vi /etc/dnsmasq.conf

    在文件的最后加入下面两行(注意这里用的是 # 而不是冒号):

    no-resolv
    server=127.0.0.1#5353

    最后,保存文件,然后重启路由器:

    reboot

    这样路由器的配置就完成了,但是 Windows 可能还在使用老的 DNS 数据库。运行下面的命令刷新 DNS 缓存:

    ipconfig /flushdns

    如果没有什么差错的话,接下来就能够访问 Google 搜索、Google Code 和 Gmail 了,手机连上 WIFI 后能够直接访问 Google Play!

    原理解析


    相信看到这里的读者一定还有许多疑问:为什么要用 chinadns,为什么还需要修改 dnsmasq 的配置文件,ss-local 和 ss-redir 分别是干什么的等等。下面对这些问题统一做个解答。

    为了屏蔽 Google 的服务, The Great Wall 做了很多的事情,其中最重要的两项是 DNS 投毒和 TCP 连接重置。

    DNS 协议在设计之初就没有考虑安全性,因此当一台主机发送了一个 DNS 请求“请问 www.google.com 的 IP 地址是多少”后,防火墙可以立刻发送一个错误的 IP 地址到源主机。源主机在收到防火墙给的应答后,提取出错误的 IP 地址,于是就忽略了后面传回来的正确的报文。由于 DNS 协议已经使用几十年了,今天要在协议上做更新相当麻烦(所有的服务器、终端都要统一更新),所以从协议上解决这个问题是不太现实的。

    但是有趣的是,防火墙给出的错误的地址依然都是国外的地址(www.google.com 本身位于国外,而防火墙返回的是另一个错误的国外地址)。或许这其中的目的是不给中国的服务器带来麻烦,想想公司 A 本身网站带宽就不大,突然有一天发来大量奇怪的连接请求,服务器都快吃不消了那如何是好。然而正是这个行为使得绕过防火墙成为可能。chinadns 本身是一个 DNS 转发服务器,它工作时需要一个本地的 DNS 和一个国外的 DNS(上面的脚本中使用的是 Google 的 DNS 服务器 8.8.4.4),工作原理如下:

    void recv_request_from_client(request r)
    {
        send_to_chinese_dns(r);
        send_to_foreign_dns(r);
    }
    
    void recv_response_from_dns(response r)
    {
        if (in_fake_ip_list(r.target_ip)) {
            drop(r);
            return;
        }
        if (!is_chinese(r.target_ip)) {
            if (is_chinese(r.dns_ip)) {
                drop(r);
                return;
            }
        }
        forward_to_client(r);
    }

    每当收到一个请求时, chinadns 同时向本地的 DNS 和国外的 DNS 发送请求。当收到应答时,如果目标 IP 在虚假 IP 地址列表中,直接丢弃这个报文不转发;如果目标 IP 是一个国外的 IP,并且这个结果是从国内的 DNS 得到的,那么结果很有可能也被污染了,因此将报文丢弃,等待国外 DNS 的解析结果。

    通过设置上面的策略,已经可以解决很多 DNS 解析问题了。但是对于部分网站(比如 Google 搜索),防火墙还会发送 TCP Reset 包,当浏览器收到 Reset 包后 TCP 连接直接就中断了,于是浏览器就会给出一个错误消息。要解决这个问题,没有特别好的办法,只能使用代理。我们在 OpenWRT 路由器上建立了 shadowsocks 代理,并且让其工作在转发(redir)模式。当 Linux 防火墙(iptables)收到特定目的地的 IP 报文时,就使用代理进行转发(上面脚本的 14 和 15 行,端口 8001)。

    同时我们还以 local 模式建立了一个本地的 Socks 服务器,这样 PC 上就无需运行 shadowsocks 客户端了,只需要安装对应的插件(比如 Firefox 下使用修改版的 AutoProxy),并且将 Socks 服务器地址设置为 192.168.1.1:8000 即可。

    上面的脚本中,仅仅使用 ss-redir 对 Google 的两个 B 类地址进行了透明代理,其它的都需要手动设置。目前可以直接使用的服务有:

    Google Search Google Maps Google Code Google Play Gmail

    Q&A


    1. 为什么上面的脚本只加了 Google 的地址,不提供一份更完整的地址列表?

    AutoProxy 和其它类似的软件维护了一个大得多的列表,但是考虑到路由器性能稳定性,暂时没有将完整的列表加到防火墙规则中,如果你有需要的话可以手动添加自己的 IP 地址。在浏览器上(如 Firefox + AutoProxy)只需要轻轻一点就能够进入全局代理模式,在访问结束可以取消,非常方便。但是如果要在路由器上更新规则就比较麻烦了,这也是只在路由器上配置少量规则,大部分交由插件处理的一个原因。

    2. 为什么要运行 ss-redir 和 ss-local 两个后台进程,不能合并吗?

    关于这个问题的内部原理我暂时还不太清楚,目前可以知道的是,ss-redir 需要配合 iptables 防火墙的 REDIRECT 规则使用,而 ss-local 是作为 Socks 服务器,配合相关软件设置使用。ss-redir 的作用是透明代理,ss-local 是普通的 Socks 代理。当把 Firefox 的代理端口改成 ss-redir 的 8001 是无法工作的。如果你有兴趣的话,可以研究研究两者工作的内在机制,然后找到准确的原因。

    3. PC 端有类似于 AutoProxy 这样的插件还算方便,手机端设置似乎比较麻烦,有更好的办法吗?

    如果你的路由器的无线芯片支持的话,可以设置双 SSID,一个工作在普通模式,另一个工作在全局代理模式。当然防火墙规则也需要做相应的修改。如果路由器不支持,也可以给树莓派接上 USB 无线网卡,配置上 shadowsocks 级联在路由器上。这样当手机需要访问特定网站时,切换到全局代理的那个 SSID 即可。

    4. 为什么不让所有的 DNS 报文通过代理服务器发送?

    如果你的网络条件非常好的话,确实可以这么做。但是当代理服务器的 ping 值在 50 - 100 ms 之间的时候,这么做会导致浏览网页的时候产生明显的延时(举例来说,当访问京东的时候,实际上涉及到的相关域名除了 jd.com 还有差不多十来个,这些时延累计起来就非常明显了)。所以这里我们用 chinadns 来处理这个问题,这样在访问国内站点时速度会有很大的提升。

    5. 为什么脚本里面有代理服务器的 IP 地址,要是 IP 变了怎么办?

    脚本的第 5 行告诉 OpenWRT,当访问代理服务器时,直接发送即可,这样就可以避免死循环了。之所以这里用的是 IP 地址,主要原因是 iptables 并不支持域名(如果一定要用一些脚本动态查询域名然后内嵌到命令里也不是不行,但是这样给人感觉不太好)。即使代理服务器的 IP 变了,也不会产生致命的问题,只要新的 IP 没有落在那两个 B 类地址里就行了,默认规则是不进行转发的。

About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规