Skip to content

pg_hba.conf 访问控制 ​

主机基础认证文件(pg_hba.conf, Host-Based Authentication)是 PostgreSQL 决定「谁、从哪里、以什么方式」可以连接的准入清单。它是最常被忽略、也最容易配错的安全配置——几乎所有「连不上」和「不该连的却能连」都出在这里。

前置知识

阅读本节前,建议先了解:初始化数据库集群(initdb)

基础概念 ​

PostgreSQL 的连接准入分四步,pg_hba.conf 负责其中的第 3 步:

  1. 监听地址:listen_addresses 决定是否接受该网络接口的连接(详见 监听地址与端口配置)
  2. 传输层准入:防火墙 / 安全组决定数据包能否到达
  3. 客户端认证:pg_hba.conf 决定该「来源 + 数据库 + 用户」组合是否放行,以及用什么方式验证身份
  4. 对象权限:GRANT 决定连上之后能对哪些对象做什么

pg_hba.conf 的读取规则有两条必须记牢:

  • 自上而下顺序匹配,命中第一条即停止(不是「最精确匹配」,是「最先匹配」)
  • 没有命中任何一行则拒绝,且报错 no pg_hba.conf entry for host ...

这两个规则决定了配置的核心原则:具体规则在前,宽泛规则在后。

术语英文名说明
主机基础认证文件pg_hba.conf客户端连接准入清单
记录Record文件中的一行,7 个字段
认证方法Methodtrust / peer / md5 / scram-sha-256 / cert / reject
连接类型Connection Typelocal / host / hostssl / hostnossl / hostgssenc
地址范围AddressCIDR 格式,如 192.168.1.0/24
规则视图pg_hba_file_rules系统视图,可在 SQL 中检查已加载的规则与错误
重载Reloadpg_ctl reload / SELECT pg_reload_conf(),不中断连接

语法与用法 ​

记录格式 ​

每行由 7 个字段组成(前 4 个必填):

text
# 连接类型  数据库      用户        地址           认证方法    [认证选项]
# TYPE      DATABASE    USER        ADDRESS        METHOD      [OPTIONS]
local       all         postgres                                peer
host        all         all         127.0.0.1/32   scram-sha-256
host        all         all         ::1/128        scram-sha-256
host        demo        app_user    10.0.0.0/8     scrub-sha-256   ← 拼写错误会被记录为错误规则
hostssl     demo        all         0.0.0.0/0      cert
字段取值说明
连接类型localUnix 域套接字连接(Linux/macOS)
hostTCP 连接,含 SSL 与非 SSL
hostssl仅 SSL 加密的 TCP 连接
hostnossl仅非 SSL 的 TCP 连接
hostgssenc仅 GSSAPI 加密连接
数据库all所有数据库
demo指定数据库
demo,report多个库,逗号分隔
replication物理复制连接
@dblist.txt从文件读取库列表
用户all所有角色
app_user指定角色
app_user,report_user多个角色
+app_group角色名前加 + 表示该组的所有成员
@userlist.txt从文件读取用户列表
地址127.0.0.1/32IPv4 单机(CIDR 必须写掩码)
10.0.0.0/8IPv4 网段
::1/128IPv6 单机
all任意地址(等价 0.0.0.0/0 + ::0/0)
samehost / samenet本机所有 IP / 本机所在子网
认证方法trust无条件放行,不需密码
reject无条件拒绝
peer用系统用户名比对数据库角色名(仅 local)
ident通过 ident 服务返回的系统用户名比对
md5MD5 摘要口令(旧,不推荐)
scram-sha-256SCRAM-SHA-256 口令认证(推荐)
cert客户端证书认证(需 SSL)
ldap / radius / pam / gss / sspi外部认证集成

认证方法详解 ​

text
# ── trust:完全放行,任何人只要有网络可达就能以任意角色连接 ──
# 危险!仅用于一次性排障,用完立刻改回
local   all   all                     trust
text
# ── reject:显式拒绝,便于在黑名单场景给出明确错误 ──
# 常用于屏蔽特定网段,比依赖「无匹配即拒绝」更易读
host    all   all   203.0.113.0/24   reject
text
# ── peer:本地连接,用 OS 用户名比对数据库角色名 ──
# sudo -u postgres psql 能成功,正是因为 OS 用户 postgres == 角色 postgres
local   all   postgres                peer
local   all   app_user                peer

