aisuneko 的软路由奇遇记
家里有了台 PVE 之后,其中一个需求是解决翻墙问题 - 这几年来对我来说翻不了墙约等于断网,我妈也跟我一起翻而且几乎每天都会刷 YouTube,我搭代理用的 VPS 也一直是她直接赞助的。她在房间里看视频用的 HTPC 上不知为何总是用不了代理客户端,我便想着在 PVE 上搭建软路由作为解决方案,顺便满足我自己在桌面设备和虚拟机上的上网需求。没想到这一折腾便延续了近一年,我在各种网络故障和连接问题之间反复横跳,疲于奔命…… 不过换个角度说,也正是这段折腾软路由的经历让我更深入的接触了 Linux networking,科技树点的更全面了。
Stage 1: OpenWRT + OpenClash
在这之前,我对软路由的配置一点概念都没有,尽管在实体路由器上刷过 OpenWRT。在朋友的指导下简单入了门:
从官网下载 OpenWRT 对应版本的 x86_64 镜像(
*-generic-ext4-combined-efi.img.gz
,我当时用的是 21.02.1)。PVE 上新建一个虚拟机 (注意内存至少分配256MB,否则无法启动),将上面的镜像解压缩后
qm importdisk
直接导入到VM中启动进 shell 后在
/etc/config/network
里改网络配置,lan interface 里把ipaddr
改成一个别的地址,再加option gateway <addr>
和list dns <addr>
设置网关和DNS(以前ip addr add/del <addr> dev br-lan
好像也行,按理说重启会失效?)
/etc/init.d/network restart
, 这时就能在软路由的 ip 访问 LuCI WebUI 了。
因为我一直是 Clash 用户,之后就装了 OpenClash。使用也不是很复杂,很快就配好代理(一开始用的是 vmess, 后来迁移到了 Trojan)可以用了。
后来就一直在用这套配置上网,感觉还算稳定。期间有一些连接问题却总是排查不出毛病,似乎就是代理本身不太稳定。中间 OpenClash 和 VM 本身也炸过,但因为有给 VM 打过备份,一般都是还原一下就行。
Stage 1.5: OpenWRT + helloworld
按理说这个软路由虚拟机就可以备份起来不再改配置了。然而 2022 不愧是疯狂的一年,Trojan 协议出了大问题……
很快这个问题便落到了我的头上:一次排查连接异常的时候发现自己 VPS 的 443 端口被墙了,而据说鉴于 Trojan 协议的特点,非 443 端口更会被秒墙…… 听说解决办法是迁移到 Trojan-Go, 可惜 OpenClash 不支持。
无奈花了 $10 换了 VPS 的 ip,这回 Trojan 倒还可以继续用,但对我来说已经 deprecated 了,换新的协议迫在眉睫。
就在这时我接触了黑科技般的 naiveproxy - 它直接基于 Chromium network stack 实现,稳定的同时搭建和使用都相当简单。我在 VPS 上通过 sing-box 快速搭了一个实例,在手机上试用了一段时间,体验确实不错,便决定整体迁移到 naive。
四处搜索 OpenWRT 上的 naive 实现,最后还是群友推荐了 helloworld 这个项目。很快我发现这不是一个开箱即用的代理工具,而是一整个 OpenWRT 软件仓库,又必须在编译时就加进镜像中。怎么办?只能自己编译 OpenWRT 了。
一般照着官方 repo 里写的步骤来就没问题,也可以同时参考 官方 wiki 上略老的教程,只是自己摸索了好几次才成功。主要的经验教训就是 make menuconfig
里一定要把要装的自定义包选全(因为是加了 feeds 之后手动选择源里的包编译到镜像里),以及编译时存储空间一定要给足。编译出镜像之后像上面的那样把 VM 设置好就可以用了。
这个源里提供的 WebUI 客户端是luci-app-ssr-plus
。好用吗?不好用。首次启动之后能用,过一会儿就会炸 - 这玩意看着如此简陋,又没有 logging,也不知道发生了什么,只跟这个 issue 里描述的情况比较接近。哎,看来这条路走不通,只能 fallback 到 OpenClash 了,奇怪的是 Trojan 坚持的比我想象中的要久。
Stage 2: Ubuntu Server + Clash/naiveproxy + iptables
2022 年底,最开始那套 OpenWRT 配置正式不可用了。我想是时候迁移到一个完全不同的方案了:既然 OpenWRT 上没有什么能用的支持 naiveproxy 的代理软件,干脆直接弃掉 OpenWRT,从零开始自己 DIY 一个 Linux 软路由。
虚拟机的 distro 本来打算用 Debian,但PVE上不知为何安装总是失败,就用上了 Ubuntu Server。
朋友发了一张 iptables processing flowchart,之后又找到了这篇文章。看过之后我大致理清了手动搭建软路由的思路:
VM 作为网关,经过它的外部流量通过 iptables 规则转发到一个透明代理,比如 Clash
使用 Clash 解析 DNS 和分流流量
Clash 本身不支持 naiveproxy 协议, 所以使用 naiveproxy 原版客户端启动本地 SOCKS5 代理作为 Clash 的上游
我承认我基本没用过 iptables,之前因为觉得太复杂就去用 ufw 了;但还是大致按照上面那篇文章里的步骤,折腾了一天勉强搭了一个出来。我的 iptables 配置大致如下:
1 | iptables -t nat -N CLASH |
我自己的配置过程跟那篇文章里讲的有一定出入。比如因为家里已经有一台主路由器,这只是作为一个翻墙用的旁路由,就省去了配置 NAT 和 DHCP 的所有步骤,虽然还是得在
/etc/sysctl.conf
里写net.ipv4.ip_forward=1
。我也没有过多的折腾netplan
,据说是个套娃但 dnsmasq 之类的配置基本是照抄的。systemd-networkd
的阴间玩意。
试用过程中却发现一些网站会有连接问题,比如 YouTube 视频加载半天加载不出来。我怀疑是我自用的 Clash 规则列表太旧,然而网上找不到能用的规则列表;GitHub 上几乎所有的 Clash 规则相关 repo 提供的都是给 Clash Premium 用的订阅式 RULE-SET
。我想 Premium 就 Premium 吧(虽然 proprietary 坏文明),结果启动直接报错提示无法连接到订阅地址:lookup raw.githubusercontent.com on 127.0.0.1:53: read udp 127.0.0.1:<whatever> -> 127.0.0.1:53: read: connection refused
。
其实各位看到这个信息就应该知道是 DNS 问题了。我当时却没反应过来,觉得问题出在没有让本机流量直连,尝试改iptables规则却找不到正解。
最后我把这个问题留到了 2023 年。 2023 年的第二天早晨,我醒来后恍然大悟:我就没装 dnsmasq
这个包!装上之后再启动 dnsmasq 服务果然恢复了正常…… 但要注意 Clash 写成 systemd 服务的话需要在 dnsmasq 之前启动,否则后者会抢占对应的 port 53。到这里总算完工了。(其实对于 Telegram 这种原生支持 SOCKS5 代理的应用也可以让它直接指向 naiveproxy,可能稳定一点。)
至于 Clash 规则的问题,后来我一气之下写了一个普通版本 Clash 专用的规则生成脚本。
总结
可能是因为我的网络经验不足,我搭建和使用软路由的过程相当曲折,最后 uptime 还差强人意,已经被我妈投诉过不止一回了。可以说我今年里不少的闲暇时间也直接花在维护软路由和排查网络问题上了。
其实我从小时候接触 computing 开始就对网络技术几乎一窍不通,可以说至今的人生都是在对 networking 的恐惧中度过的。我也有尝试过补齐技术上的这一短板;初中时试着学习了一段时间网络基础,但只初步了解了 OSI 七层模型,基本网络硬件和 TLS 之类的基础概念就没继续下去了。
直到高一时我搬了家,跟朋友一起花了半天时间搞定新家的网络配置,我对 networking 的恐惧才缓解了一点。只是我至今仍然没有继续系统地学习这方面的知识,处理网络问题仍然凭感觉。(朋友还建议我去上 Cisco CCNA,可惜我没听他的。)
回看这段搭建软路由的经历,虽然麻烦但其实还挺有意思的。从几年前惧怕乃至排斥 networking 到慢慢学会运用基本的网络技能解决实际问题…… 也许这就是我的所谓「成长」吧。