邮件和其他协议

root certificate file does not exist:PostgreSQL verify-full

作者: · 最后核实

报错信息root certificate file "/home/app/.postgresql/root.crt" does not exist

这个报错的意思是:你要求 libpq 验证服务器(sslmode 为 verify-ca 或 verify-full),却没给它用来验证的 CA 文件。 同类报错 certificate verify failed、server certificate for "..." does not match host name "...",分别表示 CA 文件不对,或者证书不包含你用的主机名。

快速修复

修法是把签发服务器证书的 CA 交给 libpq,用 sslrootcert 指定文件路径:

psql "host=db.example.com dbname=app user=app sslmode=verify-full sslrootcert=/etc/ssl/certs/db-ca.pem"
  • 云数据库: 从云厂商文档或控制台下载 CA 证书。
  • 自建服务器 + 私有 CA: 用这个 CA 的根证书。
  • 服务器用公网受信任证书(客户端 PostgreSQL 16+): sslrootcert=system 使用操作系统信任库。

也可以把文件放到默认位置:~/.postgresql/root.crt(Windows 是 %APPDATA%\postgresql\root.crt)。

原因

libpq 验证失败有 5 种原因:没有 CA 文件、CA 不匹配、主机名不匹配、证书过期、服务器证书配置错误。前三种能从报错文字直接区分。

1. 没有 CA 文件

  • 确认: 就是上面这条报错,里面写着 libpq 检查的路径。
  • 修复: 提供 sslrootcert,或者把 CA 放到运行客户端那个用户的默认路径下。

2. CA 文件和服务器不匹配

  • 确认: 报 SSL error: certificate verify failed。查看服务器证书由谁签发:
openssl s_client -starttls postgres -connect db.example.com:5432 -servername db.example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -enddate
  • 修复: 使用该签发者的根证书。服务器证书是自签名的(subject 和 issuer 相同)时,这张证书本身就是 CA 文件。服务器没发中间证书时,把中间证书加到服务器的 server.crt 里,或者加进客户端 CA 文件。

3. 主机名不匹配(verify-full)

  • 确认: 报 server certificate for "db-primary.internal" does not match host name "203.0.113.10"。
  • 修复: 用证书 SAN 里有的名字连接,或者重新签发包含所有客户端会用到的名字和 IP 的服务器证书。用 IP 连接时,IP 必须以 IP SAN 的形式出现在证书里。云数据库要用控制台给出的连接地址(域名),不要直接用解析出来的 IP。

4. 服务器证书过期

  • 修复: 更新 server.crt,执行 SELECT pg_reload_conf();(重载时 PostgreSQL 会重新读取 SSL 文件)。

5. 服务器证书文件配置错误

  • 确认: 服务器日志显示 could not load server certificate file,或者 SSL 没开启。
  • 修复: postgresql.conf 里设置 ssl = on、ssl_cert_file、ssl_key_file;私钥要属于 postgres 用户、权限 600(或按文档要求属于 root 并给组读权限)。

各环境修法

各驱动的规则和 libpq 一致:sslmode 设为 verify-full,再用 sslrootcert 或驱动的 ca 参数指定 CA 文件。

JDBC(pgJDBC): jdbc:postgresql://db.example.com/app?sslmode=verify-full&sslrootcert=/path/db-ca.pem。

Python(psycopg): 传 sslmode="verify-full", sslrootcert="/path/db-ca.pem",规则和 libpq 一样。

Node.js(pg): ssl: { ca: fs.readFileSync("/path/db-ca.pem").toString(), rejectUnauthorized: true };除非覆盖 checkServerIdentity,驱动会检查主机名。

Docker / Kubernetes: 把 CA 作为 Secret 或 ConfigMap 挂载,设置环境变量 PGSSLROOTCERT 和 PGSSLMODE=verify-full。

查看连接实际用了什么: 在 psql 里执行 \conninfo,会显示是否使用 SSL 以及协议和加密套件;服务器端执行 SELECT * FROM pg_stat_ssl; 能看到哪些会话在用 TLS。

云数据库只读实例、代理地址: 只读实例、数据库代理的连接地址和主实例不同,确认每个地址都在证书覆盖范围内,或者按云厂商文档使用对应的 CA 和地址。

连接池(PgBouncer): 客户端到连接池、连接池到数据库是两段独立的 TLS(client_tls_* 和 server_tls_*),两段都要验证。

不要这样做

降级 sslmode 或关闭校验能消除报错,代价是客户端再也认不出冒充的数据库服务器。

  • 为了消除报错把 sslmode 降到 require 或 prefer。 加密还在,但失去了服务器身份验证,冒充的服务器可以截获账号密码。
  • 在 Node.js 里设 rejectUnauthorized: false,或其他驱动里的等价设置。 结果一样。
  • 为了躲开主机名不匹配,改用 IP 加 verify-ca。 应该修正证书里的名字。

常见问题

verify-ca 和 verify-full 有什么区别?

verify-ca 只检查服务器证书是否由你信任的 CA 签发;verify-full 还检查你连接用的主机名是否和证书的 SAN(或 CN)一致。只有 verify-full 能防住“持有同一 CA 签发的任意证书”的攻击者。

云数据库的 CA 文件从哪里下载?

阿里云 RDS、腾讯云 PostgreSQL、AWS RDS、Azure 等托管服务都在官方文档或控制台提供 CA 证书下载(阿里云、腾讯云一般在实例的“数据安全性/SSL”页面)。从官方页面下载,用 sslrootcert 引用。

sslmode=require 能防中间人吗?

单独使用时不能:它加密,但没有 CA 文件时不验证服务器。libpq 文档说明,为了向后兼容,如果存在根 CA 文件,require 的行为等同于 verify-ca。要防中间人,明确使用 verify-full。

怎么查看 PostgreSQL 出示的服务器证书?

OpenSSL 1.1.1 及以上:openssl s_client -starttls postgres -connect db.example.com:5432 -showcerts </dev/null。把结果贴到证书解析工具里查看名称和有效期。

资料来源

  1. PostgreSQL documentation: libpq SSL Support
  2. OpenSSL openssl-s_client (-starttls postgres)
  3. RFC 5280: Internet X.509 PKI Certificate and CRL Profile