Skip to content

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 是楼内权限卡。

文件位置 ​

sql
SHOW hba_file;
-- /var/lib/postgresql/18/main/pg_hba.conf

默认位于 PGDATA 目录,可以通过 hba_file 参数(postmaster 上下文,需重启)改到别处。改完后 SHOW hba_file 会反映新路径。

七字段格式 ​

pg_hba.conf 每行是一条规则,由若干字段组成。字段之间用空白(空格或 Tab)分隔。核心七字段如下:

#字段说明示例
1TYPE连接类型local / host / hostssl / hostnossl
2DATABASE允许连接的数据库all / mydb / mydb,otherdb / replication
3USER允许连接的用户all / alice / +admins
4ADDRESS客户端地址(local 类型省略)192.168.1.0/24 / samehost / all
5(MASK)网络掩码(与上字段合用,新式写法可省)255.255.255.0
6AUTH-METHOD认证方法scram-sha-256 / md5 / peer / trust
7OPTIONS该认证方法的附加选项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 最重要、最容易被误解的规则:

text
        连接到来
           │
           ▼
   ┌──────────────────┐
   │ 读取第 1 条规则   │
   └────────┬─────────┘
            │ 匹配吗?
      ┌─────┴─────┐
     是            否
      │             │
      ▼             ▼
  使用该规则的   读取下一条
  认证方法           │
   (结束)          │
                    ▼
              ┌──────────┐
              │ 所有规则  │
              │ 都不匹配  │
              └────┬─────┘
                   ▼
              拒绝连接
         "no pg_hba.conf entry for host ..."

匹配到第一条后立刻停止,不会继续向下找「更精确」的规则,也不会「fall through」。因此规则顺序至关重要:越具体的规则越要写在前面。

ini
# 顺序错误示例:宽泛规则在前,具体规则永远不会被用到
host  all  all  0.0.0.0/0  trust          # ← 所有来源都信任了
host  mydb alice 10.0.0.1/32 scram-sha-256 # ← 永远匹配不到!

正确顺序:

ini
# 具体规则在前
host  mydb  alice  10.0.0.1/32  scram-sha-256
host  all   all    0.0.0.0/0    reject

TYPE 的全部取值 ​

TYPE说明是否需要 ADDRESS
localUnix 域套接字(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匹配服务器自身的所有 IPsamehost
samenet匹配服务器所在的所有子网samenet
主机名DNS 反查后匹配(性能差,慎用)db.example.com
.example.com以点开头 = 后缀匹配该域所有主机.example.com

主机名匹配有性能与安全代价

使用主机名时,PostgreSQL 需要对客户端 IP 做反向 DNS 查询,并且要求正向解析回来一致(双向往返验证)。这既有延迟(默认 DNS 超时可能导致连接变慢),又依赖 DNS 服务器可信。

生产环境优先用 CIDR 或 IP+掩码。若必须用主机名,务必配置 log_hostname 与 DNS 超时。

语法与用法 ​

完整行结构 ​

ini
# 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-256SCRAM-SHA-256 挑战应答password_encryption = scram-sha-256高(推荐)
md5MD5 挑战应答(已弃用)密码存为 md5 时按 MD5 校验;存为 SCRAM 时用 SCRAM中(即将移除)
password明文密码(不安全)无极低(密码明文上网)
gssGSSAPI(Kerberos)Kerberos 环境高
sspiSSPI(Windows 单点登录)Windows 域高
ident通过 ident 服务查询 OS 用户需 ident 服务低(易伪造)
peer用内核提供的 OS 用户名仅 Unix 域套接字中高
ldapLDAP 目录认证LDAP 服务器依配置
radiusRADIUS 认证RADIUS 服务器依配置
certSSL 客户端证书认证SSL + 客户端证书高
pamPAM 认证PAM 配置依配置
bsdBSD AuthBSD 系统依配置

md5 已被弃用

官方文档明确指出 md5 方法已弃用,未来的 PostgreSQL 版本将移除它。在 PG18 中,若某用户的密码以 SCRAM 形式存储,md5 一行也会自动按 SCRAM 协议执行(这是为了平滑迁移)。新部署一律用 scram-sha-256。

常用认证方法选项(OPTIONS) ​

不同方法支持不同选项,以下是最常用的几个:

ini
# 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=secret

pg_hba_file_rules 视图 ​

reload 前预检、reload 后核验,都靠这个视图。它会逐行列出规则,并把解析失败的行单独标出来(只有 line_number 与 error 有值)。

sql
SELECT rule_number, type, database, user_name, address, auth_method, options, error
FROM pg_hba_file_rules
ORDER BY rule_number;
text
 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}。

