本文中的域名、IP 地址、路径、Token 等信息均已脱敏处理。
前段时间部署了 Sun-Panel 导航页(1.3.0 版本)。
由于这个服务需要暴露在公网,所以我决定继续沿用之前已经建立好的客户端证书体系。
这样做以后,即使别人知道了域名,也无法直接访问页面。
浏览器在建立连接之前就会要求出示客户端证书,没有证书的请求会被 Nginx 直接拒绝。
关于 mTLS 的原理,可以参考上一篇文章:
本文只记录实际部署过程。
当前架构
我的部署结构大致如下:
公网 ↓路由器端口转发 ↓宝塔 Nginx ↓Sun-Panel其中:
Sun-Panel直接自编译运行在 Mac mini 上
Nginx运行在虚拟机中的宝塔面板为什么不用docker?详见:《Sun-Panel 的宿主机监控和登录会话调整》
Nginx 负责 HTTPS、反向代理以及客户端证书验证。
Sun-Panel 本身完全不需要感知客户端证书的存在。
对于 Sun-Panel 来说,请求是否携带客户端证书并不重要。
因为所有验证工作都已经在 Nginx 这一层完成了。
为 Sun-Panel 生成服务器证书
之前已经有一套自己的 CA。
因此不需要重新生成客户端证书。
只需要为新的域名单独签发服务器证书即可。
首先生成私钥和 CSR:
openssl req -newkey rsa:4096 \ -nodes \ -keyout sunpanel_server.key \ -out sunpanel_server.csr \ -subj "/C=CN/ST=XXX/L=XXX/O=MyHome/CN=sunpanel.example.com"生成完成后可以得到:
sunpanel_server.keysunpanel_server.csr随后使用 CA 进行签名:
openssl x509 -req \ -in sunpanel_server.csr \ -CA ca.crt \ -CAkey ca.key \ -CAcreateserial \ -out sunpanel_server.crt \ -days 3650 \ -sha256输出类似:
Certificate request self-signature oksubject=...至此服务器证书生成完成。
准备 CA 根证书
之前生成的服务器证书负责证明服务器身份。
而客户端证书验证则需要依赖同一套 CA 根证书。
因此需要把 CA 根证书复制到站点证书目录。
例如:
cp ca.crt /www/server/panel/vhost/cert/sunpanel.example.com/chmod 644 /www/server/panel/vhost/cert/sunpanel.example.com/ca.crt这样 Nginx 就能读取并使用它验证客户端证书。
修改 Nginx 配置
打开站点配置文件。
在 SSL 配置区域增加:
ssl_client_certificate /www/server/panel/vhost/cert/sunpanel.example.com/ca.crt;
ssl_verify_client on;
ssl_verify_depth 2;完整配置大致如下:
ssl_certificate fullchain.pem;ssl_certificate_key privkey.pem;
ssl_client_certificate /www/server/panel/vhost/cert/sunpanel.example.com/ca.crt;ssl_verify_client on;ssl_verify_depth 2;保存并重载 Nginx。
配置完成以后:
所有 HTTPS 请求↓先验证客户端证书↓验证通过↓转发到 Sun-Panel如果没有证书,请求会在 Nginx 这一层被直接拦截。
验证是否生效
先测试未携带客户端证书的情况。
执行:
curl -k https://sunpanel.example.com返回:
400 No required SSL certificate was sent说明客户端证书检查已经生效。
然后测试携带证书访问:
curl -k \ --cert client.crt \ --key client.key \ https://sunpanel.example.com这次返回:
<!DOCTYPE html><html>...成功获取到 Sun-Panel 页面。
说明证书链验证通过。
导入浏览器
客户端证书导出为:
client.p12然后导入:
Windows
证书管理器↓个人↓导入↓选择 client.p12macOS
钥匙串访问↓登录↓导入项目↓选择 client.p12导入完成后重新打开浏览器。
访问站点时会弹出证书选择窗口。
选择对应证书即可。
最终效果
配置完成以后:
现在访问 Sun-Panel 时:
没有客户端证书:
访问失败拥有客户端证书:
访问成功正常进入页面
而且整个过程发生在 TLS 握手阶段。
因此 Sun-Panel 本身不需要安装插件。
不需要额外配置账号密码。
也不需要修改应用代码。
所有认证工作都由 Nginx 完成。
小结
对于这类只允许自己访问的服务来说:
浏览器导航页私人部署的密码库某个服务的管理面板NAS各种自建服务等等客户端证书是一种很适合的保护方式。
它不会依赖固定 IP。
也不会额外增加一层登录页面。
只要设备持有正确证书,就能访问服务。
否则连接会在进入应用之前被直接拒绝。
我最终采用的方案是:
Let's Encrypt负责 HTTPS
自建 CA负责客户端证书
Nginx负责验证客户端证书
Sun-Panel只负责提供服务这样既保留了公网访问的便利性,也增加了一层额外的访问控制。
因为同一套 CA 和客户端证书可以被多个服务复用。
后续如果有新的自建服务需要暴露到公网,只需要为对应域名重新签发服务器证书即可。
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时




























