身份认证
PostgreSQL 使用 pg_hba.conf 进行 基于主机的认证(Host-Based Authentication):谁(用户)、从哪里(来源地址)、访问什么(数据库)、需要以何种方式证明身份(认证方法)。
这套机制足够强大,但在集群环境中手工维护的成本很高:主库与从库可能需要不同规则,配置文件又分布在每个实例的数据目录中。 如果缺少统一声明和刷新流程,各实例的规则很容易发生漂移。
Pigsty 的答案与 声明式配置 一脉相承:HBA 规则是配置清单的一部分,由剧本统一渲染与下发。
HBA 即代码
集群的 HBA 规则由两组参数拼接而成:全局默认规则 pg_default_hba_rules 与集群自定义规则 pg_hba_rules;
PgBouncer 连接池 另有独立的两组对应参数(pgb_default_hba_rules 与 pgb_hba_rules)。
每条规则可以用两种形式书写。别名形式 是推荐的方式,一条规则一行,语义一目了然:
pg_hba_rules:
- { user: dbuser_app ,db: app ,addr: 10.1.0.0/16 ,auth: ssl ,order: 50 ,title: 'app user access via ssl' }
原始形式 则直接给出 pg_hba.conf 的原文,用于表达别名覆盖不了的特殊规则。
除了四要素之外,规则还有两个控制字段:
order:渲染顺序。HBA 按“先匹配先生效”的原则工作,顺序即优先级。约定0-99保留给用户的高优先级规则,100-999是默认规则集,未指定order的规则排在最后。role:实例角色过滤。common与default规则对所有实例生效;primary、replica、offline、standby、delayed规则仅在对应角色的实例上启用;role: offline的规则还会额外下发给标记了pg_offline_query的实例。同一份声明渲染到不同实例,得到的是各自角色对应的规则 —— 主从差异不再需要手工维护。
修改声明后,使用封装好的脚本应用变更,规则会被重新渲染并重载生效:
bin/pgsql-hba pg-meta # 重新渲染并应用 pg-meta 集群的 HBA 规则
pg_hba_rules 用于追加规则,不会自动收窄范围更宽的默认规则。需要建立更严格的边界时,应同时审查 pg_default_hba_rules,并在变更后检查各实例实际生成的 pg_hba.conf。
地址与认证别名
别名形式的价值在于把常见场景抽象为语义化的词汇。addr 字段的别名展开为具体的地址块:
| 别名 | 展开为 | 含义 |
|---|---|---|
local | Unix Socket | 仅本地套接字 |
localhost | Unix Socket、127.0.0.1/32 与 ::1/128 | 本机 |
admin | <admin_ip>/32 | 管理节点 |
infra | 各 INFRA 节点的 /32 地址 | 基础设施节点 |
cluster | 集群各成员的 /32 地址 | 集群内部 |
intra | 10.0.0.0/8、172.16.0.0/12、192.168.0.0/16 | 内网网段,可通过 node_firewall_intranet 定制 |
world | 0.0.0.0/0 与 ::/0 | 任意地址 |
| CIDR 地址 | 原样保留 | 自定义网段 |
auth 字段的别名决定认证方法,以及是否强制 TLS 连接:
| 别名 | 认证方法 | 说明 |
|---|---|---|
deny | reject | 显式拒绝 |
trust | trust | 无条件放行,慎用 |
pwd | scram-sha-256 或 md5 | 依 pg_pwd_enc 而定,默认 SCRAM |
sha | scram-sha-256 | 强制 SCRAM |
md5 | md5 | 兼容旧客户端 |
ssl | hostssl 与密码认证 | 密码认证,且必须走 TLS |
ssl-sha | hostssl 与 scram-sha-256 | TLS 与强制 SCRAM |
cert | hostssl 与 cert | 客户端证书认证 |
ident、os | ident(PgBouncer 中为 peer) | 操作系统用户映射 |
peer | peer | 本地操作系统用户 |
用户字段支持四个占位符,渲染时替换为实际用户名:${dbsu}(超级用户)、${repl}(复制用户)、${monitor}(监控用户)、${admin}(管理用户);
+role 前缀表示匹配该角色的所有成员。
这里还要区分两件事:auth: ssl 只要求连接使用 TLS,并不要求客户端验证服务端身份。安全敏感的客户端还应使用 sslmode=verify-full 和可信 CA,详见 加密通信。
默认规则解读
Pigsty 的默认 HBA 规则集体现了一个简单的原则:来源越远,要求越严。以下是 PostgreSQL 侧的默认规则(源码原文):
pg_default_hba_rules: # postgres default host-based authentication rules, order by `order`
- {user: '${dbsu}' ,db: all ,addr: local ,auth: ident ,title: 'dbsu access via local os user ident' ,order: 100}
- {user: '${dbsu}' ,db: replication ,addr: local ,auth: ident ,title: 'dbsu replication from local os ident' ,order: 150}
- {user: '${repl}' ,db: replication ,addr: localhost ,auth: pwd ,title: 'replicator replication from localhost',order: 200}
- {user: '${repl}' ,db: replication ,addr: intra ,auth: pwd ,title: 'replicator replication from intranet' ,order: 250}
- {user: '${repl}' ,db: postgres ,addr: intra ,auth: pwd ,title: 'replicator postgres db from intranet' ,order: 300}
- {user: '${monitor}' ,db: all ,addr: localhost ,auth: pwd ,title: 'monitor from localhost with password' ,order: 350}
- {user: '${monitor}' ,db: all ,addr: infra ,auth: pwd ,title: 'monitor from infra host with password',order: 400}
- {user: '${admin}' ,db: all ,addr: infra ,auth: ssl ,title: 'admin @ infra nodes with pwd & ssl' ,order: 450}
- {user: '${admin}' ,db: all ,addr: world ,auth: ssl ,title: 'admin @ everywhere with ssl & pwd' ,order: 500}
- {user: '+dbrole_readonly',db: all ,addr: localhost ,auth: pwd ,title: 'pgbouncer read/write via local socket',order: 550}
- {user: '+dbrole_readonly',db: all ,addr: intra ,auth: pwd ,title: 'read/write biz user via password' ,order: 600}
- {user: '+dbrole_offline' ,db: all ,addr: intra ,auth: pwd ,title: 'allow etl offline tasks from intranet',order: 650}
逐层来看:
- 本地最受信任:超级用户
postgres只能通过本地 Unix Socket 以ident方式进入 —— 不需要密码,但也无法在远程使用。这就是为什么 dbsu 默认不设密码:不存在可以被窃取的口令。 - 内网次之:复制与业务账号在内网使用 SCRAM 口令认证;监控和管理用户的远程访问主要面向 INFRA 节点。
- 公网最严:默认只有管理员可以从任意地址访问,且必须同时提供口令与 TLS 连接。
PgBouncer 侧的默认规则更保守一层:监控与管理账号从公网访问连接池会被显式 deny,业务用户则限定在本机与内网。
需要特别说明,默认 +dbrole_offline 规则没有设置 role,因此会应用到所有实例。要把离线用户限制到 pg_role: offline 或设置了 pg_offline_query: true 的实例,必须在对应 HBA 规则上显式增加 role: offline。
这份默认规则集是“可用性优先”的取舍:业务账号在内网使用口令认证即可接入。
ha/safe 模板将主要 TCP 规则改为 ssl,管理员从非内网位置访问必须持有客户端证书(cert);本地 ident 与部分 localhost 口令规则仍然保留。
密码策略
Pigsty 默认使用 PostgreSQL 官方推荐的 scram-sha-256 算法存储密码(pg_pwd_enc),仅在需要兼容老旧客户端时才应降级为 md5。
密码处理链路会在执行 ALTER USER ... PASSWORD 前临时关闭语句日志(SET log_statement TO 'none'),避免口令进入 PostgreSQL 日志。
但明文口令仍会出现在配置清单中,渲染后的用户 SQL 也会以 0640 权限写入 /pg/tmp/pg-user-<name>.sql;相关任务没有完整使用 Ansible no_log。因此应限制管理节点、配置仓库和自动化输出的访问,并避免对含凭据任务使用 --diff。
密码强度默认不做强制,需要时可以预加载 passwordcheck 扩展,或使用规则更丰富的 credcheck 扩展:
pg_libs: '$libdir/passwordcheck, pg_stat_statements, auto_explain' # 拒绝弱密码
ha/safe 模板显式设置了上述 pg_libs;单独选择 CRIT 参数模板不会自动加载 passwordcheck。
账号有效期通过用户定义中的 expire_in(自创建起天数)或 expire_at(截止日期)声明,配合组织的密码轮换制度使用:
pg_users:
- { name: dbuser_app ,password: '<独立随机口令>' ,roles: [dbrole_readwrite] ,expire_in: 365 }
证书认证
口令终究可能被钓鱼、复用或撞库。对管理员这样的高权限账号,可以在 HBA 中使用 auth: cert 要求 客户端证书认证:
客户端必须持有由本地 CA 签发、CN 与数据库用户名一致的证书才能建立连接。在 HBA 仅接受 cert 的前提下,单独泄露口令不足以通过认证。
使用内置的 cert.yml 剧本签发客户端证书:
./cert.yml -e cn=dbuser_dba # 为 dbuser_dba 签发客户端证书,默认有效期 20 年
./cert.yml -e cn=dbuser_dba -e expire=365d # 或指定较短的有效期
签发的证书位于 files/pki/misc/<cn>.key 与 files/pki/misc/<cn>.crt。客户端私钥应通过受控渠道交付;客户端仍需使用 verify-full 验证数据库服务端,证书体系详见 加密通信。
连接池与组件 API
数据库本体之外,还有两类入口需要认证:
PgBouncer 连接池 使用独立的 HBA 规则集与用户列表。默认关闭 pgbouncer_auth_query,此时只有声明了 pgbouncer: true 的用户才会进入 userlist.txt 并通过连接池认证;启用动态认证查询后,应重新评估可登录用户范围。
Patroni REST API 承载高可用控制指令(重启、切换、重载配置),写操作要求 HTTP Basic 认证(patroni_username 与 patroni_password),
且来源受地址白名单限制;启用 patroni_ssl_enabled 后 API 全程走 HTTPS。
Grafana、HAProxy 管理界面、MinIO、etcd 等组件的凭证同样在配置清单中声明,完整清单与修改方式见 合规实践。