wd and cc

— Happy every day

Transparent Proxys

Posted at — Jun 29, 2020

这次说说透明代理。所谓透明代理指的是用户无感知的情况下(也就是不需要对自己的电脑手机做任何设置,运行任何代理软件),就可以直接访问被屏蔽的网站。一般都是通过在路由器上面做设置来实现。

翻墙这个事情最重要的就是解决两个问题

  1. 解决 DNS 污染

  2. 通过 tunnel 访问到被屏蔽的 ip 地址

DNS 污染

解决 DNS 污染,肯定首先需要有一个无污染的 dns,一般来说,国内的 dns 都是被污染的,估计也没人敢放出来无污染的给大家用。国外的 dns 里面,对于使用 dns udp 协议的,在经过出国网关的时候一般都会被污染。对于 dns tcp 协议的估计会直接连不上。对于其他的比较著名的 DoT DoH 的 dns 很多也连不上了。所以怎么能有一个无污染的 dns 呢?

自建 DNS

自己有 vps 的话,可以自己搭一个自用,但是当然不能用 udp 协议之类的,一来容易被用来当作肉鸡,而来在出国网关哪里也还是会被污染。要搭就搭一个支持 DoT 或者 DoH 的服务。可以使用 dns-over-https 来搭建一个 DoH 服务。通过 nginx 或者 caddy 之类包装一下就可以提供服务了。

通过代理访问被屏蔽的无污染 DNS

上面不是提到么,国外 dns 是无污染的,那可以通过代理提供的 tunnel 访问这些 dns 来得到无污染的结果。例如 ss-tunnel 就是可以用来做这个事情的。v2ray 的 dns-in 也可以做这个事情。

广播 dns 给客户端

有了无污染 dns 还需要广播给客户端使用才行。一般客户端连接到路由器的时候,路由器会 push 自己的 ip 地址做 dns。路由器上其实一般是会跑一个 dnsmasq 来提供 dns 和 dhcp 服务的。dnsmasq 默认使用的是通过 ppoe 或者其他方式获取的上游 dns 作为自己的上游,这样获取到的 dns 显然是被污染的。那么就需要配置 dnsmasq 使用我们刚才得到的无污染的 dns 来做上游。对于支持 udp 协议的 dns 可以直接给 dnsmasq 使用,对于 DoT DoH 的 dns,dnsmasq 不支持,还需要转换一下,上面提到的那个 dns-over-https 不止提供了 server ,也提供了 client,可以用那个 client 把 DoH 的 dns 转换成一个 udp 协议的 dns。

还存在的问题

实际上,一个软件是可以自己决定是不是要使用系统的 dns 的。国内有时候会有一些地方的 isp 有 dns 劫持,有些软件为了避免这个问题,就不会使用系统的 dns 而使用自己的 dns。这样这些 dns 实际还是被污染的。当然这是比较少数的情况。还有一种情况是,类似一些 Android TV 盒子,它压根就不使用 dhcp push 的 dns,就是坚持用自己的 dns,这样的 dns 还是系统级别的,这样会导致这个系统上面跑的 app 都得到污染的结果,这种一般都是国外的设备居多,因为人家就压根不需要考虑污染的问题。

如果遇到这些情况,会发现即使实现了透明代理,部分软件或者功能还是不能用,因为他们访问的依然是污染后的 ip。例如我的 Mi Box 国际版里面的 netflix 就用到了自己的 dns,即使我配置好了无污染的 dns 他也不会用。

对于这样的情况,还有一个办法是找到他们使用的 dns ,然后通过 iptables 强制让这些请求也走代理。dns udp/tcp 比较好搞,因为一般都是 53 端口,抓包就可以,但是 DoT DoH 就麻烦一点了。

支持 redir 方式转发的代理

这个很多软件支持,比较容易找,比如 ss, v2ray, clash 都支持。

把 DNS 解析结果和代理软件结合起来

理想情况下,我们肯定只是希望被屏蔽的 ip 才通过代理访问,其他 ip 都直接连接最好了。通过无污染 dns 得到正确的 ip 之后,如何能让这些 ip 通过代理走,而其他 ip 不通过代理走呢?

dnsmasq + ipset

一般我们路由器上面用的都是 dnsmasq,而 dnsmasq 可以针对指定的域名,使用指定的 dns 来解析,例如

1
2
server=/.google.com/127.0.0.1#5380
ipset=/.google.com/free

