HAProxy 无法加载证书:fullchain.pem 不包含私钥
HAProxy 报 unable to load SSL private key,先确认 fullchain 与匹配私钥的加载方式。保留文件权限、链、候选校验及续期部署流程。
HAProxy 的 bind ... ssl crt ... 启动时报 unable to load SSL private key from PEM file,而所指定的文件是 Certbot 的 fullchain.pem。先确认加载路径里是否真的有对应私钥:fullchain 是叶证书与中间证书链,不是“证书和私钥都齐全”的文件。
本文说明普通 PEM 文件的加载方式。HSM、外部密钥和专门的证书存储配置需要按各自部署处理;也不要将缺少私钥的错误与证书过期、客户端不信任链混为一类。
确认 HAProxy 实际加载了哪个文件
查看当前启动配置中的 crt 路径,核对它与证书名称、文件挂载和权限。容器内的路径必须指向容器实际能读取的文件,仅在宿主机生成一个同名文件并不代表已经挂载进去。
Certbot 的同一证书目录通常包括 cert.pem、chain.pem、fullchain.pem 和 privkey.pem。私钥要与叶证书匹配,不能从另一个域名目录随手取来拼接。目录名可能是证书名称而非单个域名,还要核对续期使用的实际对象。
较新的 HAProxy 支持从证书路径附加 .key 的文件中加载分离私钥,自动发现还受到 ssl-load-extra-files 等配置影响。不能把“所有版本都只能合并 PEM”当成规则。本文选择明确的合并文件方式;已经采用分离密钥或 crt-store 的部署,应检查自己的加载配置,不要同时引入两套来源。
生成包含证书链与匹配私钥的新文件
保存当前可用的证书文件与配置,备份同样应限制访问。下面是由有权限的管理员在受保护目录内生成候选文件的结构示例;目录必须事先准备好,输出路径不能覆盖正在使用的文件,也不能已经存在需要保留的内容:
umask 077
cat /etc/letsencrypt/live/example.com/fullchain.pem \
/etc/letsencrypt/live/example.com/privkey.pem \
> /etc/haproxy/certs/example.com.pem.new
example.com 和输出路径需替换。这个文件包含私钥,应只允许获准的服务加载账户访问;umask 控制新文件权限,不会修正已存在文件的权限。还要确认每个 PEM 块边界正确、文件生成过程成功且没有只写入一半。
不要把合并文件放进 Web 可访问目录或普通共享日志,也不要为排查贴出私钥内容。去掉中间证书链不能补出缺少的私钥;只有叶证书加密钥能被加载,也不代表客户端可以验证完整链。
加密私钥、权限拒绝、格式损坏或证书与密钥不匹配,会产生另外的加载问题。应根据完整错误继续核对,不能为让服务启动就默认移除密码或放宽目录权限。
校验后加载,续期也要更新合并文件
将候选文件接入待加载的配置后,可以使用配置检查,下面仍是命令示例:
haproxy -c -f /etc/haproxy/haproxy.cfg
检查账户、完整配置文件集合与实际服务应一致。它检查配置和加载材料,不会启动监听服务;通过后才按现有服务管理方式加载,并核对公网提供的证书域名、有效期与链。
Certbot 更新自己的证书目录,不会自动重新生成一个独立合并文件。需要把生成、权限检查、配置检查和服务加载纳入原续期部署流程;部署 hook 只在成功续期后运行更合适。失败时保留当前可用版本,不能把半成品替换为正式文件。
如果新证书加载或访问异常,恢复先前匹配的证书、私钥与配置,再按原方式加载。验收需要同时满足 HAProxy 无私钥加载错误、客户端正常完成 TLS 验证、后续续期能更新服务实际使用的证书;仅仅确认 fullchain.pem 已变新还不够。