若某行写错了,会出现:

text
 rule_number |   type   | database | user_name | address | auth_method | options |          error
-------------+----------+----------+-----------+---------+-------------+---------+---------------------------
           1 | host     | {all}    | {all}     |         |             |         | missing entry for user

error 行的特征

解析失败的行只有 line_number 与 error 两列有值,其余列全为 NULL,rule_number 也会是 NULL。所以「查出所有 error 行」的写法是:

sql
SELECT line_number, error FROM pg_hba_file_rules WHERE error IS NOT NULL;

修改后如何生效 ​

pg_hba.conf 属于 sighup 级别,reload 即可,无需重启:

bash
# 方式一:pg_ctl
pg_ctl reload -D /var/lib/postgresql/18/main

# 方式二:SQL(需要超级用户)
psql -c "SELECT pg_reload_conf();"

# 方式三:systemd
sudo systemctl reload postgresql
sql
-- 核对是否已生效:对比文件修改时间与 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 ​

生产环境最小可用模板(顺序即优先级):

ini
# ============ 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 以 postgres OS 用户直接登录,适合服务器本地运维。
  • 第 2 条给回环地址加密码认证,是为了防止「任何能访问本机 5432 端口的进程都绕过认证」。
  • 第 6 条兜底 reject 一定要有,否则漏网来源会得到「no pg_hba.conf entry」的错误(虽然结果也是拒绝,但显式 reject 更清晰,且便于审计)。

二、首条匹配的实证实验 ​

用一个最小实验证明「首条匹配、不 fall through」:

ini
# 实验文件(顺序很关键)
host  all  all  192.168.1.100/32  reject
host  all  all  192.168.1.0/24    trust

从 192.168.1.100 连接,会被拒绝——因为第一条就匹配了,虽然第二条 trust 更「宽松」,也轮不到它。

bash
$ 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:

sql
-- 在已连上的其他会话中查看
SELECT client_addr, client_port, usename, application_name
FROM pg_stat_activity
WHERE backend_type = 'client backend';

四、用 +group 做基于角色的批量授权 ​

当有很多用户要同一种认证方式时,用角色而不是逐个列用户名:

sql
CREATE ROLE app_users LOGIN;
CREATE ROLE app_reader LOGIN;
GRANT app_users TO alice, bob;
GRANT app_reader TO carol;
ini
# +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 时建议显式确认成员关系:

sql
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 兜底。

ini
# 正确:先放行 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 的报错信息更明确:

text
# 用 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 有用得多。

对已有连接的强制还需要参数配合:

sql
-- 让所有连接都要求 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不校验几乎不用
ini
# 用证书做认证: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 里。

ini
# pg_hba.conf
local  all  all  peer  map=os_db_map
ini
# pg_ident.conf
os_db_map  alice   alice
os_db_map  deploy  appuser

详细映射语法见下一节,这里只需记住:peer 遇到用户名不一致时,不加 map= 就会直接认证失败。

八、@file 与正则:管理大量库/用户 ​

当一个规则要覆盖几十个库时,用 @file 更好维护:

ini
# pg_hba.conf
host  @appdbs.txt  +app_users  10.0.10.0/24  scram-sha-256
text
# appdbs.txt(相对路径相对于 pg_hba.conf 所在目录)
myapp
myapp_shard_01
myapp_shard_02
analytics

正则写法(/ 开头):

ini
# 匹配所有以 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 行内用空白做分隔,正则中不能包含未转义的空白。

