Certbot 报告续期成功,磁盘上文件的日期是新的,客户端却仍然遇到旧身份。SoftEther 把 SSL 服务器证书保存在自己的服务器级配置里,因此磁盘上的新文件在叶证书及其私钥被导入之前不会改变任何东西。下面的顺序涵盖一次手动导入、一个在每次续期后运行的封装脚本,以及一项读取监听端实际出示内容的检查。每一步都会留下可在下次续期后对照的证据。

续期后的文件不会改变服务器身份

签发与部署是两个独立事件,失败方式也各自不同。ACME 客户端把文件写入血缘目录并记录成功;SoftEther 则继续使用最后一次载入其配置的证书。所以退出码为零描述的是文件,而不是服务,只要没人查看,两者可以一直不一致。请把导入视为部署步骤,把续期视为它的输入。

这一区分也解释了令人困惑的「半失败」:旧证书会一直工作到过期,所以起初看起来什么都没坏。一旦过期,所有客户端同时失败,看起来像故障,而不像漏掉的部署步骤。在每次续期后检查所出示的身份,就能把这种意外变成例行观察。

选择适合监听端的验证方式

证书必须写明客户端所验证的主机名,因此要用公共 DNS 名称,而不是内部标签或地址。HTTP-01 需要验证请求在 80 端口上短暂被应答,这只是临时监听,绝不是移动 SoftEther 自身端口的理由。DNS-01 通过一条记录证明控制权,完全不需要入站 HTTP。

无人值守的续期意味着用限定在单个区域的凭据做自动化 DNS 集成,而不是每两个月手工输入一条 TXT 记录。通配符改变的是验证类型,而不是这种自动化的必要性。要在第一张证书存在之前就决定这一点,因为以后更换验证类型意味着同时改动客户端配置和防火墙。

趁在这里,把各类产物理清。叶证书与中间证书既以独立文件出现,也以合并的链文件出现,私钥又是单独的一份,而导入需要的是带匹配私钥的证书链,不是单独一张公开证书。

在服务器级导入证书链

在触碰证书之前,先备份服务器配置,并确认仍然能进入管理界面。要以服务器管理员的身份连接,而不是 Virtual Hub 管理员,因为证书属于服务器对象。然后用绝对路径载入证书链和私钥,这些文件必须能被执行命令的账户读取。注意路径是绝对的,因为命令会从运行 vpncmd 的那个进程的工作目录来解析它们:

ServerCertSet /LOADCERT:/etc/letsencrypt/live/vpn.example.com/fullchain.pem /LOADKEY:/etc/letsencrypt/live/vpn.example.com/privkey.pem

要读取命令输出,而不是假定成功;随后回读证书链,而不是信任你传进去的文件。整体导入 PEM 包与只取第一张证书的构建之间的界线并没有集中记载,所以眼前的这个构建要靠检查来定论。数一数监听端出示了几张证书;没有中间证书的叶证书,正是只消费了第一个证书块的导入器的特征。

让导入后的私钥只有运行服务器的账户可读,并把管理密码留在 shell 历史之外。这第一次导入时,请在提示处输入密码,而不是把它作为命令参数传进去。这两个习惯不花任何代价,却能消除证书部署泄露原本应受保护内容的最常见途径。要在第一次导入之前做,而不是之后。

挂接部署,而不是挂接续期

手动导入跑通之后,把这些步骤原样放进一个小封装脚本,并在续期成功后从 ACME 客户端调用它。Certbot 会为 hook 设置 RENEWED_LINEAGE 和 RENEWED_DOMAINS,因此在一台管理多张证书的机器上,封装脚本也能只针对一张证书动作。要按血缘过滤,而不是对主机上的每次续期都作出反应。封装脚本是以服务器权限运行的部署代码,所以要像对待该位置上的任何其他脚本一样审阅它。

certbot renew --deploy-hook /usr/local/sbin/softether-deploy.sh

无人值守运行的 hook 需要自己的受保护凭据存储,以及能触达人的失败信号。如果签发成功而导入失败,应当重试导入,而不是再申请一张证书,因为第二次申请会把部署缺陷藏在新文件背后。Certbot 的续期文档讲得很清楚:一次什么都没更新的运行是正常结果,因此正是 hook 用来区分这两种情况。

把出示的证书链与文件对照

要读监听端,而不是磁盘。用客户端使用的服务器名连接面向客户端的端口,然后比对指纹和实际交付的证书链与 hook 安装的血缘。两条命令即可覆盖,第一条还能看出中间证书到底有没有送达:

openssl s_client -connect vpn.example.com:443 -servername vpn.example.com -showcerts </dev/null 2>/dev/null | grep -c 'BEGIN CERTIFICATE'
openssl s_client -connect vpn.example.com:443 -servername vpn.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -dates -fingerprint -sha256

随后新建一次客户端连接,因为在导入之前建立的会话是按旧身份认证的,对新身份不能说明任何问题。如果客户端固定了旧的叶证书,那么即便公开证书链有效,它们也需要协同更新,而这件事与续期属于同一份操作手册。服务前面的反向代理可能出示一张好证书,而 VPN 监听端仍握着旧的,所以检查必须写明客户端真正到达的端口。把指纹记在续期日期旁边,让下一次比对成为一次检查,而不是凭记忆行事。

相关文章