# ── ident:需 ident 服务,现代环境已少见 ──
host    demo  all   192.168.1.0/24   ident
text
# ── scram-sha-256:生产环境的口令认证标准 ──
# 服务端与客户端双向校验,不传输明文或可重放的摘要
host    all   all   0.0.0.0/0        scram-sha-256
text
# ── cert:双向 TLS,客户端证书的 CN 必须匹配数据库用户名 ──
# 需先在 postgresql.conf 中启用 ssl(详见 SSL 连接初始化)
hostssl demo  all   10.0.0.0/8       cert  clientcert=verify-full
text
# ── ldap:企业统一认证 ──
host    all   all   10.0.0.0/8       ldap  ldapserver=ldap.example.com ldapprefix="cn=" ldapsuffix=",ou=people,dc=example,dc=com"

匹配顺序示例 ​

text
# ── 一个完整的、顺序正确的配置示例 ──
# 1) 本地管理:只允许 postgres 系统用户以对等方式进入
local   all             postgres                                peer

# 2) 本机其他用户走口令认证
local   all             all                                     scram-sha-256

# 3) 本机回环 TCP(应用在本机连接数据库)
host    all             all             127.0.0.1/32            scram-sha-256
host    all             all             ::1/128                 scram-sha-256

# 4) 复制连接:仅允许专用复制角色、仅内网
host    replication     repl_user       10.0.0.0/8              scram-sha-256

# 5) CI 迁移机:只允许 app_owner 连 demo 库,且必须走 SSL
hostssl demo            app_owner       10.20.30.0/24           scram-sha-256

# 6) 应用服务器网段:只允许连业务库、只用业务账号
hostssl demo            app_user        10.10.0.0/16            scram-sha-256
hostssl demo            report_user     10.10.0.0/16            scram-sha-256

# 7) 屏蔽已知的扫描来源(显式拒绝,便于日志排查)
host    all             all             198.51.100.0/24         reject

# 8) 兜底:其余一律拒绝(这一行其实可省略,因为默认即拒绝,但显式写出更清晰)
host    all             all             0.0.0.0/0               reject

若把第 6 步写到第 5 步之前,第 5 步就永远不会生效。顺序即优先级。

详细说明 ​

文件位置与权限 ​

bash
# 文件位置由初始化时的 -D 参数决定,通常为:
#   Debian/Ubuntu: /etc/postgresql/18/main/pg_hba.conf
#   RHEL/CentOS:   /var/lib/pgsql/18/data/pg_hba.conf
#   macOS(brew):   /opt/homebrew/var/postgresql@18/pg_hba.conf
#   源码安装:      $PGDATA/pg_hba.conf

# 用 SQL 精确问出位置(最可靠)
psql -d postgres -c "SHOW hba_file;"
bash
# 权限要求:文件必须属于数据库运行用户,且不对其他用户开放写权限
sudo chown postgres:postgres /var/lib/pgsql/18/data/pg_hba.conf
sudo chmod 600 /var/lib/pgsql/18/data/pg_hba.conf

# 目录本身权限也要正确,否则 PostgreSQL 拒绝启动
sudo chmod 700 /var/lib/pgsql/18/data

生效方式 ​

bash
# 修改 pg_hba.conf 后,只需重载(reload),不会中断现有连接
sudo systemctl reload postgresql

# 或使用 SQL
psql -d postgres -c "SELECT pg_reload_conf();"

# 或使用 pg_ctl
sudo -u postgres pg_ctl -D /var/lib/pgsql/18/data reload
bash
# 注意区分:修改 postgresql.conf 中需要重启的参数(如 listen_addresses、
# shared_buffers)时必须 restart,只有 pg_hba.conf 与少数参数可 reload
sudo systemctl restart postgresql

用 SQL 检查规则是否写对 ​

pg_hba_file_rules 视图会把已加载的规则以表格形式呈现,并在 error 列给出语法问题——这是排查配置的最快方法,不必逐行肉眼比对:

sql
-- 查看已加载的全部规则
SELECT line_number, type, database, user_name, address, auth_method, error
FROM pg_hba_file_rules
ORDER BY line_number;

-- 只看有语法错误或语义问题的规则(error 非空即有问题)
SELECT line_number, error
FROM pg_hba_file_rules
WHERE error IS NOT NULL;
text
# 输出示例
 line_number | type  | database |   user_name   |   address    |  auth_method  | error
-------------+-------+----------+---------------+--------------+---------------+-------
           1 | local | {all}    | {postgres}    |              | peer          |
           2 | local | {all}    | {all}         |              | scram-sha-256 |
           3 | host  | {all}    | {all}         | 127.0.0.1/32 | scram-sha-256 |
           4 | host  | {demo}   | {app_user}    | 10.0.0.0/8   | scrub-sha-256 | invalid authentication method "scrub-sha-256"