这个设置可以让 .google.com 都通过 127.0.0.1 的端口是 5380 的 dns 来解析。然后同时把解析的结果放到 ipset 的名叫 free 的 list 里面。通过类似的方式可以把所有被屏蔽的域名做这样的设置,那么就可以动态的得到一个被屏蔽的 ip 的列表了。然后通过 ipables 的 -m set --match-set free 这样的匹配方式,把目标 ip 匹配到这个列表里面的请求转发给代理软件。

很多基于 gfwlist 来设置的翻墙实际就是用的这个模式。这个模式的好处是,只会处理被屏蔽的域名对应的 ip,其他完全不会处理,这样也不会有比如把对国内 ip 的访问转发给代理的情况。但是这个也有一个弊端,如果一个服务是基于 ip 访问的,并且这个 ip 已经被屏蔽,那这样的方式发不行的,例如 telegram。处理方法是,找到网上别人整理的 telegram 网段,自己放到 ipset 的 list 就可以。目前我似乎就发现 telegram 这么一个特例。

这个方法如果是一些公司内部使用,可能并不希望把所有被屏蔽的网站都支持,例如一些 porn 站点可能就没有支持的意义,这样就可以自己维护自己的域名列表,也很方便控制。

china ip list + ipset

当我们的代理的速度比较快的时候,可能有人会希望说我要把所有出国的流量都走我的代理。这样可以使用 china ip list 这样的列表添加的 ipset 的 list 里面,来实现目标。

china ip list 有几个版本,一个是 chnroute 的,一个是 ipip.net 提供的,我比较倾向于 ipip.net 的。对于这样的 ip list,需要说的是肯定无法做到 100% 准确的情况,理解他们的生成方法之后,我觉得 ipip.net 的更加可靠一点。通过这个 list 搭配 iptables 的 --match-set ,可以做到对于这个列表外的 ip 都通过代理请求。

这个列表还有另外一个用法,就是给国外用户访问国内服务用。一些网站,比如 taobao,视频网站,都会针对用户 ip 做一些不同的限制。国内用户和国外用户看到的界面和内容都不一样。有些视频可能国外用户付费也无法观看,这个时候他们就会需要通过一个国内代理来访问这些服务。那么就可以设置比如在 china ip list 里面的 ip 通过国内的代理访问。这就是所谓的回国模式。

v2ray sniffing

这个值得特别拿出来说一下。v2ray 提供了一个 sniffing 功能,通过 SNI 可以获取到一个连接请求的域名,然后可以配置自动使用代理。使用之后效果就是,不用在关心 dns 污染了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
    {
      "port": 12345,
      "protocol": "dokodemo-door",
      "settings": {
        "network": "tcp,udp",
        "followRedirect": true
      },
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      }
    }

例如上面这个就是支持 iptables redirect 方式转发的入站协议,里面配置了 sniffing。假如用户要访问 google.com,它真实的 ip 假设是 1.1.1.1,用户通过污染的 dns 会获取到例如 2.2.2.2 这样的 ip 地址,用户的软件就会去连接 2.2.2.2 这个地址,到了路由器之后,路由器会把请求转发给 v2ray 这个入站,因为配置了 sniffing,这个时候 v2ray 会尝试通过内置的一套的逻辑(一般人可能不用管,但是这套逻辑也可以相当复杂)去获取相应的 ip,这个时候会得到 1.1.1.1 这个地址,那 v2ray 会忽视用户想要连接的地址,而连接 1.1.1.1 这个正确的地址,这样用户会得到正确的结果。

这个里面唯一的存在问题是,用户使用的 dns 服务是知道用户想要访问 google.com 这个事实的。但是,what ever 我感觉不是什么大问题。

还记得前面解决 dns 污染遇到的问题么?这个 sniffing 把那些问题也解决了。因为不管用户或者软件使用了什么 dns 得到了一个域名的 ip,到了 v2ray 这里,都会通过自己的逻辑再次获取一下正确的 ip 地址。

基于这个,我强烈推荐使用 v2ray 来翻墙,目前我还没看到有其他软件实现这个功能的。v2ray 自己还支持差异化需求配置,例如可以只有某些域名或者 ip 才通过代理访问什么的,这样就非常灵活了,完全可以抛弃 ipset 了。

我这里有一个自己使用的例子,相当简单,丢到路由器(应该 linux 也可以,但是可能会需要调整 iptables 规则.. )上面用 run.sh update 安装一下 v2ray 和域名,ip库,就可以 run.sh start 了。难点可能是,我的路由器配置了 opkg,以及 swap。因为 update 需要用到 jq,以及 v2ray 内存占用比较高,一般路由器都内存不够,所以需要配置 swap。我也懒得细说了,已经浪费了很多时间在这上面了。。.

comments powered by Disqus