NAS远程访问不安全?用Lucky给自己的服务增加手机二次验证
摘要
本文介绍利用 lucky 为 NAS 本地服务添加域名反代及二次验证的方法。首先通过 Cloudflare 和 lucky 设置 DDNS 与反向代理,随后在 lucky 中配置 GitHub 第三方认证,并可选绑定 Bitwarden 二步验证。访问服务时需登录 GitHub 并输入验证码,从而在不支持密码的服务上实现安全访问。注:此功能需安装完整版万吉。
前言
为了方便在外面也能访问家里 NAS 上的服务,我一直用 lucky(一款功能很全的反代/DDNS 工具)给 NAS 上的本地服务添加域名反代,通过 DDNS 实现外网访问。
但"方便"和"安全"往往是矛盾的:一旦把服务用域名暴露到公网,就等于把它开到了公网上,谁扫到都可能来试。为了安全,我通常会再给服务加一道登录密码。可麻烦的是——
有些服务本身没有登录功能;
有些服务我压根就不想让它暴露出去,却又需要在外网临时用一下。
这种情况下,给这些服务加一道"二次验证"(第三方认证),就是一个很好的兜底方案。今天以我在 NAS 上部署的一个搜书服务为例(这个项目本身没有登录功能,但又不想被陌生人访问),演示怎么用 lucky + GitHub 给它套上一层登录墙。
如果你还没用过 lucky,建议先看基础篇:用 lucky 插件通过 IPv6 访问本地服务器,把域名反代和 DDNS 跑通,这篇讲的是在它之上再加一层认证。
先区分清楚:你要的防护在哪一层
"服务暴露在公网"这件事,其实可以在三个不同层面上做防护,它们的职责完全不一样:
层面 | 防的是什么 | 典型手段 |
|---|---|---|
网络层 | 来自哪些 IP 的访问 | 路由器防火墙、只放行特定 IP 段 |
应用层 | 恶意的请求内容 | WAF(拦截注入、扫描、恶意 payload) |
认证层 | 谁有资格进来 | 登录密码、二次验证、OAuth |
这篇做的是认证层——不管对方从哪来、请求长什么样,没通过身份验证就一律进不去。它解决的是"服务本身没有登录功能"这个最尴尬的场景。
顺带一提,lucky 自己也能配网络层和应用层的防护(IP 过滤 + WAF),那是另一套配置,我写在用 Lucky 保护你的飞牛:开启 WAF 防火墙、设置 IP 过滤这篇里。两者可以叠加:WAF 挡恶意请求,二次验证挡人。
前置:添加域名反代
先用 lucky 给服务做好域名反代(DDNS 的详细方法可以参考我之前的文章,这里快速过一遍流程):
Cloudflare 添加解析:添加一条解析记录,指向 NAS 的 IPv6 地址。

lucky 添加动态域名记录:在 lucky 的 DDNS 里配置这条域名记录,让外网 IP 变化时能自动更新解析。

web 服务中添加反代规则:在 lucky 的 Web 服务里新增一条反代规则,把域名指向 NAS 上的搜书服务。

测试外网访问:配置好后先从外网访问一下,确认能正常打开。

到这里,服务和"能外网访问"已经打通了,但此刻它也是完全裸奔的——谁拿到域名都能进。接下来就给它上锁。
"谁知道域名啊,扫不到吧?" 这是最常见的侥幸心理。事实上,只要服务在公网 IP 上开放了端口,端口扫描是全网无差别进行的——不需要知道你的域名,扫到端口再反查 SNI(HTTPS 握手里的主机名)就能找到你。所以"用了个随机域名"根本不构成防护。这也正是这篇要解决的问题。
添加 GitHub 认证
1. 添加认证用户
在 lucky 里选择 第三方认证,先添加一个认证用户。这里的思路是用 GitHub 账号作为"入场券"——只有被你授权的 GitHub 账号才能通过认证。

2. 发起授权
添加用户后,点击 发起授权,lucky 会生成一个 GitHub OAuth 授权链接。

3. 登录 GitHub 并授权
在弹出的新页面里,登录你的 GitHub 账号并完成授权。授权成功后,这个 GitHub 账号就和 lucky 的认证用户绑定了,只有它能通过这道登录墙。

