跳到主要内容
面板说续期成功,浏览器说没有——CyberPanel 的假绿灯,一条命令拆穿

面板说续期成功,浏览器说没有——CyberPanel 的假绿灯,一条命令拆穿

一键三连
一键三连

· 阅读约 4 分钟

例行检查 SSL 续期这个动作,你多久做一次?我大概每两个礼拜上 CyberPanel 看一眼,绿灯亮着,心里默念“没事了”,关掉。就这个动作,平均每次花掉我五分钟——五分钟全是心理安慰,啥也没验证。

上个月那一眼差点把我坑了。

绿灯是真的亮。面板上清清楚楚写着续期成功,日期都给你更新好了。我那天不知道哪根筋搭错,想亲眼看看证书长啥样,跑了一条:

openssl s_client -connect example.com:443 -servername example.com 2>&1 | grep "notAfter"

日期没动。跟面板上写的差了快一个月。浏览器这时候已经在给访问者弹“即将过期”的警告了,面板还在跟我表演“一切正常”。

这世上最害人的东西就是绿灯。

排查了一圈,根子不在 CyberPanel 的续期逻辑——CyberPanel 压根不实现 ACME 协议,它把这事儿完全委托给 acme.sh。acme.sh 跑完说成功,CyberPanel 就照单全收显示续期成功。问题出在 acme.sh 这边:这台服务器上那个域名的证书颁发机构,是 Let's Encrypt 的 staging 测试环境,不是 production。staging 签出来的证书,校验流程跟生产环境长得一模一样,acme.sh 高高兴兴返回 0,面板高高兴兴亮绿灯,就是浏览器不认。白折腾一整轮,该过期的还是到期。

这个骗局最缺德的地方在于:你从面板这一侧看,一切正常,你没有任何理由怀疑。唯一的破绽在外面——你得真的去问服务器要一张证书看看,才能发现它拿出来的那张是假的。

修复倒是不难。把 acme.sh 强制切回生产 CA,删掉现有的 staging 注册,重新签发,装回原来的位置,重载 OpenLiteSpeed:

acme.sh --set-default-ca --server letsencrypt
acme.sh --remove -d <domain>
acme.sh --issue -d <domain> --webroot /home/<domain>/public_html
acme.sh --install-cert -d <domain> \
  --key-file /etc/letsencrypt/live/<domain>/privkey.pem \
  --fullchain-file /etc/letsencrypt/live/<domain>/fullchain.pem
systemctl reload lsws

⚡真正起作用的是第一行——--set-default-ca。这一下把 acme.sh 从 staging 拽回 production,之后它签出来的每一张证书都是浏览器认的。我头一回跑完这条命令重新检查日期,看到 notAfter 从“下个月”跳成“明年”,那种感觉,怎么说,回不去了。面板的绿灯,从那天起我才敢真的信。

当然,信归信,例行检查这件事本身还是反人类的。所以我顺手写了两个脚本,把这事儿从“手动确认”变成“跑一下就行”。

第一个叫 renew-ssl.sh,绕开 CyberPanel 自己的调度器做例行程式续期。支持自动模式(默认提前 10 天续)、单网域模式,还有个 --check 模拟运行只打印不执行。核心逻辑跟 acme.sh 的命令行没差,但它把该先做的一步提前做了:签发之前先检查这个网域是不是还挂在 staging 名下,是的话先切回去——上次那种破事,以后不用等我发现,脚本自己就知道不对劲。

第二个叫 fix-ssl.sh,用来修复已经损坏或者卡在 staging 的证书。签发前会用 HTTP-01 挑战路径测试文件验证 webroot 真的能访问,避免你对着一个根本写不进去的目录折腾半天才发现问题;操作前会把现有证书和 acme.sh 注册信息都备份一遍,出事儿能回滚;装完证书重载 OpenLiteSpeed 之后,脚本会对比服务器实际下发的证书和刚安装的证书的 SHA256 指纹。这一步是我自己加的——因为“装好了”和“真的在服务新证书”是两回事,指纹对上了才是真的。

这俩脚本都扔 GitHub 上了。要求:CyberPanel 2.x、OpenLiteSpeed、acme.sh 在 /root/.acme.sh/、root 权限。fix-ssl.sh 额外用到 curl 做 webroot 检查,但没有 curl 的时候只会跳过一个检查给个 warning,不会直接崩。

老实说,这俩脚本解决的是“面板骗你”的问题,不是“证书过期”的问题——证书改续还是得续,但从此以后“续没续上”这件事,你不用再信面板的绿灯了。

上次那个绑定事件之后我就学乖了:凡是面板说“成功”的,我都要找一条外部路径自己验一遍。这套检查固化进脚本花了我大概一个下午——写脚本的时候骂骂咧咧,但想想以后每两礼拜省下那五分钟的核对确认,半年也回本了,还不算上哪天被 staging 证书坑去排查的半天。

你有更快的验证方式吗?教教我。我这条说到底还是笨办法,就是跑一遍真的去问服务器要证书看。你要是有什么一步到位的手段,留言区留给你,我去抄。