ini
# 错误!未转义的 . 会匹配任意字符
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 决定:

sql
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';

检查密码存储格式:

sql
SELECT rolname, substring(rolpassword, 1, 14) AS pwd_prefix, rolpassword IS NULL AS no_password
FROM pg_authid
WHERE rolcanlogin
ORDER BY rolname;
text
 rolname  |   pwd_prefix   | no_password
----------+----------------+-------------
 alice    | SCRAM-SHA-256$ | f
 appuser  | SCRAM-SHA-256$ | f
 legacy   | md5            | f
 nologin  |                | t

md5 前缀说明该用户密码还是旧格式。只要 HBA 里用的是 scram-sha-256,这类用户会认证失败(因为服务端无法用 SCRAM 校验一个 MD5 哈希)。迁移方法就是重设一次密码。

十、pg_hba.conf 的 include 指令 ​

与 postgresql.conf 一样,pg_hba.conf 也支持 include 系列指令:

ini
# 主文件只保留总纲与兜底规则
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  reject

include 插入位置决定规则顺序

include 是原地展开——被包含文件的规则会插在 include 那一行的位置参与顺序。所以「兜底 reject」必须写在所有 include 之后。

ini
# 错误!reject 在前,后面 include 进来的规则全部失效
host  all  all  0.0.0.0/0  reject
include 'hba.d/20_app.conf'

十一、日志中的 HBA 诊断信息 ​

开启以下参数有助于定位:

ini
log_connections = on
log_disconnections = on
log_line_prefix = '%m [%p] %q%u@%d,%h '

连接成功与失败都会记录,失败原因直接写在日志里:

text
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 ​

需求:

  1. 服务器本地用 postgres OS 用户免密运维。
  2. 从库复制账号只能从数据库网段 10.0.1.0/24 连。
  3. 应用(10.0.10.0/24)连 myapp 库,必须 SSL,密码认证。
  4. 报表只读账号(10.0.20.0/24)连 analytics,必须 SSL 且限制连接数。
  5. DBA 只能从跳板机 10.0.0.9 连,使用客户端证书。
  6. 其他一律拒绝。

第一步,准备角色与密码:

sql
-- 复制账号
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:

ini
# =============================================================
# 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 + 验证:

sql
-- 预检:确保没有解析错误
SELECT line_number, error FROM pg_hba_file_rules WHERE error IS NOT NULL;
text
 line_number | error
-------------+-------
(0 rows)
bash
pg_ctl reload -D /var/lib/postgresql/18/main
sql
-- 核验规则是否都读进来了
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;
text
 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 做端到端验证:

bash
# 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 意味着全互联网都可以无密码连你的数据库,这是最典型的高危配置。

ini
# 错误!等于把数据库完全开放
host  all  all  0.0.0.0/0  trust

即使是内网也要谨慎——内网不等于可信网络(横向渗透)。

不要在 md5 上新建部署。 它已被官方标记为弃用,未来版本会移除;且 md5 在密码哈希碰撞与协议层面都不如 SCRAM。

ini
# 不推荐
host  all  all  0.0.0.0/0  md5
# 推荐
host  all  all  0.0.0.0/0  scram-sha-256

不要漏掉 IPv6 规则。 只有 IPv4 规则时,来自 IPv6 的连接会落到「无匹配」并被拒绝,而很多环境(尤其容器、云平台)默认走 IPv6。

ini
# 错误!只写了 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

不要把宽泛规则写在具体规则前面。 首条匹配即生效,顺序错误会让具体规则变成死代码。

ini
# 错误!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 被劫持就能绕过限制。

ini
# 不推荐
host  all  all  app.example.com  scram-sha-256
# 推荐(用 CIDR)
host  all  all  10.0.10.0/24     scram-sha-256

不要在规则里写 password(明文)。 密码会以明文经过网络,任何能抓包的人都能拿到。

ini
# 错误!明文密码
host  all  all  0.0.0.0/0  password

不要把 local 规则当成只有本机能用。 local 指的是 Unix 域套接字,任何能访问该 socket 文件的本地进程(含容器内挂载了 socket 的场景)都能使用。

