composer如何设置自定义CA证书用于HTTPS私有仓库?(ssl.cafile配置)

2次阅读

composerssl.cafile 必须指向完整 pem 格式证书链文件,含根 ca 与中间 ca,顺序为中间 ca 在前、根 ca 在后,且需全局配置(–global)并确保 php 进程有读取权限。

composer如何设置自定义CA证书用于HTTPS私有仓库?(ssl.cafile配置)

composer config ssl.cafile 指向的证书必须是 PEM 格式合并文件

很多团队把公司内网 CA 的 .crt.pem 直接丢进 ssl.cafile,结果 composer 仍报 curl Error 60: SSL certificate problem。根本原因是:Composer 要求该路径指向一个「完整、可被 OpenSSL 识别的 PEM 文件」,它得包含完整的证书链(根 CA + 中间 CA),且不能混入非 PEM 内容(比如注释、空行、Base64 外的文本)。

实操建议:

  • openssl x509 -in your-ca.crt -text -noout 确认文件确实是 PEM 编码(以 -----BEGIN CERTIFICATE----- 开头)
  • 如果拿到的是 windows.cer 或二进制 DER,先转成 PEM:openssl x509 -inform DER -in your-ca.cer -outform PEM -out ca.pem
  • 若私有仓库用了中间 CA,需把根 CA 和中间 CA 合并到一个文件(顺序:站点证书 → 中间 CA → 根 CA),但 composer 只认 CA 部分,所以只放中间 + 根即可
  • 别用编辑器手动拼接——换行符或 bom 会导致解析失败;用 cat intermediate.pem root.pem > combined.pem 更可靠

全局设置 vs 项目级设置:优先用 –global 配置

很多人在项目根目录跑 composer config ssl.cafile /path/to/ca.pem,结果其他项目或 CLI 下的 composer 命令仍不生效。这是因为该命令默认只改当前项目的 composer.json,而 https 仓库认证发生在全局网络层,必须让所有 composer 进程都加载这个 CA。

实操建议:

  • 统一走全局配置:composer config --global ssl.cafile /etc/ssl/certs/company-ca.pem
  • 确认生效:composer config --global --list | grep ssl.cafile,输出应为实际路径
  • 如果公司强制要求项目隔离(比如多客户环境),才考虑项目级配置,但必须确保 composer install 时未启用 --no-plugins(某些自定义插件会干扰 CA 加载)
  • 注意权限:PHP 进程需有读取该文件的权限,尤其是用 nginx/php-fpm 时,www-data 用户可能无权读取家目录下的文件

composer 2.5+ 对 ssl.cafile 的加载逻辑变了

旧版(ssl.cafile 直接透传给 cURL;新版(≥2.5)会先尝试用它初始化 OpenSSL 上下文,再 fallback 到系统 CA 路径。这意味着:即使你配了 ssl.cafile,如果文件路径错、格式错、或权限不足,composer 不报明确错误,而是静默忽略,继续用系统默认 CA——然后继续报 60 错误,让人误以为“配置没生效”。

实操建议:

  • 加个快速验证步骤:php -r "var_dump(openssl_get_cert_locations());",看 default_cert_file 是否包含你设的路径(新版不会显示,但可确认 OpenSSL 本身能否读取)
  • 临时加日志:运行 composer -v install,观察是否有 Reading CA bundle from ... 类似提示(有则说明路径被识别,无则大概率路径无效)
  • 绕过验证仅用于排查:composer config --global secure-http false(⚠️仅调试用,切勿提交或长期开启)

私有 Packagist 服务(如 Satis、Private Packagist)还需额外处理

单纯配 ssl.cafile 只解决「下载包时的 HTTPS 连接」,但如果你还用了自建的 Packagist 镜像(比如 Satis),它的 repositories 元数据本身也是通过 HTTPS 拉取的——这部分请求由 Composer 自己发起,也走同一套 CA 链,但有个隐藏坑:Satis 生成的 packages.json 若含 dist.url 字段,且该 URL 是 HTTP(而非 HTTPS),composer 会跳过 CA 校验,导致你以为“CA 配好了”,其实根本没走 HTTPS 流程。

实操建议:

  • 检查 packages.json 里所有 dist.url 是否真为 https:// 开头,不是 http:// 或相对路径
  • Satis 配置中显式指定 "secure-http": true,并在 output-dir 部署后,用 curl -I https://your-satis/repo/packages.json 确认能通且返回 200
  • 若用 Nginx 反代 Satis,确保 proxy_ssl_trusted_certificate 指向同一份 combined.pem,否则反代层就校验失败了

最常被忽略的一点:证书文件路径在 Docker 容器里要映射进去,宿主机配了 --global ssl.cafile,但容器里 PHP 根本看不到那个路径——这时候光配 config 没用,得挂载 + 重新 config。

text=ZqhQzanResources