内网穿透的高可用:双实例、看门狗与端到端验证
家里的 NAS 要对外服务,穿透隧道就是生命线。这篇讲穿透架构的容灾设计、三个套件级的坑,以及'判一条链路死活'的正确姿势。
家用 NAS 对外提供服务,绕不开内网穿透。穿透隧道是这类架构的生命线,而生命线最大的敌人不是断,是你以为它活着。这篇讲我们穿透架构的演进、三个套件级的深坑,和一条被事故教育出来的铁律。
架构演进:从单实例到双实例再到收敛
初版架构是单一穿透客户端,把家里多个服务映射到公网。这个架构的脆弱性在一次误操作中暴露无遗:更换配置时直接覆盖,把一条“看起来死了”的旧隧道顺手删掉——结果一个正在服务的对外站点立刻五十二,因为那条“死”隧道其实活着,只是没人测过它。
事故后的第一版修正 是双实例:套件原生实例跑主隧道,另起一个独立服务跑备份隧道,各自指向不同的公网入口,单点故障不至于全灭。第二版修正是收敛:审计发现备份实例的一个服务端口长期对公网敞开,访客带对域名就能绕过边缘直达内部——端口当即收敛,公网暴露面从“散装”收到白名单。最终形态:单一实例、端口池受限、强制传输加密,备份能力由“配置可快速重建”代替“第二实例常驻”。
这个演进过程的结论值得记住:高可用不等于多跑一份,先把暴露面收敛、把重建时间缩到分钟级,比多养一份常驻更安全。
套件级的三个深坑
穿透客户端以套件形式安装在 NAS 上,深坑全在这里。
**坑一:服务类型是“一次性”的。**这类单元执行完就算完成,对它执行重启才真正生效,执行启动等于空操作。更深一层:重启只杀进程文件里登记的那个进程,曾有看门狗另拉一个进程不在登记里,于是两个进程并存抢配置——清理的标准姿势是按名字全杀、删进程文件、再重启,然后确认进程数归一。
**坑二:配置文件的位置敏感。**全局参数必须写在所有代理定义之前,追加到文件尾会变成最后一个代理的参数,启动直接报字段非法退出。改配置前先看结构,别做尾部追加党。
**坑三:定时任务不可信。**系统里留有一条自启用的冗余自启配置,写着“下次重启生效”——实际上那台机器的定时服务对自定义条目根本不执行。指望它,等于指望一个从不兑现的承诺。保活要靠套件自己的看门狗,自启配置要实测验证过才算数。
端口收敛审计的完整案例
那次端口审计的发现值得完整讲。起因是安全复盘时的一个问题:穿透服务的公网入口端口,除了已登记的用途,还有谁能到?逐项排查发现,穿透服务端的两个宿主端口曾在早期对公网放行——它们本是穿透架构的内部路由端口,设计上只该被本机反代访问。风险在于:外网访客带上正确的域名头直连这两个端口,就能绕过边缘反代的所有校验,直达穿透后面的内容。彼时多条生产链路都走在上面。
处置分三步:删掉防火墙的放行规则(四条,含第六版协议);外网实测直连超时、各域名走正常链路全部正常——收敛动作的前后都要有外网视角的验证;写明回滚路径(重新放行两条规则),万一误判可秒回。这次审计还留下一句被写进档案的话:边缘校验设计必须先枚举到达后端的全部网络路径——这句话后来又拦下过一次同类隐患。
转发头的覆写陷阱
穿透链路上还有一个隐蔽坑:穿透协议对转发类型的代理会覆写标准转发头——源站经边缘传来的真实协议声明,到穿透这一层被改写成穿透自己看到的协议。下游应用据此误判“来访者用的是明文”,各种基于该判断的逻辑(重定向、资源地址)全部出错。排查靠的是在链路上抓包,比对每一跳的头。修法是在穿透客户端侧无条件改写协议声明头(该链路只有加密入口一种进入方式时成立),同时下游应用把代理信任列表收紧到本机——三层各就各位后,黄条根治。
这个坑的普适意义:转发链路上每一跳对头的处理,都要留档。谁改写、谁追加、谁透传,一张表列清,出问题按表对账,五分钟定位;没有这张表,就是几小时的抓包。
铁律:判链路死活,必须逐跳实测
这场演进中最贵的一课,是一次误判:某条隧道被判定“已死”,依据是三点——配置是旧的、端口上没有连接、抽查的几个服务不通。三条论据两条站不住:端口“没有连接”从未真正实测;“不通”的抽样恰好抽在已下线的服务上。换配置直接把一条活隧道打成中断,约十七分钟后才恢复。
事故换来的铁律:判一条链路的死活,必须端到端实测每一跳——入口地址通不通、中间端口有没有监听、每个域名带对头部逐个探、协议版本分别试。抽样推断在链路问题上等于赌博,因为链路是串联的,一段活不等于全通,一段死也不代表全死。
沉淀
穿透架构的可用性,三分靠设计,七分靠验证纪律。设计上:暴露面最小化、重建时间最短化、保活交给看门狗而不是定时任务。验证上:任何“死了”的判断都要逐跳实测背书,任何“活了”的判断都要端到端实测背书。生命线这种东西,宁可错杀配置,不可错信状态。