背景

前一段时间,在ESXI上部署了一台Plex服务器。解决了一些恼人的问题后,用得还算满意,但是TMDb搜刮器在匹配电影信息时,总会丢失一些Poster海报信息。
最终发现,The Movie Database (TMDb) 这个地址被和谐社会了。

由于Plex不支持设置代理,且手动设置HTTP_PROXY后还是无效,所以决定把Plex这台服务器加入透明代理网络中。

参考以前的文章《Openwrt上使用dnsmasq和ipset实现域名分流》,将网关、DNS设置为192.168.0.254

因为之前重新部署了ESXI环境,所以Openwrt这台虚拟机不幸丢失,所以只能大侠请重新来过
由于之前的方案在实际使用中还有一些不足,在综合了网络上现有的一些方案后,设计出一份改进版的方案。

网络上原始的方案

安装软件

  • shadowsocks-libev和luci-app-shadowsocks(提供ss-rules)
    • 依赖iptables-mod-tproxy和ip
  • chinadns和luci-app-chinadns

配置

  • 使用luci-app配置shadowsocks
    • 开启端口转发,设置本地端口为5300,用于DNS查询
    • 被忽略IP列表选择ChinaDNS路由表(/etc/chinadns_chnroute.txt
  • 使用luci-app配置ChinaDNS
    • 设置本地端口为5353
    • 设置上游服务器114.114.114.114,127.0.0.1:5300
  • 使用luci-app配置DHCP/DNS
    • 设置DNS转发127.0.0.1#5353
    • 勾选忽略解析文件

这样一来,你拥有了:

  • 一个无污染的DNS服务器,端口为53
    • 国内DNS解析出国内IP,国外DNS解析出国外IP
  • 一个透明代理的网关
    • 如果请求IP在ChinaDNS路由表中,则走正常流量
    • 如果请求IP不在ChinaDNS路由表中,则走代理流量

改进版方案

尽管上面的方案已经能够实现透明代理,但是还有以下问题(附上解决方案):

  • 在某些ISP下使用ss-tunnel(即端口转发)不稳定, 导致DNS查询时间过长或超时
    • 解决方案:
      • 使用dnscrypt-proxy提供的DNS查询服务
  • ChinaDNS有时会出现误判,导致访问国内站点时走代理
    • 解决方案:
      • 在dnsmasq中配置chinalist,指定国内域名使用国内DNS进行解析
      • 不在列表中的站点交由ChinaDNS判断
  • 只能定义强制走代理的IP,而不能是域名,维护起来比较麻烦
    • 解决方案:
      • 归功于ss-rules的实现,ipset集合ss_spec_dst_fw中存放强制走代理的IP
      • 在dnsmasq中配置my-list(自定义域名)列表,将my-list的查询结果保存到ss_spec_dst_fw

额外安装以下软件:

  • 安装dnsmasq-full
    • 需要先卸载自带的dnsmasq
  • 安装dnscrypt-proxy
    • 提供安全不受污染的DNS服务器

更新配置

  • 使用luci-app配置shadowsocks
    • 关闭端口转发
  • 配置dnscrypt-proxy
    • 修改监听端口为127.0.0.1:5300
  • 配置dnsmasq
    修改/etc/dnsmasq.conf,在文末加入conf-dir=/etc/dnsmasq.d:
    echo 'conf-dir=/etc/dnsmasq.d' >> /etc/dnsmasq.conf
    

定期维护

以下脚本实现更新china-route、china-dns和my-list的功能:

#!/bin/sh

DNS=127.0.0.1#5300
IPSET=ss_spec_dst_fw

# update china route
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))  }' > china-route.txt

# update china dns
wget --no-check-certificate -O china-route.txt https://raw.githubusercontent.com/felixonmars/dnsmasq-china-list/master/accelerated-domains.china.conf

# update my list
cat my-list.txt | awk '{ printf("server=/"$1"/'$DNS'\nipset=/"$1"/'$IPSET'\n")  }' # > my-list.conf

# copy updated files
cp china-list.conf /etc/dnsmasq.d/china-list.conf
cp my-list.conf /etc/dnsmasq.d/my-list.conf
cp china-route.txt /etc/chinadns_chnroute.txt

# restart service
/etc/init.d/dnsmasq restart
/etc/init.d/chinadns restart
/etc/init.d/shadowsocks rules

echo 'updated success'

其中,my-list.txt中保存需要强制走代理的域名列表

ipip.net
mydomain.com

你需要定期执行这个脚本来对相关列表进行更新。

总结

使用该方案的前提:

  • 设置PC的网关为openwrt的ip(如:192.168.0.254)
  • 设置PC的DNS为openwrt的ip(如:192.168.0.254)
  • 当PC端访问国内网站时
    • dnsmasq解析该域名,发现该域名在dnsmasq的china-list中,使用国内DNS进行解析,返回国内IP
    • iptables检测到该IP在ss_spec_dst_bp(即china-route)中,则直连访问
  • 访问google.com
    • dnsmasq解析该域名,发现该域名不在dnsmasq的列表中,则交给ChinaDNS查询
    • ChinaDNS解析出IP后,发现该IP不匹配china-route列表,则返回国外IP
    • iptables检测到该IP不在ss_spec_dst_bp中,则代理访问
  • 访问mydomain.com
    • dnsmasq解析该域名,发现该域名在dnsmasq的my-list中,使用dnscrypt进行解析,返回IP,并将该IP加至ss_spec_dst_fw
    • iptables检测到该IP在ss_spec_dst_fw中,则代理访问。

完:)