这一步到底发生了什么? 这是标准的 OAuth 授权流程:lucky 并不是拿到了你的 GitHub 密码,而是拿到了 GitHub 签发的"这个用户确实是他本人"的凭证。你的密码全程只输在 GitHub 自己的页面上,lucky 从来接触不到。 这也是为什么这种方案比"给服务设个固定密码"更安全——密码不存在被服务方泄露的可能。
反过来说:如果你哪天在 GitHub 里撤销了这次授权(GitHub 设置 → Applications),那么这条登录墙会立刻失效,你就进不去了。这是它的"开关"所在,记住这个位置。
4.(可选)添加二步验证
想更保险的话,还可以给认证加上二步验证(2FA)。这时需要用登录工具器(TOTP 扫码工具)扫码绑定——我选择用 Bitwarden 来管这个动态码。之后每次登录除了 GitHub 授权,还要再填一个 6 位动态验证码。

为什么 6 位数字能验证身份? 因为 TOTP(基于时间的一次性密码)的原理是:绑定的时候,服务器和你各存了一份相同的密钥;之后双方用「这份密钥 + 当前时间」按同一个算法算出同一个 6 位数,每 30 秒变一次。服务器只要看到你报的数字和它算出来的一致,就认为你持有密钥。所以这个码不需要联网传输,也不会被截获后长期复用。
⚠️ 由此引出一个常见坑:这个算法强依赖时间准确。如果你的 NAS 或手机时间偏移超过 30 秒,验证码就会永远对不上。遇到"动态码怎么输都不对",先去检查两端的时间同步(
timedatectl看 NTP 是否正常)。
5. 在反代规则上启用认证
回到 Web 服务的反代规则里,修改这条子规则:打开 网页认证,选择刚才绑定的那个 GitHub 账户。这样这条反代就挂上了"登录墙"。

安全登录效果
配置完成后再访问服务链接,就会看到 lucky 的登录页面:
打开服务链接,不再直接进入搜书服务,而是先跳到 lucky 的登录页面;

点击下面的 GitHub 图标(首次会要求登录 GitHub 并完成授权),授权通过;

如果开了二步验证,还要填 Bitwarden 生成的 6 位动态密码,全部通过后才进入服务。

两个容易被忽略的点
① 这层认证是"单点"——它挂了你自己也进不去。
认证由 lucky 负责,所以lucky 本身出问题(容器挂了、配置改错、证书过期)时,你从外网也会被挡在门外。这不是 bug,是架构决定的。
务实的做法:留一条不用 lucky 的备用通道。比如在内网直接访问服务的本地端口(http://192.168.1.x:端口),或者用 Tailscale 这类不依赖 lucky 的方式连进来。别让自己只有一个入口——我的做法是把这条内网直连路径写在密码管理器里,真出事时不用现场排查。
② "密码和动态码放同一个 App",其实削弱了 2FA 的意义。
用 Bitwarden 同时存密码和 TOTP,确实方便——但 2FA 的本意是"两个独立因素":你知道的东西(密码)+ 你有的东西(手机/密钥)。两个都放进同一个密码库,等于把两个因素又合并成了一个:一旦这个库被攻破,两道门一起失守。
我的取舍是:便利性在这个场景下更重要,所以我还是放在一起。但你应该清楚这个代价——如果你保护的是真正敏感的东西(比如银行、主邮箱),那 TOTP 值得单独放一个 App(Aegis、Ente Auth 之类)。顺带一提,我之前有篇讲甲骨文账号 2FA 迁移的实践:把甲骨文登录二次验证换成 Bitwarden 验证器,那里也讨论过同样的问题。
结尾提醒
⚠️ 需要注意:这个功能可能需要 lucky 的完整版(万吉)才支持;而且 3.0 版本的界面和 2.7 可能略有不同。如果你用的是精简版或旧版本,找不到对应选项是正常的。
个人结论
用 lucky 给"没有登录功能"的服务套一层 GitHub 认证 + 可选的二步验证,等于给裸奔的反代服务补上了一道像样的门禁:未授权的 GitHub 账号进不来,加上 2FA 后即使 GitHub 账号泄露也需要动态码才能过,安全度明显上了一个台阶。特别适合那些"不得不暴露、又没有自带认证"的自托管服务。缺点是第三方认证依赖 GitHub 可用性,但对于个人自托管场景,这个方案在安全与便利之间的平衡已经相当好了。值得一试。
如果你想再叠一层:认证层解决"谁能进",应用层(WAF)解决"进来的是恶意请求怎么办"。这两层不冲突——WAF 的具体配置可以看雷池 WAF 社区版:反代 + 免费防火墙,它是另一套独立于 lucky 的方案。