ini
# 危险:本机任意用户都能以 postgres 身份连
local  all  postgres  trust
# 推荐
local  all  postgres  peer

不要在文件中间插入 include 而后置兜底规则。 顺序会被打乱,导致兜底失效或规则顺序不符合预期。

ini
# 错误!reject 之后又 include 进了新规则
host  all  all  0.0.0.0/0  reject
include_dir 'hba.d'

不要忘记 reload。 改完文件不 reload,一切照旧,这是最常见的「我改了怎么没用」。

bash
# 改完必须
pg_ctl reload -D $PGDATA
# 或
psql -c "SELECT pg_reload_conf()"

不要用 pg_hba_file_rules 判断「当前生效规则」。 该视图反映的是文件内容,不是内存中已生效的规则集。改动后没有 reload,视图会显示新内容而实际行为仍是旧的。

sql
-- reload 前后对比,确认真的 reload 过
SELECT pg_stat_file('pg_hba.conf').modification AS file_mtime, now();

最佳实践 ​

先写具体规则,再写宽泛规则,最后必须兜底 reject。 把顺序当成契约来维护。

ini
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 格式」导致的认证失败。

sql
SHOW password_encryption;
ALTER SYSTEM SET password_encryption = 'scram-sha-256';
SELECT pg_reload_conf();
-- 存量用户需要重设一次密码才会转成 SCRAM

把「强制加密」写成 hostssl 放行 + hostnossl 拒绝的组合。 报错信息更明确,排查更快。

ini
hostssl   all all 0.0.0.0/0 scram-sha-256
hostnossl all all 0.0.0.0/0 reject

用 +role 而不是逐个列举用户名。 用户增减只需改角色成员关系,不用碰 HBA 文件。

sql
GRANT app_users TO new_hire;
ini
hostssl myapp +app_users 10.0.10.0/24 scram-sha-256

用 @file 管理大量数据库/用户名单。 名单变更不需要 reload HBA 之外的逻辑,且便于脚本生成。

ini
hostssl @/etc/postgresql/hba/appdbs.txt +app_users 10.0.10.0/24 scram-sha-256

每次改动都先跑一遍预检查询。 把它固化到变更流程里,避免「改错了但没发现」。

sql
SELECT line_number, error FROM pg_hba_file_rules WHERE error IS NOT NULL;
-- 必须 0 行才执行 reload

开启 log_connections 与 log_disconnections,并把失败原因纳入监控。 认证失败突然增多往往是攻击或配置漂移的信号。

ini
log_connections = on
log_disconnections = on
sql
-- 用 pg_stat_database 观察
SELECT datname, numbackends FROM pg_stat_database ORDER BY numbackends DESC;

为复制连接单独建账号、单独限定网段。 复制账号权限高(REPLICATION),不要与业务账号混用。

ini
host  replication  replicator  10.0.1.0/24  scram-sha-256
sql
CREATE ROLE replicator WITH LOGIN REPLICATION PASSWORD '...';

为报表/BI 账号设置 CONNECTION LIMIT,并单独网段。 防止报表把连接池占满影响在线业务。

sql
ALTER ROLE report_ro CONNECTION LIMIT 20;

每个规则加注释说明「为什么这么写」。 HBA 文件往往只有几行,但每行的安全含义都很重,注释是最划算的保险。

ini
# 跳板机证书认证:CN 必须等于角色名,用于审计所有 DBA 操作
hostssl all dba 10.0.0.9/32 cert clientcert=verify-full

用 pg_hba_file_rules 生成「当前规则清单」并纳入版本库。 文件内容与视图内容应当一致,出现差异说明有人直接改了运行实例。

sql
\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,验证通过后再删除旧规则,避免中途出现无规则可用的窗口。

ini
# 第一步:新规则放前面,旧规则先保留
hostssl myapp appuser 10.0.10.0/24 scram-sha-256
host    myapp appuser 10.0.10.0/24 md5            # 待删除

下一节

继续学习:pg_ident.conf 用户映射

参考链接 ​