(4 rows)
sql
-- 修复后再次确认,并核对当前连接命中了哪条规则
SELECT line_number, type, database, user_name, address, auth_method
FROM pg_hba_file_rules
WHERE 'demo' = ANY(database) OR 'all' = ANY(database);

常见认证方法的安全等级 ​

方法抗窃听抗重放是否需要密码安全等级建议
trust否否否极低仅临时排障
peer是(本地套接字)是否中本地管理连接可用
ident否否否低不推荐新部署
md5否(若未加密)弱是低尽快迁移
scram-sha-256是是是高推荐默认
cert是是否(凭证书)最高高安全场景
reject————黑名单

md5 的问题不只是「摘要可被暴力破解」,更在于它的握手过程在现代威胁模型下不足以抵抗离线攻击。自 PostgreSQL 14 起,scram-sha-256 已是默认的 password_encryption,新集群应直接使用它。

实战示例 ​

示例一:把「裸奔」的默认配置收紧为生产可用 ​

默认安装后常见的一段配置是:

text
# ── 收紧前的危险配置 ──
# 危险!任意 IP 都能无密码以任意角色连接
host    all   all   0.0.0.0/0   trust
# 危险!明文口令上限极低
host    all   all   ::/0        md5

改造为:

text
# ── 收紧后的生产配置 ──
# TYPE      DATABASE  USER        ADDRESS         METHOD
local       all       postgres                    peer
local       all       all                         scram-sha-256
host        all       all         127.0.0.1/32    scram-sha-256
host        all       all         ::1/128         scram-sha-256
hostssl     demo      app_user    10.10.0.0/16    scram-sha-256
hostssl     demo      report_user 10.10.0.0/16    scram-sha-256
host        all       all         0.0.0.0/0       reject
host        all       all         ::/0            reject
bash
# 变更流程:备份 → 编辑 → 检查语法 → 重载 → 验证
cp /var/lib/pgsql/18/data/pg_hba.conf /var/lib/pgsql/18/data/pg_hba.conf.bak.$(date +%F)
sudo vi /var/lib/pgsql/18/data/pg_hba.conf
sudo -u postgres psql -d postgres -c "SELECT line_number, error FROM pg_hba_file_rules WHERE error IS NOT NULL;"
sudo systemctl reload postgresql
bash
# 验证:正确的连接应成功
psql "host=10.20.30.11 dbname=demo user=app_user sslmode=require" -c "SELECT current_user;"

# 验证:来自未授权网段应被拒绝(应看到 no pg_hba.conf entry)
psql "host=10.20.30.11 dbname=demo user=app_user hostaddr=203.0.113.5" -c "SELECT 1;"
# psql: error: connection ... FATAL:  no pg_hba.conf entry for host "203.0.113.5",
#       user "app_user", database "demo", no encryption

示例二:用 SSL 强制约束 + 客户端证书实现免密登录 ​

text
# 第 1 步:要求报告账号必须走 SSL,且证书 CN 必须等于数据库用户名
hostssl  demo  report_user  10.10.0.0/16  cert  clientcert=verify-full
# 第 2 步:其余仍用口令
hostssl  demo  app_user     10.10.0.0/16  scram-sha-256
bash
# 第 3 步:用证书连接(无需输入密码)
psql "host=db.example.com dbname=demo user=report_user \
      sslmode=verify-full sslrootcert=/etc/pki/ca.crt \
      sslcert=/etc/pki/report_user.crt sslkey=/etc/pki/report_user.key" \
     -c "\conninfo"
text
# \conninfo 输出应显示 SSL 已启用
You are connected to database "demo" as user "report_user" on host "db.example.com" (address "10.0.0.21") at port "5432".
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)

示例三:按「环境 × 角色」生成规则并做一致性校验 ​

sql
-- 用 SQL 检查「是否存在对公网开放的 trust 规则」——最危险的一类配置
SELECT line_number, type, database, user_name, address, auth_method
FROM pg_hba_file_rules
WHERE auth_method = 'trust'
  AND address IN ('0.0.0.0/0', '::/0', 'all');

-- 检查是否仍有 md5(应逐步淘汰)
SELECT line_number, user_name, address
FROM pg_hba_file_rules
WHERE auth_method IN ('md5', 'password');

-- 检查对外网暴露的口令认证(非 SSL)
SELECT line_number, database, user_name, address
FROM pg_hba_file_rules
WHERE type = 'host'
  AND auth_method LIKE '%scram%'
  AND address NOT IN ('127.0.0.1/32', '::1/128');
bash
# 把上述检查做成上线前的门禁脚本
#!/usr/bin/env bash
set -euo pipefail

