1. PostgreSQL语境下的"证书"其实有两副面孔:加密证书与认证证书
做PostgreSQL运维这几年,被问得最多的问题里,"数据库证书有哪些"一定排得上号。有意思的是,这个问题背后,问的人往往说的是两件完全不相干的事——一部分人在排查连接报错,数据库连不上,日志里跳出一句SSL error,他们要找的是能让加密连接跑起来的数字证书;另一部分人在规划职业路线,想考本写着"PostgreSQL认证"的本本,好在简历上多一行。这两个需求要是被混在一起回答,两边都会觉得答非所问。
所以我先把口径掰开。在PostgreSQL的世界里,凡是和技术配置有关的"证书",九成指的是SSL/TLS数字证书,也就是加密连接时用来证明"你是你"、证明"对方是对方"的那套公钥基础设施文件;而凡是和职业发展有关的"证书",指的是各类培训机构或厂商发的资质证明。前者是运维和开发人员的日常,后者是入行与进阶的路径选择。
这篇文章我会把重点放在第一类证书上,从概念拆解到openssl实操生成、再到配置和排错,完整走一遍。等你把环境搭起来、踩过几个坑之后,第二类"证书"到底选哪种,自然就有了判断。无论你是刚开始接触PostgreSQL的运维新手,还是负责一处生产环境的TSG成员,这套东西都可以直接照着落地。
2. SSL证书体系里的三个角色:服务端、客户端、CA各管什么事
先说结论:一套完整的PostgreSQL SSL连接体系里,通常跑不掉三个文件角色——CA根证书、服务端证书、客户端证书。它们的分工用一句话概括:服务端证书证明数据库服务器的身份,客户端证书证明连接者的身份,CA根证书负责给前两者做担保。一旦理清这个三角关系,后面看到server.crt、root.crt这种文件名就不会再发怵了。
2.1 服务端证书和私钥:数据库的身份证
PostgreSQL服务器对外提供服务时,如果开启了SSL,会向客户端出示一张证书,这套文件在服务端通常叫server.crt和server.key。.crt是证书本体,相当于挂在胸前的身份证;.key是私钥,相当于只有本人持有的签名印章,绝对不能泄露,也不能被其他用户读取。
服务器收到客户端连接请求时,会先把server.crt发给对方,同时用server.key参与握手过程中的签名计算。客户端拿到证书后要做两件事:第一,检查这张证书是不是由自己信任的CA签发的;第二,验证服务器确实持有与证书配套的私钥。这两步都通过,加密隧道才真正建立。
这里必须提一个新手最容易误解的点:自签名证书能用来建SSL连接,但"能加密"不代表"可信"。一张自己拿openssl随便生成的server.crt,客户端在sslmode=require模式下能连上,但换成sslmode=verify-full就会直接报错——因为没有任何权威方替这张证书做担保。打个比方,别人递给你一张手写签名的纸条,上面写着"我是某某公司",你没法验证笔迹真伪;如果这张纸条经过公证处盖章,你对它的信任程度就完全不一样。CA签名就是数据库世界里的公证盖章。
2.2 CA根证书:信任链条的起点
CA根证书是整套体系里最重要的一环。它的名字在PostgreSQL服务端配置里通常是root.crt,配套的私钥是root.key。CA做的事很简单:用自己的私钥给他人的证书签上"担保"这层字。客户端或服务器在验证对方证书时,沿着签名链一直往上追,最终会找到CA根证书;只要自己预先信任了这张根证书,那么所有由它签发的下级证书都被视为可信。
实际部署中,如果你用的是云数据库厂商提供的实例,从控制台下载的那个"CA证书"文件,本质就是这个厂商的CA根证书。你把它的内容放到客户端的信任目录里,就能正常走verify-ca或verify-full的校验。我见过不少同事被"下载云数据库CA证书"这个操作搞晕,其实逻辑一句话讲完:你把厂商的根证书存进客户端的信任列表,之后它签发的所有实例证书你都会认。
2.3 客户端证书:双向认证场景下的第二张身份证
默认情况下,PostgreSQL连接时客户端只需要提供用户名密码,服务端不强制校验客户端的证书。但在安全要求较高的环境里,比如金融内网、政府项目,会改成"必须先出示客户端证书,验证通过后才算合法身份",这就叫双向认证,也叫mTLS。
客户端证书一般由和服务器同一个CA签发,文件名习惯叫client.crt、client.key。服务端通过ssl_ca_file指定root.crt,再用pg_hba.conf里的clientcert=verify-ca或verify-full决定校验强度。需要注意,PG客户端在默认情况下会去读用户目录下的~/.postgresql/postgresql.crt和~/.postgresql/postgresql.key,如果你用psql测试双向认证,需要把客户端证书放到这个位置,或者在连接串里显式指定路径。
三个角色放在一起,可以用下面这张表快速记住它们的分工:
| 文件类型 | 常见文件名 | 所在位置 | 保密性 | 作用 |
|---|---|---|---|---|
| CA根证书 | root.crt | 服务端与客户端各有信任副本 | 公开可分发 | 签发下级证书、作为信任锚点 |
| CA私钥 | root.key | 仅CA管理机 | 绝对机密 | 给服务端/客户端证书签名 |
| 服务端证书 | server.crt | 数据库数据目录 | 公开 | 证明数据库身份 |
| 服务端私钥 | server.key | 数据库数据目录 | 机密,仅数据库用户可读 | 完成握手签名 |
| 客户端证书 | client.crt/postgresql.crt | 客户端机器 | 公开 | 证明连接者身份 |
| 客户端私钥 | client.key/postgresql.key | 客户端机器 | 机密 | 完成客户端侧握手签名 |
3. 从零搭建一套可信证书体系:openssl生成、签发与配置全流程
理论说完,直接上手。下面我会以"自己搭建一台私有CA,用它给PostgreSQL服务端和客户端签发证书"为例,把完整步骤走一遍。这套方法在无法接入商业CA的机房内网里是标准做法,也适合学习和测试环境。
3.1 第一步:生成CA根证书与CA私钥
CA是整个信任体系的源头,它的私钥一旦泄露,整条信任链就塌了。所以我在生产环境里会把它放在单独的机器或者目录里,权限设成仅管理员可读。生成命令如下:
# 生成CA私钥 ca.key 和自签根证书 ca.crt,有效期设为10年 openssl req -new -x509 -nodes -days 3650 -newkey rsa:2048 \ -keyout ca.key -out ca.crt \ -subj "/CN=pg-local-ca" -sha256几个参数说明一下:
-newkey rsa:2048:同时生成新的私钥,长度2048位。内部环境这个强度够用;如果是公网生产,建议换成4096。-nodes:私钥不加密。很多人疑惑为什么不用des3加个口令——因为PostgreSQL服务器启动时是非交互式的,如果私钥有口令,服务起来后没法自动解锁,反而麻烦。安全和易用之间,这里选了易用,靠文件权限兜底。-x509:直接生成一个自签名的根证书,不需要再走一遍签发流程。-days 3650:根证书有效期10年。根证书过期意味着所有下级证书链全部失效,所以给长一点。
执行完后目录里会有ca.key和ca.crt。ca.crt可以分发给所有需要信任该CA的客户端和服务器,ca.key必须锁起来。
3.2 第二步:生成服务端证书并完成签发
服务端证书不能直接自签一下就完事,因为客户端的verify-full会校验主机名,这张证书必须和数据库服务器的域名或IP匹配。先为服务器生成一个私钥和CSR(证书签名请求):
# 生成服务端私钥和CSR,CN务必填数据库服务器实际可解析的主机名 openssl req -new -nodes -newkey rsa:2048 \ -keyout server.key -out server.csr \ -subj "/CN=db01.example.internal" \ -addext "subjectAltName=DNS:db01.example.internal,DNS:db01,IP:192.168.10.15"这里的-addext是OpenSSL 1.1.1以上才支持,把备用主机名和IP写进证书的SAN(Subject Alternative Name)字段。为什么要写SAN?因为现代OpenSSL和PG客户端在做verify-full时,优先匹配SAN里的主机名,而不只是证书的CN字段。如果你用的OpenSSL比较旧,不支持-addext,可以改成先创建server_ext.cnf文件:
subjectAltName = DNS:db01.example.internal, DNS:db01, IP:192.168.10.15然后用-extfile server_ext.cnf方式签发。
接下来用第一步的CA私钥对这个CSR签字,生成正式的server.crt:
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \ -CAcreateserial -out server.crt -days 825 -sha256 \ -extfile server_ext.cnf-CAcreateserial会在首次签发时生成一个ca.srl文件用于记录序列号,后续再签别的证书不需要重复加这个参数。有效期我习惯给825天,这个数字来自现代浏览器对TLS证书有效期的限制,数据库客户端没有硬性规定,但保持一致没坏处,而且825天约等于两年零三个月,方便按季度巡检。
签完之后把server.key和server.crt传给数据库服务器。server.csr留着签发记录需要时可以回溯,不保留也可以。
3.3 第三步:生成客户端证书,为双向认证做准备
如果不是必需,客户端证书可以暂缓。一旦你想开启双向认证,或者团队里有安全审计要求必须用证书证明连接者身份,再走这一步。生成逻辑和服务端一样,只是CN字段通常填数据库用户名或员工标识:
openssl req -new -nodes -newkey rsa:2048 \ -keyout client.key -out client.csr \ -subj "/CN=pgapp" \ -addext "subjectAltName=DNS:pgapp" openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key \ -out client.crt -days 825 -sha256 -extfile client_ext.cnf客户端侧使用时的默认路径和服务端不一样。PostgreSQL官方客户端(psql、libpq)默认会读~/.postgresql/root.crt、~/.postgresql/postgresql.crt、~/.postgresql/postgresql.key。你可以把ca.crt改名成root.crt,把client.crt改名成postgresql.crt,client.key改名成postgresql.key,放在连接用户的家目录里,最省事。
3.4 第四步:配置postgresql.conf并放置证书文件
把服务端证书放到数据库数据目录,注意路径和权限:
# 假设PGDATA的路径是 /var/lib/postgresql/15/main cp server.crt server.key /var/lib/postgresql/15/main/ chown postgres:postgres /var/lib/postgresql/15/main/server.crt /var/lib/postgresql/15/main/server.key chmod 600 /var/lib/postgresql/15/main/server.key chmod 644 /var/lib/postgresql/15/main/server.crt然后在postgresql.conf里打开SSL开关并指定证书路径:
ssl = on ssl_cert_file = 'server.crt' ssl_key_file = 'server.key' ssl_ca_file = 'root.crt'这里有个版本细节值得注意。9.6之前的PostgreSQL不允许自定义这三个文件路径,证书固定要叫server.crt、server.key、root.crt并且放在数据目录里;9.6及以后才支持上面的配置项。另外,PostgreSQL默认从9.6开始ssl=on,前提是编译时带了OpenSSL支持。如果你是自己从源码编译,一定要在configure阶段加上--with-openssl,否则无论配置写得多漂亮,启动时都会报SSL not supported。之前帮人排查过Ubuntu上源码编译的实例,费了半天劲发现是忘了这一个参数。
还有一个好消息:PostgreSQL 14以后的initdb在检测到OpenSSL可用且数据目录里没有现成证书时,会自动生成一张自签名证书,保证开箱即用。但请注意,那张自动生成的证书是"能用"不是"可信",生产环境还是要换成自己CA签发的证书。
3.5 第五步:改pg_hba.conf并完成连接验证
证书放好后,还要在客户端认证配置文件pg_hba.conf里告诉PG:"哪些客户端必须走SSL、是否强制出示客户端证书"。比如只允许内网段走SSL加密,并且要求出示客户端证书:
# TYPE DATABASE USER ADDRESS METHOD hostssl all all 192.168.10.0/24 clientcert=verify-fullhostssl表示该规则仅匹配SSL连接,非SSL连接在这个网段直接拒绝。clientcert=verify-full表示客户端必须提供一张由ssl_ca_file指定的CA签发的证书,且证书CN还要和数据库用户名匹配。如果只想验证"证书确实由我们CA签发"、不强制和用户名绑定,用clientcert=verify-ca即可。这个区别是高安全场景下的关键分水岭,别选错了级别。
改完配置后需要区分两种重载方式:pg_hba.conf的改动用SELECT pg_reload_conf();或pg_ctl reload就能生效;而postgresql.conf里ssl相关参数改动则必须重启实例。很多人在这上面栽过跟头,改了ssl后只reload然后发现没生效,其实日志里会有一行提示。
验证阶段我习惯做三层检查。首先是服务端SSL是否真的在监听:
openssl s_client -connect 192.168.10.15:5432 -CAfile ca.crt -brief这条命令如果输出CONNECTION ESTABLISHED且没有验证告警,说明服务端证书、私钥权限和CA链都没问题。然后从客户端用psql实际连一次:
psql "host=192.168.10.15 port=5432 user=pgapp dbname=appdb sslmode=verify-full sslrootcert=ca.crt"这里sslmode=verify-full是客户端侧的最高校验级别,它会同时验证证书链有效性和服务器主机名匹配。连接成功后执行\conninfo,输出里看到SSL connection (protocol: TLSv1.3, cipher: ...)就说明加密隧道已经建起来了。
4. 证书在真实环境里的高频坑:权限、有效期、主机名、签名链
搭建一次成功的证书环境只是开始,真正考验人的是日常运维。下面这些坑我几乎每隔一段时间就会遇到,每个都有血泪教训。
4.1 私钥权限不过关,服务就是起不来
PostgreSQL对私钥文件的权限检查非常严格,服务器启动时如果发现server.key的权限比600更宽松,或者属主不对,会直接拒绝启动。典型的报错是:
FATAL: private key file "server.key" has group or world access DETAIL: File must have permissions u=rw (0600) or less if it is owned by the database user.处理方式刚才第三步里已经写了:chmod 600 && chown postgres:postgres。这里多说一句为什么PG这么较真——私钥一旦能被其他用户读取,等同于把服务器的身份证和印章同时交了出去,任何人都可以伪装成数据库服务器骗取客户端连接。权限误设后服务起不来,其实是PG在替你拦一道。
4.2 主机名校验失败:证书里写着A,连接却用了B
我接过一个典型的工单,现象是应用端突然连不上PostgreSQL,日志里报:
server certificate does not match host name "192.168.10.15"排查链路是这样的:先用openssl s_client -connect看服务器实际出示的证书CN,发现里面写的是db01.example.internal;再看应用连接串,host写的却是IP192.168.10.15。根因马上清楚——客户端用的是sslmode=verify-full,它会严格校验连接主机名和证书中的CN/SAN是否一致。
处理方向有两个:最彻底的是把证书重签一份,SAN里加上实际访问用的IP和别名;如果短期内不方便重签,就在连接主机上把host改成证书里的主机名,并配合内部DNS或hosts文件做解析。归根结底就是一句话:verify-full校验的"full"指的就是主机名这一层,证书签发之前必须先确认客户端将来用什么名字访问,否则证书就是废纸。
4.3 证书到期那几天,运维最容易出事
证书有效期到了之后,最典型的表现就是客户端开始随机报SSL certificate expired或server certificate verification failed。为什么说随机?因为不同客户端库里对证书过期时间的检查严格程度不一样,有的提前一个月就开始警告,有的到了当天才拒绝。
我的习惯是给证书建一个巡检脚本,放进cron里每天跑一遍,到期前60天就发告警:
#!/bin/bash CERT=/var/lib/postgresql/15/main/server.crt ENDDATE=$(openssl x509 -in "$CERT" -noout -enddate | awk -F= '{print $2}') END_EPOCH=$(date -d "$ENDDATE" +%s) NOW_EPOCH=$(date +%s) LEFT=$(( (END_EPOCH - NOW_EPOCH) / 86400 )) if [ "$LEFT" -lt 60 ]; then echo "PostgreSQL server.crt will expire in ${LEFT} days: $CERT" fi脚本逻辑很简单:读出证书的到期时间,换算成剩余天数,低于60天就输出告警。你也可以拿去接Zabbix、Prometheus或者自研的告警平台。证书到期这种问题,不发生则以,一发生永远是深夜加急工单。
4.4 双向认证里,客户端证书的路径和权限同样敏感
开启clientcert=verify-full之后,服务端会强行要求客户端出示证书。如果客户端没配置,报错通常是:
FATAL: connection requires a valid client certificate如果是psql,检查~/.postgresql/下有没有postgresql.crt和postgresql.key,以及root.crt是否存在。这里要注意几点:客户端私钥postgresql.key的权限同样必须设成600,否则PG客户端会拒绝读取;root.crt的内容必须和服务端信任的是同一份CA;如果连接串里已经通过sslcert/sslkey指定了路径,那么优先级高于默认目录。
另外,clientcert=verify-full要求证书CN等于数据库用户名,这个坑最隐蔽。假设你的数据库用户是pgapp,证书CN却写成了application,握手时PG会认为证书主体与登录用户不匹配,给出一条不那么容易看懂的拒绝日志。这种问题靠表面排错很难定位,我的经验是反过来核对:先用openssl x509 -in client.crt -noout -subject看CN,再和pg_hba.conf里的用户名映射对一下,问题立现。
4.5 常见报错速查表
与其在文档里翻半天,不如直接对照下表定位:
| 报错或现象 | 可能原因 | 优先检查顺序 |
|---|---|---|
FATAL: private key file ... has group or world access | server.key权限过大 | 检查文件权限,改为600 |
server certificate does not match host name | 证书CN/SAN与连接host不一致 | 查openssl s_client输出,重签或修改host |
connection requires a valid client certificate | 客户端未配置证书或证书不可信 | 查客户端目录文件、root.crt、连接串路径 |
SSL error: certificate verify failed | CA链不完整或自签名证书未被信任 | 检查sslmode、sslrootcert路径 |
SSL not supported | 编译未带OpenSSL或版本过旧 | 检查configure参数,重新编译安装 |
certificate expired | 证书到期 | 查看openssl x509 -enddate,立即重签 |
5. 想考"能写进简历的证书"?先看清楚PostgreSQL认证的现状
聊完技术证书,回到开篇提到的第二种"证书"。如果你搜这个问题的目的是想知道"考什么证能证明自己会PostgreSQL",我得先泼一盆相对实在的冷水。
5.1 官方并没有一套统一的PostgreSQL认证考试
和Oracle的OCP、MySQL的官方认证体系不一样,PostgreSQL官方社区目前并没有推出由厂商背书的全球统一考试。你在postgresql.org官网上找不到"报名考试拿证书"的入口,更多的情况是:国内外的培训机构、部分数据库厂商基于PostgreSQL推出自己的培训课程,结课后发一张"PostgreSQL认证工程师"或类似名字的证明。这些证明有没有用?可以说有用但又没那么有用——有用在于它能推动你把PostgreSQL系统地学一遍,尤其是那些包含实战演练的课程;没那么有用在于,面试官大概率不会因为一张培训证书就认定你的能力,他更可能随手抛出一个问题:"你解释一下PG的主从复制原理",或者"你线上怎么处理一个CPU打满的会话"。
5.2 甄别培训课程含金量的三个标准
如果一定要选一个培训证书,我建议看三点。第一,课程大纲里有没有源码级或者参数级的内容,比如shared_buffers、work_mem、checkpoint这些调优参数是不是讲清楚了;第二,课程里有没有让你动手搭环境,包括二进制安装甚至源码编译、流复制配置、备份恢复演练,而不是全程看PPT;第三,讲师是不是在一线做数据库相关工作的人,你可以试着问一个生产环境的冷门问题,答得是否干脆利落,就基本知道水平了。
我个人见过不少包装得很好看的培训项目,课程目录长得像百科全书,实际讲课时却跳过最难的备份恢复和高可用部分,因为这两块最考验讲师经验。一张学了三天就发的证书,含金量很难高过你亲手搭一套PG高可用环境的经历。
5.3 比证书更能说明问题的事情
与其纠结考哪个证书,不如把时间花在能留在简历和公开仓库里的实打实的东西。比如:独立完成过一套PostgreSQL高可用集群的搭建与切换演练,拿得出拓扑设计和failover记录;在社区或开源项目里有代码贡献,哪怕只是文档改进和Bug修复;写过几篇有深度的排错复盘文章,能讲清楚一条慢SQL从捕获到优化的完整链路。另外,如果你本身就在云厂商环境中工作,云厂商推出的数据库专项认证含金量会更高一些,毕竟它贴近真实企业使用场景,而且考试内容覆盖面广,包含高可用、备份恢复、性能优化等硬核板块。
说到学习路径,我建议按这个顺序推进:先解决安装部署(二进制、源码引入都试一遍),再吃透基础运维(psql日常操作、备份恢复、日志排查),接着挑战高可用(流复制、Patroni这类管理工具),最后才是性能调优(EXPLAIN ANALYZE、索引设计、参数调整)。把这四步走完,你对PostgreSQL的理解已经完全不需要靠一张纸来背书——就像文章里这一整套SSL证书体系的搭建过程,真正动手走过一遍,就已经比很多只拿着一张培训结业证的人更能解决实际问题了。
我在实际运维中还有一个习惯,就是所有证书操作都留档记录:谁在什么时间签发了哪张证书、有效期到什么时候、覆盖了哪些主机,全部写进变更记录。别小看这一步,等证书批量到期,或者需要追溯某台机器为什么不被客户端信任的时候,这份记录能帮你省下大把时间。生产环境的证书体系,搭建只是第一天的事,之后的每一轮更新、每一次换人维护,才真正决定这套体系能走多远。