pg_hba.conf 客户端认证
pg_hba.conf 是 PostgreSQL 的客户端认证配置文件,决定了「谁、从哪台机器、连哪个库、用什么认证方式」这四个问题的答案。它不控制权限(那是 GRANT 的事),只控制能否建立连接。任何连接质量问题——「为什么本机能连、远程连不上」「为什么密码明明对却认证失败」——追根究底都会落到这个文件的匹配规则上。
前置知识
阅读本节前,建议先了解:postgresql.conf 主配置
基础概念
pg_hba.conf 的定位
| 维度 | pg_hba.conf 管什么 | 它不管什么 |
|---|---|---|
| 连接准入 | ✅ 能否建立连接 | ❌ 连上后能读哪些表(由 GRANT 决定) |
| 认证方式 | ✅ 用密码/证书/ident/peer 等 | ❌ 密码强度策略(由密码机制决定) |
| 来源限制 | ✅ 来源 IP / CIDR / 主机名 | ❌ 连接数上限(由 max_connections 决定) |
| 加密要求 | ✅ hostssl 强制 SSL | ❌ 数据加密(由 SSL/TLS 配置决定) |
一句话:pg_hba.conf 是大门门卫,GRANT 是楼内权限卡。
文件位置
SHOW hba_file;
-- /var/lib/postgresql/18/main/pg_hba.conf默认位于 PGDATA 目录,可以通过 hba_file 参数(postmaster 上下文,需重启)改到别处。改完后 SHOW hba_file 会反映新路径。
七字段格式
pg_hba.conf 每行是一条规则,由若干字段组成。字段之间用空白(空格或 Tab)分隔。核心七字段如下:
| # | 字段 | 说明 | 示例 |
|---|---|---|---|
| 1 | TYPE | 连接类型 | local / host / hostssl / hostnossl |
| 2 | DATABASE | 允许连接的数据库 | all / mydb / mydb,otherdb / replication |
| 3 | USER | 允许连接的用户 | all / alice / +admins |
| 4 | ADDRESS | 客户端地址(local 类型省略) | 192.168.1.0/24 / samehost / all |
| 5 | (MASK) | 网络掩码(与上字段合用,新式写法可省) | 255.255.255.0 |
| 6 | AUTH-METHOD | 认证方法 | scram-sha-256 / md5 / peer / trust |
| 7 | OPTIONS | 该认证方法的附加选项 | map=omicron / clientcert=verify-full |
字段数不是固定的
字段数取决于 TYPE 与 ADDRESS 的写法:
local类型省略 ADDRESS 字段,所以是 5 个字段(TYPE DB USER METHOD OPTIONS)。host类型若使用 CIDR 写法(如192.168.1.0/24),ADDRESS 一个字段就够,共 6 个字段。host类型若使用「地址 + 掩码」两列写法,ADDRESS 与 MASK 各占一个字段,共 7 个字段。
不要死记「七个字段」,要看 TYPE 决定。
匹配规则:首条匹配即生效
这是 pg_hba.conf 最重要、最容易被误解的规则:
连接到来
│
▼
┌──────────────────┐
│ 读取第 1 条规则 │
└────────┬─────────┘
│ 匹配吗?
┌─────┴─────┐
是 否
│ │
▼ ▼
使用该规则的 读取下一条
认证方法 │
(结束) │
▼
┌──────────┐
│ 所有规则 │
│ 都不匹配 │
└────┬─────┘
▼
拒绝连接
"no pg_hba.conf entry for host ..."匹配到第一条后立刻停止,不会继续向下找「更精确」的规则,也不会「fall through」。因此规则顺序至关重要:越具体的规则越要写在前面。
# 顺序错误示例:宽泛规则在前,具体规则永远不会被用到
host all all 0.0.0.0/0 trust # ← 所有来源都信任了
host mydb alice 10.0.0.1/32 scram-sha-256 # ← 永远匹配不到!正确顺序:
# 具体规则在前
host mydb alice 10.0.0.1/32 scram-sha-256
host all all 0.0.0.0/0 rejectTYPE 的全部取值
| TYPE | 说明 | 是否需要 ADDRESS |
|---|---|---|
local | Unix 域套接字(socket)连接 | 否 |
host | 匹配 TCP/IP 连接(SSL 与非 SSL 都可) | 是 |
hostssl | 只匹配使用 SSL 的 TCP 连接 | 是 |
hostnossl | 只匹配未使用 SSL 的 TCP 连接 | 是 |
hostgssenc | 只匹配使用 GSSAPI 加密的 TCP 连接 | 是 |
hostnogssenc | 只匹配未使用 GSSAPI 加密的 TCP 连接 | 是 |
hostssl 是最常用的安全加固手段:先用 hostssl 放行指定网段,再用 host ... reject 兜底,即可强制加密。
DATABASE 字段的所有写法
| 写法 | 含义 |
|---|---|
all | 任意数据库 |
sameuser | 请求的数据库名与用户名相同 |
samerole | 请求的数据库与该用户所属的某个角色同名(samegroup 是过时的同义词) |
replication | 物理复制连接(pg_basebackup、START_REPLICATION) |
db1,db2 | 逗号分隔的多个数据库名 |
/regex | 以 / 开头表示正则表达式匹配数据库名 |
@filename | 从文件读取数据库名列表(每行一个) |
USER 字段的所有写法
| 写法 | 含义 |
|---|---|
all | 任意用户 |
alice | 指定用户 |
alice,bob | 逗号分隔多个 |
+admins | + 前缀表示角色成员(该角色的所有成员都匹配) |
/regex | 以 / 开头为正则 |
@filename | 从文件读取用户名列表 |
ADDRESS 字段的所有写法
| 写法 | 含义 | 示例 |
|---|---|---|
| CIDR | 标准 CIDR 网段 | 192.168.1.0/24 |
| IP + MASK | 两列写法(等价于 CIDR) | 192.168.1.0 255.255.255.0 |
all | 任意地址(等价 0.0.0.0/0 + ::/0) | all |
samehost | 匹配服务器自身的所有 IP | samehost |
samenet | 匹配服务器所在的所有子网 | samenet |
| 主机名 | DNS 反查后匹配(性能差,慎用) | db.example.com |
.example.com | 以点开头 = 后缀匹配该域所有主机 | .example.com |
主机名匹配有性能与安全代价
使用主机名时,PostgreSQL 需要对客户端 IP 做反向 DNS 查询,并且要求正向解析回来一致(双向往返验证)。这既有延迟(默认 DNS 超时可能导致连接变慢),又依赖 DNS 服务器可信。
生产环境优先用 CIDR 或 IP+掩码。若必须用主机名,务必配置 log_hostname 与 DNS 超时。
语法与用法
完整行结构
# TYPE DATABASE USER ADDRESS METHOD [OPTIONS]
local all all peer
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
host mydb alice 192.168.1.0/24 scram-sha-256
hostssl all all 0.0.0.0/0 scram-sha-256 clientcert=verify-full
host all all 0.0.0.0/0 reject每行是一条规则,注释用 #。文件中的空行会被忽略。同一行内不能跨行(一条规则必须写在一行)。
认证方法(AUTH-METHOD)全表
| 方法 | 说明 | 依赖 | 安全等级 |
|---|---|---|---|
trust | 无认证,任意人可连 | 无 | 极低(仅限本机/受控环境) |
reject | 无条件拒绝 | 无 | —— |
scram-sha-256 | SCRAM-SHA-256 挑战应答 | password_encryption = scram-sha-256 | 高(推荐) |
md5 | MD5 挑战应答(已弃用) | 密码存为 md5 时按 MD5 校验;存为 SCRAM 时用 SCRAM | 中(即将移除) |
password | 明文密码(不安全) | 无 | 极低(密码明文上网) |
gss | GSSAPI(Kerberos) | Kerberos 环境 | 高 |
sspi | SSPI(Windows 单点登录) | Windows 域 | 高 |
ident | 通过 ident 服务查询 OS 用户 | 需 ident 服务 | 低(易伪造) |
peer | 用内核提供的 OS 用户名 | 仅 Unix 域套接字 | 中高 |
ldap | LDAP 目录认证 | LDAP 服务器 | 依配置 |
radius | RADIUS 认证 | RADIUS 服务器 | 依配置 |
cert | SSL 客户端证书认证 | SSL + 客户端证书 | 高 |
pam | PAM 认证 | PAM 配置 | 依配置 |
bsd | BSD Auth | BSD 系统 | 依配置 |
md5 已被弃用
官方文档明确指出 md5 方法已弃用,未来的 PostgreSQL 版本将移除它。在 PG18 中,若某用户的密码以 SCRAM 形式存储,md5 一行也会自动按 SCRAM 协议执行(这是为了平滑迁移)。新部署一律用 scram-sha-256。
常用认证方法选项(OPTIONS)
不同方法支持不同选项,以下是最常用的几个:
# peer 方法:把 OS 用户名映射为数据库用户名
local all all peer map=os_user_map
# cert 方法:校验客户端证书的 CN
hostssl all all 0.0.0.0/0 cert clientcert=verify-full
# scram-sha-256:无额外选项(算法由 password_encryption 决定)
host all all 0.0.0.0/0 scram-sha-256
# ldap:指定服务器与搜索基准
host all all 0.0.0.0/0 ldap ldapserver=ldap.example.com ldapprefix="uid=" ldapsuffix=",ou=people,dc=example,dc=com"
# radius:指定服务器与密钥
host all all 0.0.0.0/0 radius radiusservers=radius.example.com radiussecrets=secretpg_hba_file_rules 视图
reload 前预检、reload 后核验,都靠这个视图。它会逐行列出规则,并把解析失败的行单独标出来(只有 line_number 与 error 有值)。
SELECT rule_number, type, database, user_name, address, auth_method, options, error
FROM pg_hba_file_rules
ORDER BY rule_number; rule_number | type | database | user_name | address | auth_method | options | error
-------------+----------+----------+-----------+----------------+---------------+------------+-------
1 | local | {all} | {all} | | peer | |
2 | host | {all} | {all} | {127.0.0.1/32} | scram-sha-256 | |
3 | host | {all} | {all} | {::1/128} | scram-sha-256 | |
4 | hostssl | {all} | {all} | {0.0.0.0/0} | scram-sha-256 | {clientcert=verify-full} |
5 | host | {all} | {all} | {0.0.0.0/0} | reject | |database 与 user_name 是数组类型(因为一行里可以写多个),所以显示为 {all}。
若某行写错了,会出现:
rule_number | type | database | user_name | address | auth_method | options | error
-------------+----------+----------+-----------+---------+-------------+---------+---------------------------
1 | host | {all} | {all} | | | | missing entry for usererror 行的特征
解析失败的行只有 line_number 与 error 两列有值,其余列全为 NULL,rule_number 也会是 NULL。所以「查出所有 error 行」的写法是:
SELECT line_number, error FROM pg_hba_file_rules WHERE error IS NOT NULL;修改后如何生效
pg_hba.conf 属于 sighup 级别,reload 即可,无需重启:
# 方式一:pg_ctl
pg_ctl reload -D /var/lib/postgresql/18/main
# 方式二:SQL(需要超级用户)
psql -c "SELECT pg_reload_conf();"
# 方式三:systemd
sudo systemctl reload postgresql-- 核对是否已生效:对比文件修改时间与 pg_stat_file
SELECT pg_stat_file('pg_hba.conf').modification;
-- 或直接看视图内容
SELECT count(*) FROM pg_hba_file_rules;Windows 上的特殊行为
在 Windows 上,pg_hba.conf 的变更由后端进程每次检查时立即读取,因为 Windows 没有 SIGHUP 机制。所以 Windows 上不需要显式 reload 也可能立刻生效,这会让人误以为 reload 无意义。跨平台脚本不要依赖这个差异。
详细说明
一、从零构建一份安全的 pg_hba.conf
生产环境最小可用模板(顺序即优先级):
# ============ 1. 本地管理:peer 认证(免密但对 OS 用户强约束) ============
local all postgres peer
local all all peer
# ============ 2. 本地回环 TCP:需要密码(防止本地提权绕过) ============
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
# ============ 3. 复制专用:单独网段 + 密码 ============
host replication replicator 10.0.0.0/24 scram-sha-256
# ============ 4. 应用网段:强制 SSL + 密码 ============
hostssl myapp appuser 10.0.10.0/24 scram-sha-256
hostssl analytics report_ro 10.0.20.0/24 scram-sha-256
# ============ 5. DBA 跳板机:限定 IP + 证书 ============
hostssl all dba 10.0.0.5/32 cert clientcert=verify-full
# ============ 6. 兜底:其余全部拒绝(务必保留) ============
host all all 0.0.0.0/0 reject
host all all ::/0 reject关键点:
- 第 1 条
local ... peer让psql以postgresOS 用户直接登录,适合服务器本地运维。 - 第 2 条给回环地址加密码认证,是为了防止「任何能访问本机 5432 端口的进程都绕过认证」。
- 第 6 条兜底
reject一定要有,否则漏网来源会得到「no pg_hba.conf entry」的错误(虽然结果也是拒绝,但显式 reject 更清晰,且便于审计)。
二、首条匹配的实证实验
用一个最小实验证明「首条匹配、不 fall through」:
# 实验文件(顺序很关键)
host all all 192.168.1.100/32 reject
host all all 192.168.1.0/24 trust从 192.168.1.100 连接,会被拒绝——因为第一条就匹配了,虽然第二条 trust 更「宽松」,也轮不到它。
$ psql -h 192.168.1.50 -U appuser -d myapp
psql: error: connection to server at "192.168.1.50", port 5432 failed:
FATAL: pg_hba.conf rejects connection for host "192.168.1.100", user "appuser", database "myapp", SSL off注意错误信息里明确指出是 pg_hba.conf rejects,这比 no pg_hba.conf entry 更有诊断价值——前者说明匹配到了 reject 规则,后者说明一条都没匹配上。
三、no pg_hba.conf entry 错误的所有可能原因
这个错误是最常见的连接问题,排查清单:
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
no pg_hba.conf entry for host "x.x.x.x" | 来源 IP 不在任何 ADDRESS 范围内 | 确认客户端出口 IP(NAT 后可能与预期不同) |
no pg_hba.conf entry for host ..., user "y" | USER 字段不匹配(含大小写) | SELECT * FROM pg_hba_file_rules 逐条核对 |
| 只报 host 不报 user | 该行的 TYPE/DATABASE 不匹配 | 检查 TYPE 是否 host 而客户端走了 SSL(应 hostssl) |
| IPv6 连接失败 | 只写了 IPv4 规则 | 补 ::1/128 与 ::/0 |
| Docker 容器内连不上 | 容器源 IP 是网桥地址 | 用 docker network inspect 查实际源 IP |
快速定位客户端真实源 IP:
-- 在已连上的其他会话中查看
SELECT client_addr, client_port, usename, application_name
FROM pg_stat_activity
WHERE backend_type = 'client backend';四、用 +group 做基于角色的批量授权
当有很多用户要同一种认证方式时,用角色而不是逐个列用户名:
CREATE ROLE app_users LOGIN;
CREATE ROLE app_reader LOGIN;
GRANT app_users TO alice, bob;
GRANT app_reader TO carol;# +app_users 匹配所有属于 app_users 角色的用户(含继承成员)
hostssl myapp +app_users 10.0.10.0/24 scram-sha-256
hostssl myapp +app_reader 10.0.20.0/24 scram-sha-256角色成员身份不会级联到「成员的成员」
+role 匹配的是直接或间接属于该角色的用户(通过 pg_auth_members 递归)。但在 PG16 之前,GRANT role TO user 默认会继承;PG16 引入了 INHERIT/NOINHERIT 语义与 SET ROLE。写 HBA 时建议显式确认成员关系:
SELECT r.rolname AS role, m.rolname AS member
FROM pg_auth_members am
JOIN pg_roles r ON r.oid = am.roleid
JOIN pg_roles m ON m.oid = am.member;五、强制 SSL:hostssl + reject 组合
只写 hostssl 是不够的——它只匹配 SSL 连接,非 SSL 连接可能落到后面的宽松规则上。必须用 reject 兜底。
# 正确:先放行 SSL,再拒绝非 SSL
hostssl all all 0.0.0.0/0 scram-sha-256
hostnossl all all 0.0.0.0/0 reject也可以只用 host + reject 兜底,但显式 hostnossl ... reject 的报错信息更明确:
# 用 hostnossl reject 时:
FATAL: pg_hba.conf rejects connection for host "1.2.3.4", user "app", database "myapp", SSL off报错里的 SSL off 直接告诉你「你需要用 SSL 重连」,比 no pg_hba.conf entry 有用得多。
对已有连接的强制还需要参数配合:
-- 让所有连接都要求 SSL
ALTER SYSTEM SET ssl = on;
-- 可选:要求客户端提供证书
-- 在 postgresql.conf 中:ssl_ca_file、ssl_cert_file、ssl_key_file六、clientcert 的三个取值
cert 方法或 clientcert 选项控制客户端证书的校验强度:
| 取值 | 含义 | 用途 |
|---|---|---|
verify-ca(默认) | 只验证证书由受信 CA 签发 | 一般 mTLS |
verify-full | 额外验证证书的 CN 与请求的用户名一致 | 强身份绑定(推荐) |
no-verify | 不校验 | 几乎不用 |
# 用证书做认证:CN 必须等于数据库用户名
hostssl all all 0.0.0.0/0 cert clientcert=verify-full
# 用证书做「额外一层」:先密码,再校验证书由受信 CA 签发
hostssl all all 0.0.0.0/0 scram-sha-256 clientcert=verify-ca使用 cert 时,用户必须是已存在的数据库角色,且证书的 Common Name 要与角色名匹配(verify-full 下)。
cert 方法要求 ssl 必须先开启
cert 依赖 ssl = on 以及 ssl_ca_file 指向可信 CA。如果 ssl 关闭,hostssl 规则永远不会匹配,客户端会收到 no pg_hba.conf entry。
七、peer 认证与 pg_ident.conf 的配合
peer 方法通过 Unix 域套接字的内核凭据拿到 OS 用户名,然后(默认)直接当作数据库用户名。若 OS 用户名与数据库用户名不同,就必须用用户名映射(map=),映射规则写在 pg_ident.conf 里。
# pg_hba.conf
local all all peer map=os_db_map# pg_ident.conf
os_db_map alice alice
os_db_map deploy appuser详细映射语法见下一节,这里只需记住:peer 遇到用户名不一致时,不加 map= 就会直接认证失败。
八、@file 与正则:管理大量库/用户
当一个规则要覆盖几十个库时,用 @file 更好维护:
# pg_hba.conf
host @appdbs.txt +app_users 10.0.10.0/24 scram-sha-256# appdbs.txt(相对路径相对于 pg_hba.conf 所在目录)
myapp
myapp_shard_01
myapp_shard_02
analytics正则写法(/ 开头):
# 匹配所有以 myapp_ 开头的数据库
host /^myapp_.*$ +app_users 10.0.10.0/24 scram-sha-256
# 匹配所有以 _ro 结尾的用户
host analytics /.*_ro$ 10.0.20.0/24 scram-sha-256正则里的特殊字符要转义
正则引擎是 POSIX 风格,. 等元字符需要 \.。同时因为 HBA 行内用空白做分隔,正则中不能包含未转义的空白。
# 错误!未转义的 . 会匹配任意字符
host /myapp.db/ all 10.0.0.0/24 scram-sha-256
# 正确
host /^myapp\.db$/ all 10.0.0.0/24 scram-sha-256九、加密密码与 password_encryption
scram-sha-256 与 md5 都要求角色有密码(CREATE ROLE ... PASSWORD 或 ALTER ROLE ... PASSWORD),密码的存储格式由 password_encryption 决定:
SHOW password_encryption;
-- scram-sha-256(PG14+ 的默认值)
-- 修改后重设密码才会用新算法存储
ALTER SYSTEM SET password_encryption = 'scram-sha-256';
SELECT pg_reload_conf();
ALTER ROLE appuser PASSWORD 'Str0ng-P@ssw0rd';检查密码存储格式:
SELECT rolname, substring(rolpassword, 1, 14) AS pwd_prefix, rolpassword IS NULL AS no_password
FROM pg_authid
WHERE rolcanlogin
ORDER BY rolname; rolname | pwd_prefix | no_password
----------+----------------+-------------
alice | SCRAM-SHA-256$ | f
appuser | SCRAM-SHA-256$ | f
legacy | md5 | f
nologin | | tmd5 前缀说明该用户密码还是旧格式。只要 HBA 里用的是 scram-sha-256,这类用户会认证失败(因为服务端无法用 SCRAM 校验一个 MD5 哈希)。迁移方法就是重设一次密码。
十、pg_hba.conf 的 include 指令
与 postgresql.conf 一样,pg_hba.conf 也支持 include 系列指令:
# 主文件只保留总纲与兜底规则
include 'hba.d/10_local.conf'
include 'hba.d/20_app.conf'
include_if_exists 'hba.d/99_local_override.conf'
include_dir 'hba.d'
# 兜底必须写在最后(注意:include_dir 内容插在此处之前)
host all all 0.0.0.0/0 rejectinclude 插入位置决定规则顺序
include 是原地展开——被包含文件的规则会插在 include 那一行的位置参与顺序。所以「兜底 reject」必须写在所有 include 之后。
# 错误!reject 在前,后面 include 进来的规则全部失效
host all all 0.0.0.0/0 reject
include 'hba.d/20_app.conf'十一、日志中的 HBA 诊断信息
开启以下参数有助于定位:
log_connections = on
log_disconnections = on
log_line_prefix = '%m [%p] %q%u@%d,%h '连接成功与失败都会记录,失败原因直接写在日志里:
2026-09-29 10:12:33.412 CST [28841] LOG: connection received: host=10.0.10.77 port=51234
2026-09-29 10:12:33.418 CST [28841] FATAL: no pg_hba.conf entry for host "10.0.10.77", user "appuser", database "myapp", no encryption
2026-09-29 10:12:33.418 CST [28841] LOG: disconnection: session time: 0:00:00.006 user=appuser database=myapp host=10.0.10.77 port=51234最后一行的 no encryption(或 SSL off)是判断「客户端没走 SSL」的关键线索。
实战示例
场景:为「主库 + 只读从库 + 应用网段 + 跳板机」设计完整 HBA
需求:
- 服务器本地用
postgresOS 用户免密运维。 - 从库复制账号只能从数据库网段
10.0.1.0/24连。 - 应用(
10.0.10.0/24)连myapp库,必须 SSL,密码认证。 - 报表只读账号(
10.0.20.0/24)连analytics,必须 SSL 且限制连接数。 - DBA 只能从跳板机
10.0.0.9连,使用客户端证书。 - 其他一律拒绝。
第一步,准备角色与密码:
-- 复制账号
CREATE ROLE replicator WITH LOGIN REPLICATION PASSWORD 'R3pl-C@tion-2026';
-- 应用账号
CREATE ROLE appuser WITH LOGIN PASSWORD 'App-U$er-2026';
-- 报表账号
CREATE ROLE report_ro WITH LOGIN PASSWORD 'Rpt-Read-2026' CONNECTION LIMIT 10;
-- DBA 账号(证书认证,可无密码)
CREATE ROLE dba WITH LOGIN;
GRANT pg_monitor TO report_ro;第二步,编写 pg_hba.conf:
# =============================================================
# pg_hba.conf - 生成日期 2026-09-29
# 规则自上而下匹配,首条命中即生效,顺序不可随意调整
# =============================================================
# ---- 1. 本地 Unix socket:peer 认证 ----
local all postgres peer
local all all peer
# ---- 2. 本地 TCP 回环:密码认证(防本地绕过) ----
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
# ---- 3. 物理复制:仅数据库网段 ----
host replication replicator 10.0.1.0/24 scram-sha-256
# ---- 4. DBA 跳板机:证书认证(最具体,放前面) ----
hostssl all dba 10.0.0.9/32 cert clientcert=verify-full
# ---- 5. 报表只读:SSL + 密码 ----
hostssl analytics report_ro 10.0.20.0/24 scram-sha-256
# ---- 6. 应用:SSL + 密码 ----
hostssl myapp appuser 10.0.10.0/24 scram-sha-256
# ---- 7. 非 SSL 明确拒绝(给出更清晰的报错) ----
hostnossl all all 0.0.0.0/0 reject
# ---- 8. 兜底拒绝 ----
host all all 0.0.0.0/0 reject
host all all ::/0 reject第三步,预检 + reload + 验证:
-- 预检:确保没有解析错误
SELECT line_number, error FROM pg_hba_file_rules WHERE error IS NOT NULL; line_number | error
-------------+-------
(0 rows)pg_ctl reload -D /var/lib/postgresql/18/main-- 核验规则是否都读进来了
SELECT rule_number, type, database, user_name, address, auth_method, options
FROM pg_hba_file_rules
WHERE rule_number IS NOT NULL
ORDER BY rule_number; rule_number | type | database | user_name | address | auth_method | options
-------------+----------+-------------+-----------+----------------+---------------+--------------------------
1 | local | {all} | {postgres}| | peer |
2 | local | {all} | {all} | | peer |
3 | host | {all} | {all} | {127.0.0.1/32} | scram-sha-256 |
4 | host | {all} | {all} | {::1/128} | scram-sha-256 |
5 | host | {replication}| {replicator} | {10.0.1.0/24} | scram-sha-256 |
6 | hostssl | {all} | {dba} | {10.0.0.9/32} | cert | {clientcert=verify-full}
7 | hostssl | {analytics} | {report_ro}| {10.0.20.0/24}| scram-sha-256 |
8 | hostssl | {myapp} | {appuser} | {10.0.10.0/24} | scram-sha-256 |
9 | hostnossl| {all} | {all} | {0.0.0.0/0} | reject |
10 | host | {all} | {all} | {0.0.0.0/0} | reject |
11 | host | {all} | {all} | {::/0} | reject |
(11 rows)第四步,用 psql 做端到端验证:
# 1) 本地 peer:应当直接成功(以 postgres OS 用户执行)
$ sudo -u postgres psql -c "SELECT current_user, inet_client_addr();"
current_user | inet_client_addr
--------------+------------------
postgres | # local socket,无 IP
(1 row)
# 2) 应用连接:应当成功(假设客户端在 10.0.10.x)
$ psql "host=10.0.1.5 dbname=myapp user=appuser sslmode=require"
myapp=>
# 3) 应用连接但不带 SSL:应当被 hostnossl reject 拦下
$ psql "host=10.0.1.5 dbname=myapp user=appuser sslmode=disable"
psql: error: connection to server at "10.0.1.5", port 5432 failed:
FATAL: pg_hba.conf rejects connection for host "10.0.10.7", user "appuser", database "myapp", SSL off
# 4) 从非授权网段连接:应当落到兜底 reject
$ psql "host=10.0.1.5 dbname=myapp user=appuser"
psql: error: connection to server at "10.0.1.5", port 5432 failed:
FATAL: pg_hba.conf rejects connection for host "172.16.5.9", user "appuser", database "myapp", SSL off结果解读:
- 第 3 步的报错里有
SSL off,说明是hostnossl ... reject命中了,正好达成「强制 SSL」的目标。 - 第 4 步的报错也来自兜底
reject,且报错明确是「HBA 拒绝」而非「无匹配入口」。
注意事项
不要把 trust 用在非本地来源上。 0.0.0.0/0 trust 意味着全互联网都可以无密码连你的数据库,这是最典型的高危配置。
# 错误!等于把数据库完全开放
host all all 0.0.0.0/0 trust即使是内网也要谨慎——内网不等于可信网络(横向渗透)。
不要在 md5 上新建部署。 它已被官方标记为弃用,未来版本会移除;且 md5 在密码哈希碰撞与协议层面都不如 SCRAM。
# 不推荐
host all all 0.0.0.0/0 md5
# 推荐
host all all 0.0.0.0/0 scram-sha-256不要漏掉 IPv6 规则。 只有 IPv4 规则时,来自 IPv6 的连接会落到「无匹配」并被拒绝,而很多环境(尤其容器、云平台)默认走 IPv6。
# 错误!只写了 IPv4
host all all 0.0.0.0/0 reject
# 正确:IPv4 与 IPv6 都要写
host all all 0.0.0.0/0 reject
host all all ::/0 reject不要把宽泛规则写在具体规则前面。 首条匹配即生效,顺序错误会让具体规则变成死代码。
# 错误!alice 的专用规则永远不生效
host all all 10.0.0.0/8 trust
host mydb alice 10.0.0.1/32 scram-sha-256
# 正确
host mydb alice 10.0.0.1/32 scram-sha-256
host all all 10.0.0.0/8 trust不要用主机名做主要来源限制。 DNS 反查有性能开销,且一旦 DNS 被劫持就能绕过限制。
# 不推荐
host all all app.example.com scram-sha-256
# 推荐(用 CIDR)
host all all 10.0.10.0/24 scram-sha-256不要在规则里写 password(明文)。 密码会以明文经过网络,任何能抓包的人都能拿到。
# 错误!明文密码
host all all 0.0.0.0/0 password不要把 local 规则当成只有本机能用。 local 指的是 Unix 域套接字,任何能访问该 socket 文件的本地进程(含容器内挂载了 socket 的场景)都能使用。
# 危险:本机任意用户都能以 postgres 身份连
local all postgres trust
# 推荐
local all postgres peer不要在文件中间插入 include 而后置兜底规则。 顺序会被打乱,导致兜底失效或规则顺序不符合预期。
# 错误!reject 之后又 include 进了新规则
host all all 0.0.0.0/0 reject
include_dir 'hba.d'不要忘记 reload。 改完文件不 reload,一切照旧,这是最常见的「我改了怎么没用」。
# 改完必须
pg_ctl reload -D $PGDATA
# 或
psql -c "SELECT pg_reload_conf()"不要用 pg_hba_file_rules 判断「当前生效规则」。 该视图反映的是文件内容,不是内存中已生效的规则集。改动后没有 reload,视图会显示新内容而实际行为仍是旧的。
-- reload 前后对比,确认真的 reload 过
SELECT pg_stat_file('pg_hba.conf').modification AS file_mtime, now();最佳实践
先写具体规则,再写宽泛规则,最后必须兜底 reject。 把顺序当成契约来维护。
hostssl myapp appuser 10.0.10.0/24 scram-sha-256
hostnossl all all 0.0.0.0/0 reject
host all all 0.0.0.0/0 reject
host all all ::/0 reject生产环境一律用 scram-sha-256,并把 password_encryption 设为 scram-sha-256。 两者必须配套,否则会出现「HBA 写了 SCRAM 但用户密码是 md5 格式」导致的认证失败。
SHOW password_encryption;
ALTER SYSTEM SET password_encryption = 'scram-sha-256';
SELECT pg_reload_conf();
-- 存量用户需要重设一次密码才会转成 SCRAM把「强制加密」写成 hostssl 放行 + hostnossl 拒绝的组合。 报错信息更明确,排查更快。
hostssl all all 0.0.0.0/0 scram-sha-256
hostnossl all all 0.0.0.0/0 reject用 +role 而不是逐个列举用户名。 用户增减只需改角色成员关系,不用碰 HBA 文件。
GRANT app_users TO new_hire;hostssl myapp +app_users 10.0.10.0/24 scram-sha-256用 @file 管理大量数据库/用户名单。 名单变更不需要 reload HBA 之外的逻辑,且便于脚本生成。
hostssl @/etc/postgresql/hba/appdbs.txt +app_users 10.0.10.0/24 scram-sha-256每次改动都先跑一遍预检查询。 把它固化到变更流程里,避免「改错了但没发现」。
SELECT line_number, error FROM pg_hba_file_rules WHERE error IS NOT NULL;
-- 必须 0 行才执行 reload开启 log_connections 与 log_disconnections,并把失败原因纳入监控。 认证失败突然增多往往是攻击或配置漂移的信号。
log_connections = on
log_disconnections = on-- 用 pg_stat_database 观察
SELECT datname, numbackends FROM pg_stat_database ORDER BY numbackends DESC;为复制连接单独建账号、单独限定网段。 复制账号权限高(REPLICATION),不要与业务账号混用。
host replication replicator 10.0.1.0/24 scram-sha-256CREATE ROLE replicator WITH LOGIN REPLICATION PASSWORD '...';为报表/BI 账号设置 CONNECTION LIMIT,并单独网段。 防止报表把连接池占满影响在线业务。
ALTER ROLE report_ro CONNECTION LIMIT 20;每个规则加注释说明「为什么这么写」。 HBA 文件往往只有几行,但每行的安全含义都很重,注释是最划算的保险。
# 跳板机证书认证:CN 必须等于角色名,用于审计所有 DBA 操作
hostssl all dba 10.0.0.9/32 cert clientcert=verify-full用 pg_hba_file_rules 生成「当前规则清单」并纳入版本库。 文件内容与视图内容应当一致,出现差异说明有人直接改了运行实例。
\copy (SELECT rule_number, type, database, user_name, address, auth_method, options FROM pg_hba_file_rules WHERE rule_number IS NOT NULL ORDER BY rule_number) TO 'hba-snapshot.csv' WITH CSV HEADER变更 HBA 用「先加后删」的灰度方式。 先加入新规则(放前面)并 reload,验证通过后再删除旧规则,避免中途出现无规则可用的窗口。
# 第一步:新规则放前面,旧规则先保留
hostssl myapp appuser 10.0.10.0/24 scram-sha-256
host myapp appuser 10.0.10.0/24 md5 # 待删除下一节
继续学习:pg_ident.conf 用户映射
参考链接
- PostgreSQL 官方文档:The pg_hba.conf File(auth-pg-hba-conf)
- PostgreSQL 官方文档:The pg_hba_file_rules View(view-pg-hba-file-rules)
- PostgreSQL 官方文档:Client Authentication(Chapter 20)
- PostgreSQL 官方文档:Password Authentication(auth-password)
- PostgreSQL 官方文档:Certificate Authentication(auth-cert)
- PostgreSQL 官方文档:SSL Support(ssl-tcp)
- PostgreSQL 官方文档:File Locations(runtime-config-file-locations)
- PostgreSQL 官方文档:Setting Parameters(config-setting)