mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6mobile wallpaper 7mobile wallpaper 8mobile wallpaper 9mobile wallpaper 10mobile wallpaper 11mobile wallpaper 12mobile wallpaper 13mobile wallpaper 14mobile wallpaper 15mobile wallpaper 16mobile wallpaper 17mobile wallpaper 18mobile wallpaper 19mobile wallpaper 20mobile wallpaper 21mobile wallpaper 22
1005 字
3 分钟
使用客户端证书保护 Sun-Panel
2026-05-29

本文中的域名、IP 地址、路径、Token 等信息均已脱敏处理。

前段时间部署了 Sun-Panel 导航页(1.3.0 版本)。

由于这个服务需要暴露在公网,所以我决定继续沿用之前已经建立好的客户端证书体系。

这样做以后,即使别人知道了域名,也无法直接访问页面。

浏览器在建立连接之前就会要求出示客户端证书,没有证书的请求会被 Nginx 直接拒绝。

关于 mTLS 的原理,可以参考上一篇文章:

《理解 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.key
sunpanel_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 ok
subject=...

至此服务器证书生成完成。


准备 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.p12

macOS#

钥匙串访问
登录
导入项目
选择 client.p12

导入完成后重新打开浏览器。

访问站点时会弹出证书选择窗口。

选择对应证书即可。


最终效果#

配置完成以后:

现在访问 Sun-Panel 时:

没有客户端证书:

访问失败

拥有客户端证书:

访问成功

正常进入页面

而且整个过程发生在 TLS 握手阶段。

因此 Sun-Panel 本身不需要安装插件。

不需要额外配置账号密码。

也不需要修改应用代码。

所有认证工作都由 Nginx 完成。


小结#

对于这类只允许自己访问的服务来说:

浏览器导航页
私人部署的密码库
某个服务的管理面板
NAS
各种自建服务等等

客户端证书是一种很适合的保护方式。

它不会依赖固定 IP。

也不会额外增加一层登录页面。

只要设备持有正确证书,就能访问服务。

否则连接会在进入应用之前被直接拒绝。

我最终采用的方案是:

Let's Encrypt
负责 HTTPS
自建 CA
负责客户端证书
Nginx
负责验证客户端证书
Sun-Panel
只负责提供服务

这样既保留了公网访问的便利性,也增加了一层额外的访问控制。

因为同一套 CA 和客户端证书可以被多个服务复用。

后续如果有新的自建服务需要暴露到公网,只需要为对应域名重新签发服务器证书即可。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

部分信息可能已经过时

目录