1212 字
6 分钟
给openwrt添加现代化安全2fa认证

现状#

TIP

经过努力,已经把luci-plugin-2fa和对应的登录补丁合并进OpenWRT的luci代码库,可以直接从新版(大概是25.x之后)下载luci-plugin-2fa软件包使用。

NOTE

欢迎前往我独立的 luci-plugin-2fa 代码库点个 Star,感谢支持!

起因#

OpenWrt 自 2004 年诞生以来,一直是大量路由器系统的基石。即便其功能与性能不断进化,但在账号安全认证方面,LuCI(其 Web 界面)长期停留在传统的“用户名+密码”模式。在当今网络攻击手段日益多样的环境下,这种单一的认证方式显得有些力不从心。

在立项之初,我查阅了社区资料,发现官方一直缺乏原生的 2FA 组件。虽然在 Issue #8273 中可以看到社区曾有过零星的尝试,但大多由于架构限制或开发者断更,最终都停留在不可用的状态。最接近成功的一次是 PR #7069,它实现了 TOTP 算法的基础,但由于未能解决登录逻辑的耦合问题,原作者也已查无此人,项目随之搁浅。

在接手这个挑战时,我并没有预料到,真正的难点并不在 2FA 算法本身,而是在于 OpenWrt 沿用了十几年的登录架构。

修改架构:重新设计登录流程#

在深入研究 PR #8281 时,我意识到 LuCI 之前的登录链过于线性且缺乏原子化。原始的逻辑可以概括为:

输入凭据 -> 验证 -> 立即生成 Session -> 登录完成

这种强耦合的逻辑被硬编码在核心分发器(Dispatcher)中,完全没有给第三方模块留出干预空间。若直接在这个链条里强行塞入 2FA 逻辑,会导致代码极其臃肿且难以维护。

在方案选择上,直接拦截(HOOK)核心分发器文件虽然实现路径较短,但这种做法缺乏优雅性,会带来巨大的维护风险,本质上是在原有的技术债务上继续堆叠。考虑到未来还需加入验证码(Captcha)或 WebAuthn 等认证方式,这种打补丁式的开发模式是完全不负责任的。

为了从根本上解决问题,我与 OpenWrt 核心开发者 @systemcrash 进行了多次深入沟通。我们在讨论中明确了插件系统的概念与设计。通过在登录过程中引入“多阶段验证钩子”机制,将登录流程重塑为:

输入凭据 -> 基础验证 -> 调用认证钩子(2FA/LDAP等) -> 全部通过后生成 Session

这种设计将 2FA 转化为一个可选的增强插件,消除了必须修改核心代码的束缚。相较于传统的硬编码方式,这种原子化的重构为后续支持 WebAuthn 等现代化安全协议打下了稳健的底层基础。

2FA 的实现细节#

在解决了底层架构后,PR #8280 的实现就变得水到渠成了。对于 TOTP(基于时间的一次性密码)算法,我利用了现成的 uqr 库来生成配置所需的二维码,降低了系统的依赖负担。

在开发过程中,安全性与稳健性是考量的核心。相较于简单的 Demo,合入官方库的代码需要考虑多种边缘情况,例如:

  1. 时钟同步问题:路由器如果 NTP 同步失败,会导致 TOTP 验证失效,因此增加了显眼的同步提示。
  2. 紧急恢复方案:一旦用户丢失手机或误删密钥,需要有后台重置机制。
  3. UI 适配:确保在各种移动端浏览器上都能正常显示验证界面。

整个过程经历多次代码审查(Code Review),最终得到了官方维护者的认可并成功合入主干。

未来展望:拥抱 WebAuthn#

目前的 2FA 主要是基于 TOTP 的验证码,虽然安全,但在用户体验上仍有提升空间。我目前正在推进 luci-plugin-webauthn 的开发工作。

相比 TOTP 验证码,WebAuthn 允许用户使用指纹、FaceID 或是 Yubikey 等硬件密钥进行身份认证。这不仅是安全性上的跃升,更能实现“无感登录”。虽然这部分的实现涉及更复杂的加密挑战与浏览器交互,但我相信它会成为 OpenWrt 安全体系中的重要一环。

从一个简单的 Issue 到最终合入官方主干,这段经历让我深刻体会到:在开源社区,实现一个功能往往只需几行代码,但若要构建一个通用的标准架构,则需要反复的推敲与对细节的极致追求。

给openwrt添加现代化安全2fa认证
https://tski.uk/blog/openwrt-advance-security/
作者
Tokisaki Galaxy
发布于
2026-05-27
许可协议
CC BY