看到 ArchWiki 上 GoAgent 条目的亚全局代理方案,只是设置了代理相关环境变量。我就想,为什么不实现一个真正的全局 HTTP 代理呢?
最终,答案是:Linux 太灵活了,以至于想写一个脚本来搞定很麻烦。不过方案如下,有兴趣的可以折腾折腾。
首先,需要用到的工具:dnsmasq、iptables、redsocks,以及 HTTP 代理工具。dnsmasq 是用来缓存 DNS 请求的,iptables 把 TCP 流转接到 redsocks,而 redsocks 将 TCP 流转接到代理上。
最小 dnsmasq 配置如下:
listen-address=127.0.0.1 cache-size=500 server=127.0.0.1#5353 bogus-nxdomain=127.0.0.1
这里使用了本地的 dnscrypt 服务(假设其在 5353 端口上提供服务)。也可以使用国外服务器,只是需要更细致的配置来迫使其走 TCP。
iptables 命令如下:
# 创建一个叫 REDSOCKS 的链,查看和删除的时候方便 iptables -t nat -N REDSOCKS # 所有输出的数据都使用此链 iptables -t nat -A OUTPUT -j REDSOCKS # 代理自己不要再被重定向,按自己的需求调整/添加。一定不要弄错,否则会造成死循环的 iptables -t nat -I REDSOCKS -m owner --uid-owner redsocks -j RETURN iptables -t nat -I REDSOCKS -m owner --uid-owner goagent -j RETURN iptables -t nat -I REDSOCKS -m owner --uid-owner dnscrypt -j RETURN # 局域网不要代理 iptables -t nat -A REDSOCKS -d 0.0.0.0/8 -j RETURN iptables -t nat -A REDSOCKS -d 10.0.0.0/8 -j RETURN iptables -t nat -A REDSOCKS -d 169.254.0.0/16 -j RETURN iptables -t nat -A REDSOCKS -d 172.16.0.0/12 -j RETURN iptables -t nat -A REDSOCKS -d 192.168.0.0/16 -j RETURN iptables -t nat -A REDSOCKS -d 224.0.0.0/4 -j RETURN iptables -t nat -A REDSOCKS -d 240.0.0.0/4 -j RETURN # HTTP 和 HTTPS 转到 redsocks iptables -t nat -A REDSOCKS -p tcp --dport 80 -j REDIRECT --to-ports $HTTP_PORT iptables -t nat -A REDSOCKS -p tcp --dport 443 -j REDIRECT --to-ports $HTTPS_PORT # 如果使用国外代理的话,走 UDP 的 DNS 请求转到 redsocks,redsocks 会让其使用 TCP 重试 iptables -t nat -A REDSOCKS -p udp --dport 53 -j REDIRECT --to-ports $DNS_PORT # 如果走 TCP 的 DNS 请求也需要代理的话,使用下边这句。一般不需要 iptables -t nat -A REDSOCKS -p tcp --dport 53 -j REDIRECT --to-ports $HTTPS_PORT
redsocks 的配置:
base { log_debug = off; log_info = off; daemon = on; redirector = iptables; } // 处理 HTTP 请求 redsocks { local_ip = 127.0.0.1; local_port = $HTTP_PORT; ip = $HTTP_PROXY_IP; port = $HTTP_PROXY_PORT; type = http-relay; } // 处理 HTTPS 请求,需要一个支持 HTTP CONNECT 的代理服务器,或者 socks 代理服务器 redsocks { local_ip = 127.0.0.1; local_port = $HTTPS_PORT; ip = $SSL_PROXY_IP; port = $SSL_PROXY_PORT; type = http-connect; // or socks4, socks5 } // 回应 UDP DNS 请求,告诉其需要使用 TCP 协议重试 dnstc { local_ip = 127.0.0.1; local_port = $DNS_PORT; }
然后以相应的用户和配置文件启动 dnsmasq 以及 redsocks。修改/etc/resolv.conf
:
nameserver 127.0.0.1
至于分流的事情,HTTP 部分可以交给 privoxy,但是 HTTPS 部分不好办。可以再设立一个像 GoAgent 那样的中间人型 HTTPS 代理,或者更简单地,直接根据 IP 地址,国内的直接RETURN
掉。
以上就是整个方案了。有些麻烦而我又不需要所以没测试。反正就是这个意思。Android 软件 GAEProxy 就是这么干的(不过它没使用 iptables 的 owner 模块,导致我不小心弄出了死循环)。另外,BSD 系统也可以使用类似的方案。