PSQL="psql -d postgres -tAc"
UNSAFE=$(${PSQL} "SELECT count(*) FROM pg_hba_file_rules
                 WHERE auth_method = 'trust'
                   AND address IN ('0.0.0.0/0','::/0','all');")

if [ "${UNSAFE}" -ne 0 ]; then
  echo "拒绝上线:pg_hba.conf 存在对全网开放的 trust 规则"
  exit 1
fi

ERR=$(${PSQL} "SELECT count(*) FROM pg_hba_file_rules WHERE error IS NOT NULL;")
if [ "${ERR}" -ne 0 ]; then
  echo "拒绝上线:pg_hba.conf 存在加载失败的规则"
  exit 1
fi

echo "pg_hba.conf 检查通过"

注意事项 ​

  • 顺序即优先级,且不可回溯:第一条匹配的规则生效。宽泛规则写在前面会让后面所有精细规则变成死代码。具体→宽泛是唯一正确顺序。
  • 改完必须 reload 才生效:仅保存文件不会生效。忘记 reload 是「配置明明改了却没起作用」的头号原因。
  • host 同时匹配 SSL 与非 SSL:想让某来源必须走 SSL,必须写 hostssl 而不是 host。反过来,hostssl 规则不会拦截非 SSL 连接——那些连接会继续往下匹配,可能命中后面的 host 规则。
  • 0.0.0.0/0 不代表「所有地址」:它只覆盖 IPv4。要覆盖全部需同时写 ::/0(或用 all)。这是「以为封了其实没封」的常见坑。
  • reload 不踢掉现有连接:旧连接会保持原样。修改认证策略后若要立即生效,需主动断开旧连接。
  • peer 仅适用于 local:把它写在 host 行会加载报错(可在 pg_hba_file_rules.error 中看到)。
  • error 列非空的规则会被跳过:一条写错的规则不会阻止服务启动,但它对应的连接会「静默地」走到更后面的规则,可能产生意外的放行。任何改动后都要检查该视图。
  • 不要把 pg_hba.conf 交给配置管理工具随意覆盖:它与 postgresql.conf 常常由不同团队维护,覆盖式下发会丢掉手工加的精确规则。
  • 日志是最终裁决:报错信息(no pg_hba.conf entry for host ...)本身就说明「没有任何规则命中」,这通常不是密码问题,而是地址/库名/用户名写错了。
bash
# 打开认证相关的详细日志(postgresql.conf),便于定位
# log_connections = on
# log_disconnections = on
# log_line_prefix = '%m [%p] %q%u@%d %r '
sudo systemctl reload postgresql
sudo tail -f /var/log/postgresql/postgresql-18-main.log
  • 阶段十六会深入认证机制:本节只覆盖「怎么写、怎么生效」的工程实践。认证协议细节(SCRAM 握手、GSSAPI、LDAP 集成、行级安全的配合)会在 16.2 客户端认证中展开,届时可结合本节配置对照理解。

最佳实践 ​

  • 默认拒绝,白名单放行:不写兜底 0.0.0.0/0 reject 也能拒绝,但显式写出能让意图可读、便于 review。
  • 一行一意图,并写注释:每个规则的用途、责任团队、变更日期写清。pg_hba.conf 是少数需要人肉 review 的配置。
  • 生产禁用 trust 与 md5:新集群直接用 scram-sha-256;存量集群制定迁移计划,先 ALTER ROLE 重设密码(使其以 SCRAM 存储),再切换方法。
  • 本地管理用 peer,远程管理用 scram-sha-256 + hostssl:管理连接要给最窄的地址范围。
  • 优先 hostssl 而非 host:让「加密」成为默认而非可选项。配合 sslmode=require 可有效防止降级。
  • 配置纳入版本控制:pg_hba.conf 用 Git 管理(注意不要提交含敏感地址的注释),每次变更留 commit 记录,便于回滚与审计。
  • 变更走「备份 → 检查 → 重载 → 验证」四步:永远先在测试环境演练,尤其是收紧类变更,否则可能把自己也挡在外面。
  • 给自己留后路:变更前确认还有一个可用的本地 peer 通道(如 sudo -u postgres psql),避免改错后完全无法进入。
bash
# 变更前的自保检查:确认本地 peer 通道可用
sudo -u postgres psql -d postgres -c "SELECT 'peer ok';"

# 变更后立刻复验该通道(若失败,恢复备份并 reload)
sudo -u postgres psql -d postgres -c "SELECT 'peer still ok';"

下一节

继续学习:监听地址与端口配置

参考链接 ​