起源
作为一个(贫穷的)润人,生活在没有墙的地区,为什么还要用 VPN 呢?这才是 VPN 的本意:虚拟局域网,目标一定是少数网址。例如第一种情况:远程访问家里的 NAS 资源,则目标就是家庭的局域网 IP。此时需要在家庭的设备上搭建服务。
第二种情况是远程访问单位的内网资源,此时需要在单位的设备上搭建服务。由于单位的设备管控比家庭更加严格一些,所以就需要动动脑筋。
无论是哪种模式,因为目标网址都很少,所以在规则判断上比较简单,这与东方某大陆把物理因特网变成物理局域网,迫使程序员把虚拟局域网改造成虚拟因特网的情况截然不同。
现状
单位提供官方的 VPN 客户端,但使用起来不太方便。单位还有一台性能不错的服务器仅供我一人使用,但是只能暴露80、443 端口和一个 SSH 端口。除此之外不能暴露别的端口。
放弃 WireGuard
开始的设想是:因为访问家庭资源用 WireGuard,那么在单位的服务器上也搭建一个 WireGuard 服务器,然后自己设备上用 WireGuard 客户端就能一举两用了。但是仅有的三个端口已经全部使用完毕,不能暴露别的端口,就无法直接搭建 WireGuard 服务。另外也不能使用 cloudflared 来曲线救国,因为 WireGuard 是使用 UDP 的,与 cloudflared 的 TCP 不太合得来,而且 QUIC 流量通过单位的防火墙时有问题。
SOCKS 5 代理
因为访问单位内网仅仅是一些很轻量的数据浏览,ChatGPT 给出的方案是 SOCKS 5 代理(没错,在润之前也经常听过这个词)。
首先在自己电脑上(以我的 macOS 为例)的终端运行:
ssh -D 1080 -C -q -N [email protected]
这时就创建了一个微型 VPN:地址是 localhost 即 127.0.0.1,端口号是 1080。
由于我平时使用 Safari 浏览器,没有专门的扩展来给浏览器自己设置代理,而是需要在系统网络里面设置。首先我设置了全局代理,如右图所示。设置之后测试成功。
但因为并不需要全局代理,只是针对少数几个网址进行代理,ChatGPT 给出了 PAC 方案。即写一个名字叫做 proxy.pac 的文件,代码如下:
function FindProxyForURL(url, host) {
if (shExpMatch(host, "*.example.com") || shExpMatch(host, "another-site.com")) {
return "SOCKS5 127.0.0.1:1080";
}
return "DIRECT";
}
然后把文件地址 file:///path/to/proxy.pac
填入设置区域即可。(当然需要把之前测试用的全局代理取消)
以上是 ChatGPT 宣称的。然而填入之后发现完全没有任何效果,说明要么是 PAC 文件有问题,要么是 URL 填写有问题。
到此为止 ChatGPT 就无法进一步解释了,开始胡说八道了。最后是自己去网上搜到的两个坑。
小坑:macOS 已经不再支持填写本地的 PAC 文件
这个在好几年前就是这样了(file:/// 无效),ChatGPT 却不知道。于是我用
python3 -m http.server 8000
搞一个本地 HTTP 服务器。但还是不行。
大坑:macOS/Safari 不认 SOCKS5 语法
要把 SOCKS5 127.0.0.1:1080
这句话里面的 5 去掉,变成 SOCKS 127.0.0.1:1080
。这时就可以用了。
转移二线
这么一搞问题变复杂了,这意味着电脑上需要跑两个背景进程,一个是 ssh 的 SOCKS 服务器进程,一个是 python 的微型 http 服务器进程。可是我的是 macbook 笔记本电脑平时用完就合上盖子,此时系统进入睡眠状态,这些网络进程一定会断掉。每次都要重新开启太麻烦了。于是我就想把两个进程转移到 VPS 上。
在 VPS 上设置好这两个进程之后,首先仍然在电脑上测试一下全局代理(把所有 127.0.0.1 换成 VPS 的 IP)。傻眼了,所有的网站都上不去了,说明 SOCKS 服务设置不对。
再次询问 ChatGPT,这回没有胡说八道:ssh -D
默认只认本地的 SOCKS,想要让它侦听远程来源的 SOCKS 访问请求,需要给 SOCKS 命令加上 0.0.0.0,即 ssh -D 0.0.0.0:1080
,此时就可以侦听远程来源的 SOCKS 访问请求。
设置之后,再测试全局代理成功。然后 VPS 本身有标准的 HTTP 服务,托管 proxy.pac
文件,把 URL 填入系统设置里面。测试成功。
转移三线
VPS 对外是公网 IPv4 的地址,如果被黑客扫到 SOCKS5 代理的端口号,那问题会非常严重,他们只需要用 IP + 端口号就可以无限制利用我的微型 VPN,不需要任何验证。
虽然 SOCKS5 代理也有用户名和密码验证机制,但是比较麻烦。所以我决定转移三线:把 VPS 换成我的 OpenWRT 路由器!
在 OpenWRT 上运行 ssh -D 0.0.0.0:1080
….. 之类的命令,PAC文件则使用 OpenWRT 内置的 uhttpd 来进行托管。由于路由器在内网,虽然也写成了 0.0.0.0,但实际上只有自己能够访问,外面的人无法访问。如果自己不在家里而是在外面,则用 WireGuard 翻回家里即可。
黑客攻破 WireGuard 密钥的概率基本为0,这下子应该安全了。
二线 + 三线
虽然 PAC 文件是在当前某个 Wi-Fi 的菜单里面点进去设置的,但是它对所有 Wi-Fi 都是默认生效的,和我预期的不同。因此在外面网络环境下,需要一直连着 WireGuard 才行,否则系统无法从内网 IP 获取 PAC 文件,就不能正常上网。这也太浪费了吧!我只是偶尔用到这个 VPN 而已,就要一直开着 WireGuard?
于是就想出了 二线 + 三线的策略:PAC 文件放在 VPS 上,采用 https://mydomain.com/proxy.pac
的 URL,填入 macOS 系统的网络设置里面。而 proxy.pac
的文件的 SOCKS 代理地址则写路由器的内网地址。这样子就算是即方便又安全了。无论是在家里还是在外面,PAC 文件都能访问到,因此不影响正常上网。需要访问 VPN 时,就连上 WireGuard 进入家庭局域网,即可连接 SOCKS 代理。
我比较懒,一般用clash,tun模式接管系统所有流量,使用规则覆写,对指定ip段和域名使用代理。 😀 😆
性能也降低不了多少,基本无感。但是省去劳心费神的时间。
WireGuard 也不错。不过我没设置成功当时。