23.2 认证与连接准入
客户端拿到数据库连接之前,至少要连续通过五道关:
这五道关回答的问题不同。防火墙放行不等于 HBA 放行,HBA 选中
scram-sha-256 不等于密码正确,认证成功也不等于角色拥有
CONNECT,更不等于它可以读取业务表。安全评审必须逐层给出证据,不能用
“我连上了”概括全部连接准入。
23.2.1 pg_hba.conf 的匹配顺序与证据
HBA 是有序规则,不是规则集合
PostgreSQL 对一条新连接从上到下检查 HBA:
- 找到第一条在连接类型、数据库、用户和来源地址上都匹配的记录;
- 使用这条记录指定的方法认证;
- 认证失败就拒绝,不会继续尝试后面的记录;
- 没有任何记录匹配也会拒绝。
因此下面两段配置语义完全不同:
HBA 不是防火墙 ACL 的“最具体规则优先”,也没有失败后回退。官方文档明确 规定了 first-match 语义;任何生成器、模板或平台都不能改变这一点。 参见 PostgreSQL:客户端认证配置文件。
一条记录匹配哪些维度
常见 record type:
| 类型 | 传输条件 | 典型用途 |
|---|---|---|
local |
Unix-domain socket | 节点本地管理或 peer 认证 |
host |
TCP,TLS 与非 TLS 都可 | 仅当两种传输都明确允许 |
hostssl |
TCP 且已经建立 TLS | 业务和远程管理的常见下限 |
hostnossl |
TCP 且未使用 TLS | 显式拒绝或受控兼容例外 |
hostgssenc |
TCP 且使用 GSS 加密 | 采用 GSSAPI 的环境 |
匹配列还包括:
几个容易误判的细节:
all很宽,不表示“最末默认规则”;sameuser、samerole等数据库关键字有特定语义;- user 列中的
+role_name匹配该角色的直接或间接成员,而不是匹配字符串 前缀; - database 列匹配的是客户端请求的数据库;
- hostname 规则需要正反向名称解析,延迟和失败模式不同于 CIDR;
- replication 连接有专门的 database 关键字和权限要求;
hostssl只说明客户端到该 PostgreSQL listener 的这段链路用了 TLS。
如果入口是 PgBouncer,客户端首先连接的是 PgBouncer。此时:
这是两条独立的准入链。PostgreSQL 的 pg_hba.conf 不会替 PgBouncer
过滤客户端来源;PgBouncer 的 HBA 也不会自动约束绕过代理、直连
PostgreSQL 的流量。
pg_hba_file_rules 是解析证据
不要只读取模板文件。PostgreSQL 提供
pg_hba_file_rules,可把当前 HBA 文件解析为行:
它能证明:
- PostgreSQL 从哪些文件和行解析出规则;
- include 后的最终顺序;
- 方法、地址、选项是否符合预期;
- 是否存在语法或解析错误。
它不能单独证明:
- 某条规则可从目标网段实际到达;
- 防火墙、安全组、HAProxy 或 PgBouncer 是否放行;
- DNS 名称匹配是否如预期;
- 密码、证书或外部身份提供方是否可用;
- 连接最后究竟命中了哪条规则。
因此验收还要从允许与禁止的真实来源分别连接,并把时间、目标地址、目标 数据库、login、TLS 属性和结果关联起来。生产系统不应为了测试负例而从未知 公网来源扫描数据库;应使用预先批准的测试节点。
HBA 不负责对象授权
下面的连接可能通过 HBA 和 SCRAM,却仍被数据库拒绝:
反过来,CONNECT 只是进入数据库:
它没有授予:
- schema 的
USAGE; - table 的
SELECT; - sequence 的
USAGE; - function 的
EXECUTE; - 切换到某个业务角色的 membership。
这也是为什么 HBA 不能被称为“权限配置”。它选择认证方法并执行连接准入, 对象授权要在 23.3 单独证明。
安全变更顺序
HBA 变更采用“声明—解析—负例—正例—回滚”:
在 Pigsty 中,应同时检查 PostgreSQL 与 PgBouncer 的 HBA 声明和渲染产物。
23.6 会把这条流程映射到 pg_hba_rules、pgb_hba_rules 及默认规则。
23.2.2 SCRAM、证书与外部身份
先区分四件事
“数据库密码安全”常把四个问题混在一起:
| 问题 | 典型机制 |
|---|---|
| 服务器保存什么 | SCRAM verifier、外部身份映射、客户端证书映射 |
| 线上如何证明身份 | SCRAM exchange、certificate、GSS/SSPI、LDAP、OAuth |
| 链路是否加密 | TLS 或 GSS encryption |
| 客户端是否找对服务器 | CA chain + hostname/IP identity verification |
只把 password_encryption 设为 scram-sha-256,不会自动启用 TLS;只使用 TLS
也不会自动把数据库中旧的 MD5 verifier 变成 SCRAM。
SCRAM 的角色
PostgreSQL 使用 SCRAM-SHA-256 时,服务器保存的是 salted verifier,而不是 可直接用于登录的明文密码。新密码应在:
返回 scram-sha-256 的受控环境中设置。不要通过命令行参数、shell history、
CI 日志或 Git 文件传递明文:
环境变量也不是天然的 secret manager:它可能被子进程继承、被诊断工具采集, 或留在流水线元数据中。重点是限制创建、读取、传递和销毁它的主体与时间。
PostgreSQL 18 已将 MD5 密码支持标记为弃用。迁移时可以先把 HBA 目标方法改为 SCRAM-compatible 路径,再逐个重置用户密码生成 SCRAM verifier,并验证所有 驱动。官方迁移说明见 PostgreSQL:密码认证。
channel binding
SCRAM channel binding 把认证交换绑定到当前 TLS channel,降低凭据交换被代理 到另一条 TLS 会话的风险。支持它的 libpq 客户端可以要求:
这里两个选项不可互相替代:
部署前必须确认驱动版本、TLS 库和中间代理是否支持。不能因为服务端支持 SCRAM
就假设所有客户端都支持 SCRAM-SHA-256-PLUS。
本章沙箱的直连实验证明 verify-full + channel_binding=require 可以成功;
这是一条兼容性证据,不代表 PgBouncer 客户端入口也自动具备同样属性。
客户端证书
cert 认证由 TLS 客户端证书证明身份,通常还需用 map= 把证书主体映射到
PostgreSQL role。它适合:
- 节点间或服务间受管身份;
- 有成熟 CA、签发、吊销和轮换系统的环境;
- 不希望长期共享密码的管理链路。
它并不自动适合每个终端用户。必须解决:
- private key 存放与文件权限;
- 客户端证书分发;
- SAN/subject 与 role 的映射;
- 有效期和轮换重叠期;
- 离职、设备丢失与 CRL/OCSP;
- 代理终止 TLS 后如何继续传递可信身份。
如果 PgBouncer 终止客户端 TLS,PostgreSQL 后端看到的是 PgBouncer 的连接, 不能凭空看到原始客户端证书。身份终止点必须在架构图和审计模型中明确。
外部身份不是“无密码”捷径
PostgreSQL 还可以接入 LDAP、GSS/SSPI、PAM、RADIUS、OAuth 等方法,具体可用 范围取决于版本和构建。它们把一部分认证判断交给外部系统,但数据库仍需定义:
评审时要问:
- 外部主体如何唯一映射,是否会因重名或大小写碰撞映射错误;
- 身份提供方不可用时是 fail-closed 还是出现旁路;
- token/ticket 的 audience、issuer、有效期和撤销如何验证;
- 数据库本地 break-glass 是否独立保管;
- PgBouncer 是否支持该认证方法,还是需要
auth_query/代理集成; - 外部组变化多久才能反映到数据库 session;
- 已建立 session 在外部身份撤销后何时终止。
身份联邦减少的是一类凭据管理,不会消除 role graph、对象 ACL、RLS 和审计。
PgBouncer 的身份交付面
PgBouncer 需要知道如何验证 client login,并以何种 server identity 连接 PostgreSQL。常见入口包括:
这意味着“PostgreSQL 中创建了 LOGIN role”并不必然让 PgBouncer 接受该用户。 本章轮换探针刻意创建了一个未进入池认证面的临时 login:
这个负例证明两套身份面必须分别验收。不要为了让探针通过而临时把高权用户 加入 PgBouncer userlist。
23.2.3 TLS 验证、吊销与密钥轮换
sslmode 分别证明什么
libpq 的主要模式可理解为:
sslmode |
加密 | 验证 CA | 验证目标名称 | 适用判断 |
|---|---|---|---|---|
disable |
否 | 否 | 否 | 只用于明确受控的非 TLS 路径 |
allow |
不保证 | 不保证 | 不保证 | 兼容优先,不是生产安全基线 |
prefer |
不保证 | 不保证 | 不保证 | 默认兼容行为,不是证明 |
require |
是 | 通常不证明名称 | 否 | 防窃听,不充分防冒充 |
verify-ca |
是 | 是 | 否 | 对端由受信 CA 签发 |
verify-full |
是 | 是 | 是 | 生产客户端通常应达到 |
require 能加密,但若不核对服务器名称,客户端可能把密码交给持有另一张受信
证书或被错误路由的服务器。生产应用应优先使用 verify-full。libpq 的精确
行为和 root certificate 兼容细节见
PostgreSQL:SSL 支持。
名称验证依赖 SAN 和连接名
verify-full 校验的是连接参数中的 host 与证书身份。证书应在
Subject Alternative Name 中声明实际使用的 DNS name 或 IP:
若客户端使用 VIP、HAProxy 名称或 Kubernetes service name,证书必须覆盖这个 稳定入口;只给后端节点名签证书并不能验证入口名称。
本章沙箱节点证书包含 localhost、集群名、节点名、loopback 与节点 IP 的 DNS/IP SAN。正式实验从证书解析 SAN,再执行三种连接:
第三条负例和前两条正例同等重要。没有负例,无法排除客户端根本没有执行名称 校验。
看 pg_stat_ssl,但不要只看它
服务器端可把 session 与 TLS 属性关联:
正式直连观察到:
但 pg_stat_ssl.ssl=true 只证明 PostgreSQL 看到的这一跳使用 TLS。若拓扑是:
PostgreSQL 看到的后端连接自然是 ssl=false,不能据此断言客户端链路未加密。
反之,后端 TLS 为 true 也不能证明 client-to-proxy 使用 TLS。两跳要分别测。
CA、CRL 与 private key
服务端最少要治理:
- server certificate;
- server private key;
- trusted client CA(若验证客户端证书);
- CRL 或其他撤销机制;
- 文件 owner、mode 和可读取主体;
- reload/restart 语义;
- 到期时间、提前轮换窗口和告警。
PostgreSQL 要求 private key 权限受到严格限制。本章节点上的服务端 key 为
0600。这只是静态权限证据,还需确认备份、配置管理缓存、工单附件和监控
采集器没有复制密钥。
沙箱的 CRL file/dir 未配置,因此不能声称已经具备客户端证书撤销闭环。证书 有效期很长也不等于安全或不安全;必须根据签发自动化、暴露面和撤销能力制定 生命周期,而不是只看 expiry date。
双版本轮换,而不是瞬间替换
CA、服务器证书和客户端证书/密码轮换都应保留重叠窗口:
对于 CA:
对于密码:
PostgreSQL role 只有一个当前 password verifier,没有天然的“双密码同时有效”。 应用侧重叠通常要借助两个 login、连接池分批切换,或把数据库密码变更与快速 客户端 rollout 精确协调。
凭据撤销不等于 session 撤销
本章做了一个容易被忽略的实验:
结论是:
若处置的是泄露或人员离职,还要:
- 在应用池和代理层停止新借用;
- 找出目标 role/session;
- 评估事务影响后终止连接;
- rotate downstream secret;
- 检查复制、备份、日志和导出物;
- 保存不含秘密的取证证据。
本章沙箱的诚实结论
形式化验收同时发现:
| 项目 | 运行事实 | 结论 |
|---|---|---|
| PostgreSQL TLS | 开启,最低 TLS 1.2 | 基础能力存在 |
直连 verify-full |
成功,错误名称失败 | 名称校验可用 |
| SCRAM channel binding | 直连成功 | 该客户端路径兼容 |
| 业务 HBA | 内网存在 host 规则 |
非 TLS 直连仍可成功 |
| PgBouncer client TLS | 禁用 | 客户端到池入口不加密 |
| PgBouncer backend | 本地 Unix socket | 该跳不使用 TLS,符合本地链路事实 |
| CRL | 未配置 | 吊销闭环缺失 |
所以沙箱适合验证机制,不满足本章定义的生产安全门槛。正确结论不是因为发现 缺口就隐藏实验,而是输出:
下一节在已经确定 login 身份之后,继续回答 effective role 与对象权限问题。
上一节:威胁模型与信任边界 · 返回本章目录 · 下一节:角色与最小权限 · 查看全书目录 · 查看索引中心