环境:pgcm1(10.0.0.231:5432),以 OS 用户
postgres登录操作
数据库版本:PostgreSQL 18.6(psql (18.6))
虚拟化平台:VMware Workstation;NAT 网段10.0.0.0/24(VMnet8),Host-Only 网段10.0.10.0/24(VMnet1)
整理日期:2026-11-07(含宿主机访问章节与实操排查记录)
一、故障现象
[postgres@pgcm1 data]$ psql 10.0.0.231 -p 5432 -d postgres
psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed:
FATAL: role "10.0.0.231" does not exist
[postgres@pgcm1 data]$ psql localhost -p 5432 -d postgres
psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed:
FATAL: role "localhost" does not exist
[postgres@pgcm1 data]$ psql 127.0.0.1 -p 5432 -d postgres
psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed:
FATAL: role "127.0.0.1" does not exist
三条命令全部报同一个角色不存在的错误,且都指向 Unix socket(/tmp/.s.PGSQL.5432)而不是 TCP。
二、根因分析
2.1 psql 的位置参数是 dbname,不是 host
psql 的语法是:
psql [选项] [dbname [username]]
第一个位置参数一律被当成数据库名,主机必须用 -h 显式指定。
所以 psql 10.0.0.231 -p 5432 -d postgres 的实际行为是:
10.0.0.231→ 被解析为 dbname(随后又被后面的-d postgres覆盖/冲突)- 没有
-h→ 客户端使用默认的 Unix domain socket - 该字符串被当作连接标识参与认证,于是服务端报
role "10.0.0.231" does not exist
localhost 和 127.0.0.1 完全同理,三者错误表现一致,正说明问题不在网络、不在 pg_hba,而在命令写法。
2.2 为什么报 role 而报 database
服务端认证阶段先解析角色,角色不存在即在认证层直接 FATAL 退出,还没走到数据库存在性检查。所以看到 role ... does not exist 并不代表 pg_hba 有问题。
2.3 一个判断口诀
| 报错内容 | 指向的环节 |
|---|---|
role "xxx" does not exist | 客户端发出的用户名不对(-U 或位置参数被误用) |
database "xxx" does not exist | 角色 OK,库名不对 |
no pg_hba.conf entry for host "x.x.x.x" | 角色/库都可能 OK,服务端没放行该来源 IP |
password authentication failed | 认证方法或密码存储格式不匹配(见第四节的 md5/SCRAM 陷阱) |
Peer authentication failed | 走了 socket,local 是 peer 且 OS 用户与角色名不一致 |
connection refused | listen_addresses 未含该地址,或防火墙/端口不通 |
三、正确写法
# 走 TCP 连远端/本机业务 IP
psql -h 10.0.0.231 -p 5432 -U postgres -d postgres
psql -h 127.0.0.1 -p 5432 -U postgres -d postgres
psql -h localhost -p 5432 -U postgres -d postgres
# 走本地 Unix socket(peer/trust 认证,OS 用户 postgres 直接匹配同名角色)
psql -d postgres
# URI 形式,一条搞定
psql "postgresql://postgres@10.0.0.231:5432/postgres"
补充要点:
-h之后不带-U时,默认用户名仍是 OS 用户名(此处为postgres),不是 dbname。- 避免交互提示密码可用
PGPASSWORD环境变量或~/.pgpass(权限须为 600)。 - 排查时加上
-v ON_ERROR_STOP=1便于脚本化判断退出码。
四、pg_hba.conf 逐条解析
4.1 规则格式与匹配语义
连接类型 数据库 角色 来源地址 认证方法
host all all 10.0.0.231/32 trust
核心语义:从上往下匹配,第一条命中的规则立即生效并停止后续查找。
不存在”最精确优先”,只有”先出现优先”。这一点决定了本文后面几乎所有问题的成因。
列含义补充:
host同时覆盖 IPv4 与 IPv6 的 TCP 连接;local只覆盖 Unix domain socket。- 数据库列
all不包含replication这个伪数据库,复制连接必须单独成行。 hostssl/hostnossl用于按 SSL 与否分流,本文件未使用。
4.2 本文件实际内容逐条拆解
# "local" is for Unix domain socket connections only
local all all trust
# IPv4 local connections:
host all all 127.0.0.1/32 trust
host all all 10.0.0.231/32 trust
host all all 0.0.0.0/24 trust
# IPv6 local connections:
host all all ::1/128 trust
# Allow replication connections from localhost, by a user with the
# replication privilege.
local replication all trust
host replication all 127.0.0.1/32 trust
host replication all ::1/128 trust
host all all 0.0.0.0/0 md5
host all all 0.0.0.0/0 scram-sha-256
| # | 配置行 | 解析 |
|---|---|---|
| 1 | # "local" is for Unix domain socket connections only | 默认模板注释,说明 local 类型仅对 Unix socket 生效(/tmp/.s.PGSQL.5432、/var/run/postgresql/.s.PGSQL.5432) |
| 2 | local all all trust | socket 连接、所有库、所有角色、免密。任何能访问该 socket 的 OS 用户都可自称任意角色(含 postgres 超级用户),是本机最大越权面 |
| 3 | host all all 127.0.0.1/32 trust | 仅本机 IPv4 回环,/32 为精确匹配单个 IP,免密 |
| 4 | host all all 10.0.0.231/32 trust | 仅来源 IP 恰为 10.0.0.231(本机业务 IP)的 TCP 连接,免密 |
| 5 | host all all 0.0.0.0/24 trust | 写错。0.0.0.0/24 = 0.0.0.0 ~ 0.0.0.255,属保留的无效/未指定网段,真实客户端 IP 永不落入此区间,该行永不命中(死规则)。原意应为 10.0.0.0/24 |
| 6 | host all all ::1/128 trust | IPv6 回环 ::1 精确匹配,免密 |
| 7 | local replication all trust | socket 上的物理/流复制连接,免密。仅覆盖 socket,pg_basebackup -h 走 TCP 不受此行保护;角色自身还需 REPLICATION 权限或超级用户 |
| 8 | host replication all 127.0.0.1/32 trust | 回环 IPv4 上的复制连接,免密 |
| 9 | host replication all ::1/128 trust | 回环 IPv6 上的复制连接,免密 |
| 10 | host all all 0.0.0.0/0 md5 | 兜底规则 1:任意 IPv4 来源,md5 方式认证。因位置靠前,实际是所有非本机 IPv4 客户端的最终归宿 |
| 11 | host all all 0.0.0.0/0 scram-sha-256 | 兜底规则 2:任意 IPv4 来源用 SCRAM-SHA-256。但被第 10 行完全遮蔽,永远轮不到,属死规则 |
五、问题清单(按危害排序)
1)第 10、11 行互相打架,md5 屏蔽了 scram —— 最隐蔽的坑
PostgreSQL 14 起 password_encryption 默认即为 scram-sha-256,角色密码存的是 SCRAM 验证子($SCRAM-SHA-256$...)。
而 md5 认证方法无法校验 SCRAM 存储的密码。后果:远端 10.0.0.x 的客户端即使密码完全正确,也会收到 password authentication failed,且日志里看不出所以然。
处理:删除第 10 行,或把两行合并为一条
host all all 0.0.0.0/0 scram-sha-256。
2)0.0.0.0/24 是无效网段
应改为 10.0.0.0/24。本意大概是”同网段免密”,但实际等于没写;同网段其他机器会落到 md5 兜底,行为与预期不一致。
结合真实拓扑(VMware NAT 网段即
10.0.0.0/24),这一行正是宿主机访问所需要的规则,修正方式与完整步骤见第八章。
3)大面积 trust
第 2、4 行均为 all + trust:能建 socket 或能从 10.0.0.231 发起连接者,可任意指定角色名进入,包括 postgres。
生产环境建议:
local改peer(OS 用户名须与角色名一致);host ... 10.0.0.231/32改scram-sha-256。
4)IPv6 客户端会被直接拒绝
0.0.0.0/0 在 pg_hba 中只匹配 IPv4,不匹配 IPv6。因此除 ::1 外的 IPv6 客户端无任何规则命中,报 no pg_hba.conf entry for host ...。不需要 IPv6 可不配,需要则补 host all all ::0/0 scram-sha-256。
5)复制规则只覆盖本机
第 7–9 行只允许回环与 socket 复制。若要加备库(另一台 10.0.0.x),pg_basebackup -h 10.0.0.231 会落到 md5 兜底,同样可能因 SCRAM 存储而失败。
6)缺少 hostssl
若要求传输加密,需显式使用 hostssl 并对 host 收紧,否则明文连接仍被放行。
六、建议改写版本
# ===== 本地 Unix socket:用 peer,比 trust 安全 =====
local all postgres peer
local all all peer
# ===== 本机回环 =====
host all all 127.0.0.1/32 scram-sha-256
host all all ::1/128 scram-sha-256
# ===== 同网段:明确写 10.0.0.0/24,切勿写 0.0.0.0/24 =====
host all all 10.0.0.0/24 scram-sha-256
host all all 10.0.0.231/32 trust # 仅内部自动化/监控
# ===== 复制:显式放行备库网段 =====
host replication replicator 10.0.0.0/24 scram-sha-256
local replication all peer
# ===== 兜底:只留一条,方法统一 =====
host all all 0.0.0.0/0 scram-sha-256
host all all ::0/0 scram-sha-256
要点:
- 兜底只保留一条,避免”前一条遮蔽后一条”。
- 认证方法统一为
scram-sha-256,与 PG14+ 的密码存储默认保持一致。 peer要求 OS 用户名与数据库角色名相同,运维脚本里注意保持一致。
七、生效与验证
7.1 重载(无需重启)
SELECT pg_reload_conf();
或命令行:
pg_ctl reload -D $PGDATA
7.2 校验规则(PG10+,能直接定位到行号级错误)
SELECT line_number, type, database, user_name, address, auth_method, error
FROM pg_hba_file_rules
ORDER BY line_number;
-- 只列有问题的行
SELECT line_number, error FROM pg_hba_file_rules WHERE error IS NOT NULL;
7.3 连通性实测
psql -h 10.0.0.231 -U postgres -d postgres # 应命中 10.0.0.231/32 trust,免密
psql -h 10.0.0.231 -U app_user -d appdb # 应命中兜底,提示输入密码
psql -d postgres # 走 socket,按 local 规则
7.4 服务端起监听确认
ss -lntp | grep 5432
# 期望看到 10.0.0.231:5432 或 0.0.0.0:5432,否则检查 postgresql.conf 的 listen_addresses
八、宿主机访问配置(VMware Workstation + NAT 拓扑)
8.1 拓扑事实
VMnet8(NAT) 10.0.0.0/24
宿主机 VMnet8 适配器 10.0.0.1
NAT 网关(虚拟路由器) 10.0.0.2 ← guest 的默认网关
guest 业务网卡 10.0.0.231 ← PostgreSQL 监听在此
VMnet1(Host-Only) 10.0.10.0/24
宿主机 VMnet1 适配器 10.0.10.1
8.2 关键结论:VMware NAT 与 VirtualBox NAT 语义不同
- VirtualBox 的 NAT:guest 藏在 NAT 后面,宿主机无法直达,必须做
--natpf1端口转发。 - VMware 的 NAT(VMnet8):宿主机上存在一块同名虚拟适配器,与 guest 同处一个虚拟交换网段。宿主机发起的是同网段直连(不经过 NAT 设备的地址转换),因此 只要 VMnet8 适配器位于
10.0.0.0/24内,宿主机即可直接访问10.0.0.231,无需端口转发。
换言之,本例中”精力该花在 guest 的
listen_addresses/pg_hba.conf/ 防火墙三处”,而不是网络映射。
8.3 宿主机侧先验证网络与端口
# 确认 VMnet8 适配器地址,期望 10.0.0.1/24
Get-NetIPAddress -AddressFamily IPv4 |
Where-Object { $_.InterfaceAlias -like "*VMnet8*" } |
Select-Object InterfaceAlias, IPAddress, PrefixLength
# 网络层
ping 10.0.0.231
# 端口层
Test-NetConnection -ComputerName 10.0.0.231 -Port 5432
TcpTestSucceeded : True 说明网络与端口均通,后续失败只可能是认证问题。
若 ping 不通,用 VMware「编辑 → 虚拟网络编辑器」核对:VMnet8 应为 NAT 模式、子网地址为 10.0.0.0、掩码 255.255.255.0,并确认”将虚拟网络连接到”未指向错误的物理网卡。
8.4 guest 侧三件套
① 监听地址(postgresql.conf)
listen_addresses = 'localhost,10.0.0.231'
# 实验环境可写 '*',生产环境不要
修改
listen_addresses必须重启(pg_ctl restart -D $PGDATA),reload 不生效。
ss -lntp | grep 5432 # 必须能看到 10.0.0.231:5432
② pg_hba.conf:把写错的那条修成真正的网段
宿主机发起的连接,其来源 IP 显示为 10.0.0.1(宿主机 VMnet8 适配器地址)。原先那条 0.0.0.0/24(无效网段,详见第五节第 2 条)应替换为:
host all all 10.0.0.0/24 scram-sha-256
# 或精确到单机
host all all 10.0.0.1/32 scram-sha-256
- 不要用
trust:同网段任何主机都可免密以postgres超级用户进入。 - 不要用
md5:PG14+ 默认 SCRAM 密码存储,md5方法无法校验(详见第五节第 1 条)。
③ 防火墙
firewall-cmd --list-all
firewall-cmd --add-port=5432/tcp --permanent && firewall-cmd --reload
# 临时开放:firewall-cmd --add-port=5432/tcp
④ 重载并校验
SELECT pg_reload_conf();
SELECT line_number, type, database, user_name, address, auth_method, error
FROM pg_hba_file_rules ORDER BY line_number;
8.5 确认真实来源地址(不要凭猜写规则)
宿主机连接成功后,在 guest 上执行:
SELECT inet_client_addr(), inet_client_port(), usename, datname
FROM pg_stat_activity
WHERE pid = pg_backend_pid();
inet_client_addr() 返回的就是 pg_hba 实际匹配所用的来源地址。若走的是 NAT 端口映射,该值会变成 NAT 网关 10.0.0.2;若是同网段直连,则会显示 10.0.0.1。
8.6 宿主机侧实测
Test-NetConnection -ComputerName 10.0.0.231 -Port 5432
psql -h 10.0.0.231 -p 5432 -U postgres -d postgres
只装了图形客户端(pgAdmin / DBeaver)时:主机 10.0.0.231、端口 5432、库 postgres、用户 postgres,认证方式选密码(SCRAM),不要选 md5。注意别把宿主机本地的 127.0.0.1:5432 实例与 guest 混淆。
8.7 备选 A:改走 Host-Only 网段 10.0.10.0/24(长期更稳)
NAT 网段依赖 VMware DHCP,guest IP 可能漂移;Host-Only 不依赖外部网络,拓扑更干净,也便于后续加备库、加实验机。
- 关机,给虚拟机加第二块网卡,模式选「仅主机(Host-Only)」,自定义网络指向 VMnet1。
- 在 guest 内配置静态 IP:
nmcli con add type ethernet conname hostonly ifname eth1 \
ipv4.method manual ipv4.addresses 10.0.10.101/24
nmcli con up hostonly
ip addr show eth1
- 修改监听地址(重启生效):
listen_addresses = 'localhost,10.0.0.231,10.0.10.101'
- pg_hba 追加:
host all all 10.0.10.0/24 scram-sha-256
- 重启 PostgreSQL,防火墙已放行 5432 则无需再改。
- 宿主机验证:
ping 10.0.10.101→Test-NetConnection -Port 5432→psql -h 10.0.10.101 ...。
前提:宿主机
VMware Network Adapter VMnet1必须位于10.0.10.1/24,否则需在虚拟网络编辑器中把 VMnet1 子网改为10.0.10.0/24。
8.8 备选 B:vmnetnat.conf 端口映射(仅在直连确实不通时使用)
编辑 C:\ProgramData\VMware\vmnetnat.conf:
[incomingtcp]
15432 = 10.0.0.231:5432
Restart-Service -Name "VMware NAT Service" -Force
Test-NetConnection -ComputerName 127.0.0.1 -Port 15432
- 宿主机端口不要用 5432,避免与宿主机本地 PostgreSQL 实例冲突(本例统一用 15432)。
- 此方式下 guest 看到的来源是 NAT 网关
10.0.0.2,pg_hba 需额外放行10.0.0.2/32。 - 既然宿主机本就在
10.0.0.0/24内,绕这一圈并不划算,仅作保底。
8.9 排错对照表(宿主机侧视角)
| 现象 | 根因 |
|---|---|
ping 10.0.0.231 不通 | VMnet8 适配器不在 10.0.0.0/24;虚拟网络编辑器配置异常;guest 网卡未”已连接” |
ping 通但 Test-NetConnection 失败 | listen_addresses 未含 10.0.0.231(改后是否重启?);firewalld 拦截;用 ss -lntp 复核 |
no pg_hba.conf entry for host "10.0.0.1" | pg_hba 缺 10.0.0.0/24 或 10.0.0.1/32(原 0.0.0.0/24 那条是无效的) |
password authentication failed | hba 走 md5,而角色密码为 SCRAM 存储 |
宿主机 127.0.0.1:5432 能连但不是这个库 | 连到了宿主机本地实例,非 guest;改用 10.0.0.231 或映射端口 |
8.10 最小改动清单(只求宿主机能连)
- 将 pg_hba 中
0.0.0.0/24 trust改为10.0.0.0/24 scram-sha-256,SELECT pg_reload_conf(); - 确认
listen_addresses含10.0.0.231,然后 重启 PostgreSQL firewall-cmd --add-port=5432/tcp --permanent && firewall-cmd --reload- 宿主机
Test-NetConnection 10.0.0.231 -Port 5432,再psql -h 10.0.0.231 -U postgres -d postgres
全程不需要端口转发 —— 宿主机的 VMnet8 适配器与 guest 同网段,属于直连。
实操补充:本环境最终确认 firewalld 并未拦截 5432(由客户端报
fe_sendauth反证 TCP 已建立),但排查时仍应先执行firewall-cmd --state确认防火墙是否在运行,再决定是否需要放行。详见第九章 9.6。
九、实操排查记录(2026-11-07 已落地)
9.1 环境事实
| 项目 | 实际值 |
|---|---|
| 数据库版本 | PostgreSQL 18.6(psql (18.6)) |
| guest 主机名 | pgcm1 |
| guest 业务网卡 | 10.0.0.231(VMnet8 / NAT 网段) |
| VMnet8 网关(NAT 虚拟路由器) | 10.0.0.2 |
| 宿主机 VMnet8 适配器 | 10.0.0.1(同网段直连,无需端口转发) |
| Host-Only 网段 | 10.0.10.0/24(VMnet1) |
| 客户端 | Windows 宿主机上的 pgAdmin(Register – Server 表单) |
9.2 操作时间线(含每一步的判定依据)
| # | 操作 | 实际输出 / 结果 | 判定 |
|---|---|---|---|
| 1 | 首次追加 host all all 10.0.0.0/24 scram-sha-256 到文件末尾(130 行),pg_ctl reload | server signaled,日志仅 LOG: received SIGHUP, reloading configuration files | 语法解析通过,但新行位于 0.0.0.0/0 md5 之后,被遮蔽、实际不生效 |
| 2 | 重新排序:注释掉 0.0.0.0/0 md5,把 10.0.0.0/24 提到 0.0.0.0/0 scram-sha-256 之前,pg_ctl reload | 同上,无 WARNING | 顺序修正,第 129 行成为本网段的实际生效规则 |
| 3 | SELECT ... FROM pg_hba_file_rules ORDER BY line_number DESC LIMIT 5; | 130 行 0.0.0.0 scram-sha-256、129 行 10.0.0.0 scram-sha-256、126 行 ::1 trust、125 行 127.0.0.1/32 trust、124 行 local all trust;error 列全空 | 语法无错、顺序正确 |
| 4 | SHOW listen_addresses; | 0.0.0.0 | 已监听全部接口(PG18 默认即为 * 等价形式) |
| 5 | ss -lntp | grep 5432 | LISTEN 0 200 0.0.0.0:5432 0.0.0.0:* users:(("postgres",pid=7855,fd=6)) | TCP 监听已生效,无需重启 |
| 6 | 以 postgres 用户执行 firewall-cmd --list-ports | Authorization failed.(polkit 限制) | 普通用户无权查防火墙,须 root |
| 7 | 切 root 执行 firewall-cmd --list-ports | 空输出(命令本身执行成功) | 未见显式开放端口(后经第 10 步反证:实际未拦截) |
| 8 | 查询 pg_authid.postgres 的 rolpassword 前缀 | SCRAM | 密码存储格式与 hba 的 scram-sha-256 匹配 |
| 9 | 宿主机 pgAdmin 连接 10.0.0.231:5432(用户名/维护库均为 postgres,Password 留空) | 见 9.4 的两段报错 | 链路已通,仅客户端未发送密码 |
| 10 | pgAdmin 中开启 Save Password 并填入密码后重连 | 连接建立 | 认证通过,闭环 |
9.3 最终 pg_hba.conf 尾部实际配置
# 124 行
local all all trust
# 125 行
host all all 127.0.0.1/32 trust
# 126 行
host all all ::1/128 trust
# ...(复制相关三行略)
# 129 行 ← 宿主机(10.0.0.1)实际命中的规则
host all all 10.0.0.0/24 scram-sha-256
# 130 行 ← 兜底
host all all 0.0.0.0/0 scram-sha-256
# 已注释:host all all 0.0.0.0/0 md5
文件状态:"pg_hba.conf" 130L, 5826C written。
9.4 pgAdmin 报错逐句解读
Unable to connect to server:
connection failed: connection to server at "10.0.0.231", port 5432 failed:
could not initiate GSSAPI security context:
No credentials were supplied, or the credentials were unavailable or inaccessible:
Credential cache is empty
connection to server at "10.0.0.231", port 5432 failed:
fe_sendauth: no password supplied
| 报错片段 | 含义 | 是否真因 |
|---|---|---|
could not initiate GSSAPI security context ... Credential cache is empty | libpq 默认 gssencmode=prefer,先尝试 GSSAPI 加密连接;Windows 无 Kerberos 票据 → 失败后自动回退 | 否,纯噪声 |
fe_sendauth: no password supplied | TCP 已建立、服务端已请求认证,但客户端未发送任何密码 | 是 |
关键判读:fe_sendauth 是客户端侧报错,出现在网络层/端口层/服务端认证请求全部成功之后。它同时反证了三件事:端口可达、listen_addresses 生效、pg_hba 已命中并进入密码认证分支。
根因:pgAdmin 4 在编辑模式下,Password 字段只有在 Save Password = True 时才可编辑(界面上那句灰色提示即此意)。未开启时不会弹密码框,而是直接以空密码发送,必然 no password supplied。
处理顺序(不可颠倒):
- 打开 Password 输入框右侧的 Save Password(锁形图标);
- 此时 Password 字段才变为可编辑,填入
postgres的 SCRAM 密码; - 点击 Save;
- 重新 Connect。
9.5 消除 GSSAPI 噪声(可选)
在宿主机(pgAdmin 运行环境)设置为用户级环境变量,重启 pgAdmin 生效:
[Environment]::SetEnvironmentVariable('PGGSSENCMODE','disable','User')
之后报错中不再出现 GSSAPI 段落,只剩真实原因,便于判断。
9.6 结论与遗留项
已确认通过
pg_hba.conf:语法无误、顺序正确,10.0.0.0/24排在兜底0.0.0.0/0之前(第 129 行 vs 第 130 行)。- 监听:
listen_addresses = 0.0.0.0,ss显示0.0.0.0:5432 LISTEN。 - 认证格式:
postgres角色密码为SCRAM,与 hba 方法一致,无 md5/SCRAM 错配。 - 网络:宿主机经 VMnet8 同网段直连,未使用任何端口转发。
- firewalld:实际未拦截 5432——由
fe_sendauth反证 TCP 已握手成功。此前”防火墙待放行”的判断不成立,需以firewall-cmd --state的实测状态为准,避免在笔记中留下错误结论。
遗留项(未做,建议后续处理)
local all all trust、127.0.0.1/32 trust、::1/128 trust仍为免密,本机任意 OS 用户可冒用任意角色(含超级用户)。生产化时应改为peer+scram-sha-256。10.0.0.231/32 trust仍在,本机自连免密;如需统一认证口径,一并改为scram-sha-256。- 复制相关三行仅覆盖回环,若后续加备库(
10.0.0.x)需显式放行该网段。 - 未验证 IPv6 客户端路径;
0.0.0.0/0仅匹配 IPv4。 pg_stat_activity的inet_client_addr()尚未记录实际取值,闭环确认时补一次。
9.7 闭环验收清单(可直接复制)
guest 侧(postgres 用户)
SELECT line_number, type, user_name, address, auth_method, error
FROM pg_hba_file_rules ORDER BY line_number DESC LIMIT 5;
SELECT line_number, error FROM pg_hba_file_rules WHERE error IS NOT NULL;
SHOW listen_addresses;
SELECT rolname,
CASE WHEN rolpassword IS NULL THEN 'NULL'
WHEN rolpassword LIKE 'SCRAM-SHA-256%' THEN 'SCRAM'
WHEN rolpassword LIKE 'md5%' THEN 'md5'
ELSE 'other' END AS pwd_type
FROM pg_authid WHERE rolname = 'postgres';
ss -lntp | grep 5432
firewall-cmd --state # 以 root 执行
firewall-cmd --list-ports # 以 root 执行
宿主机侧(Windows PowerShell)
Get-NetIPAddress -AddressFamily IPv4 |
Where-Object { $_.InterfaceAlias -like "*VMnet8*" } |
Select-Object InterfaceAlias, IPAddress, PrefixLength
Test-NetConnection -ComputerName 10.0.0.231 -Port 5432
连接成功后回查来源(pgAdmin Query Tool / psql)
SELECT inet_client_addr() AS client_ip,
inet_client_port() AS client_port,
usename, datname
FROM pg_stat_activity
WHERE pid = pg_backend_pid();
期望:client_ip = 10.0.0.1(宿主机 VMnet8 地址)。
十、经验总结
psql第一个位置参数是 dbname,主机必须用-h—— 本次故障的唯一根因。- 报
role ... does not exist说明问题在客户端用户名,不在网络和 pg_hba,可快速排除大范围排查方向。 - pg_hba 是首条命中即生效,追加规则前必须确认前面没有吞掉来源的规则;两条
0.0.0.0/0并存几乎必然有一条是死代码。 md5认证方法与 SCRAM 密码存储不兼容,PG14+ 环境混写会导致”密码明明对却认证失败”。- CIDR 写错(
0.0.0.0/24)不会报错,只会静默失效,改配置后务必用pg_hba_file_rules复核。 - 改完用
SELECT pg_reload_conf()生效,pg_ctl restart不是必需的;但listen_addresses变更例外,必须重启。 - 不要想当然套用 VirtualBox 的 NAT 结论:VMware 的 VMnet8 让宿主机与 guest 同网段直连,多数”宿主机访问不了 guest 服务”的真实原因是 guest 端监听地址、pg_hba 或防火墙,而不是缺端口映射。
- 判断来源 IP 用
SELECT inet_client_addr();实测,比按经验推断更可靠 —— pg_hba 规则必须与实际来源地址对齐才生效。 - 长期使用优先 Host-Only 网段(如
10.0.10.0/24),可避开 NAT 网段 DHCP 导致的 IP 漂移。 - “追加到文件末尾”是 pg_hba 改配置的头号陷阱。
sed -i '$a'、>>追加看似省事,结果落在兜底规则之后,reload 无错、日志无 WARNING,但规则静默失效——这种”看起来成功”的失败最难发现,必须用line_number显式核对顺序。 fe_sendauth: no password supplied是链路已通的好消息:它发生在 TCP 握手与服务端认证请求之后,说明端口、监听、pg_hba 全部 OK,问题只在客户端凭证。不要把它误判为网络或防火墙故障。- GSSAPI 报错是回退噪声:
gssencmode=prefer在 Windows 无 Kerberos 票据时必然失败一次再回退到密码认证;PGGSSENCMODE=disable可静音,避免掩盖真实原因。 - pgAdmin 4 编辑模式下,未开启 Save Password 时密码框不可编辑,客户端会直接以空密码发送。操作顺序固定为:开 Save Password → 填密码 → Save → Connect。
firewall-cmd由普通用户执行会报Authorization failed(polkit 限制),须 root;且--permanent必须搭配--reload才生效。- 最终结论要由实测反证,而不是由中间命令的”看起来正常”推断。本例中
--list-ports为空曾让人以为防火墙是拦截点,实际被fe_sendauth推翻。记录结论时应同时写下推翻它的证据来源。 - PostgreSQL 18 环境下
md5已是淘汰路径,兜底统一用scram-sha-256,且改动前先查pg_authid.rolpassword前缀,避免方法/密码格式错配。 - 固定排查顺序可显著降低来回试的成本:客户端参数 → 规则顺序 → 监听地址 → 防火墙 → 密码格式 → 客户端凭证,每一层都有可判定的输出(
pg_hba_file_rules/ss/pg_authid/ 报错文案)。

徐万新之路

