ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

PostgreSQL连接失败?一文彻底搞懂pg_hba.conf配置与排查方法

PostgreSQL连接失败?一文彻底搞懂pg_hba.conf配置与排查方法 先交代一个背景我平时习惯用 Java 写后端数据库这块基本就是 PostgreSQL。前段时间搭建测试环境新装了一台 PostgreSQL结果本地用 psql 连过去直接报了一串乱码错误org.postgresql.util.PSQLException: : û “192.168.1.101“,乍一看一脸懵但把乱码翻译过来其实就是一句话没有匹配 pg_hba.conf 的条目。这类连接失败问题在 PostgreSQL 运维里实在太常见了尤其是第一次从远程客户端连数据库的时候十个里面有八个都是这个原因。这篇内容我就围绕这个报错详细拆解一下从原理到排查再到配置修复尽量让你看完之后不光能解决手头的问题以后遇到类似的连接问题也能有自己的判断思路。1. 报错信息拆解先看懂这串“天书”在说什么1.1 PSQLException 与 pg_hba.conf 的关系我们得先搞清楚这个报错是怎么来的。Java 程序里看不到具体中文因为 JDBC 驱动抛出的异常类叫org.postgresql.util.PSQLException它只是 PostgreSQL JDBC 驱动的通用异常外壳。真正有价值的信息在后面的消息文本里也就是那串乱码。大多数情况下乱码是因为服务端返回的错误信息用了服务端本地化语言而客户端连接串里没指定编码Windows 控制台默认 GBK 解码 UTF-8 文本于是显示成一堆问号。把乱码还原之后错误本质非常清晰错误: 没有匹配 pg_hba.conf 的条目这句话直接告诉我们PostgreSQL 服务端在检查访问控制规则时没有找到一条允许当前客户端连接的规则于是拒绝了连接请求。换句话说这不是密码错误不是用户名错误也不是数据库不存在而是你的客户端 IP、数据库、用户名、连接方式这四者的组合没有命中任何一条白名单规则。很多人一看到PSQLException就怀疑是驱动问题、密码问题实际上这时候最应该做的是打开 PostgreSQL 数据目录下的pg_hba.conf文件看一眼。pg_hba.conf就是 PostgreSQL 的访问控制白名单全称是“host-based authentication”配置文件。所有客户端连接不管本地还是远程都必须匹配它里面的一条规则否则连接就会被直接拒绝进程都不会进入密码校验阶段。这就解释了为什么即使你密码完全正确依然连不上。1.2 乱码问题客户端字符集带来的二次干扰我之所以要多说一句乱码的事是因为很多人第一次遇到这个报错时会被那一串问号带偏思路以为是什么编码不兼容引起的深层次问题。实际操作中如果控制台显示乱码有两个办法快速确认内容。第一个办法是看报错的最后一部分一般会带上客户端的 IP 地址比如“192.168.1.101“这个 IP 就是被拒绝的来源地址。第二个办法是把客户端的编码强制改成 UTF-8很多数据库管理工具和终端都支持设置客户端编码psql 可以用SET client_encoding TO UTF8JDBC 连接串加?characterEncodingUTF-8。但这里要提醒一句改客户端编码只是让报错信息可读并不能修饰服务端的判断逻辑。也就是说即便你在客户端把编码调对了看到的信息仍然是“没有匹配 pg_hba.conf 的条目”问题不会自动消失。所以正确的姿势是第一确认报错是不是这个第二拿着报错里的 IP 去检查pg_hba.conf。如果报错里的 IP 是你自己的 IP而配置里确实没放行那这事基本上就定位清楚了。2. 快速排查流程连接失败不全是 pg_hba.conf 的锅2.1 从网络到数据库一步步缩小问题范围pg_hba.conf是常见原因但绝对不是唯一原因。远程连接 PostgreSQL 的完整链路是客户端通过网络访问服务器 IP服务器上的 PostgreSQL 进程监听某个端口收到连接后校验访问控制规则然后才校验用户名密码。这个链路里任何一环出问题表现都是“连接失败”但报错细节不一样。我自己排查的时候习惯按这个顺序走先确认网络通不通再确认端口通不通再确认 PostgreSQL 有没有在监听最后才去看访问控制。如果网络都不通你连报错都看不到只会看到Connection timed out或者Connection refused。而PSQLException: 没有匹配 pg_hba.conf 的条目这个报错有一个特点它意味着你已经过了网络层成功触达了 PostgreSQL 服务端否则根本不会进入到访问控制校验这一步。有个小技巧用telnet或者nc测端口最直接。比如 PostgreSQL 默认端口是 5432可以在客户端机器上执行telnet 192.168.1.200 5432如果端口通了会立即收到 PostgreSQL 的欢迎信息或者直接黑屏等待这说明 TCP 连接是通的。如果网络不通会卡住直到超时如果端口没监听会显示连接被拒绝。这个测法比连数据库本身更快因为不需要输入任何用户名密码也能排除大部分网络因素。知道这一点你在排查的时候就不会一头扎进pg_hba.conf里瞎改。2.2 listen_addresses 与防火墙的双重影响网络通了之后还有一个容易被忽略的配置项就是postgresql.conf里的listen_addresses。这个参数控制 PostgreSQL 监听哪些网络接口。默认情况下很多安装包把listen_addresses设成localhost这意味着就算防火墙全开外部 IP 也没法连进来因为服务端根本没在外部网卡上监听。有时候你会遇到一个很奇怪的现象本机psql能连远程连不上。这种情况十有八九是listen_addresses没改。检查方法很简单在 PostgreSQL 服务器上执行SHOW listen_addresses;如果返回的是localhost而你的客户端是从其他机器发起的连接那就需要把它改成*或者改成具体的服务器 IP 地址。修改之后需要重启 PostgreSQL 服务才能生效这一步和pg_hba.conf的reload可不一样listen_addresses是启动时读取的参数不能热加载。防火墙也是老生常谈的问题了。很多云服务器默认安全组没放行 5432 端口或者本机防火墙拦了外部来源。判断办法就是在服务器上执行telnet 127.0.0.1 5432能通换成服务器公网 IP 或局域网 IP 就不通那基本就是防火墙层面的问题。这个环节容易和pg_hba.conf的问题混淆因为两者表现非常接近区别就在于报错细节如果连接被防火墙拦截客户端通常报Connection timed out如果连接到达了 PostgreSQL 但被访问控制拒绝报错则是我们开头看到的 PSQLException 加上 pg_hba 相关消息。学会从报错文案区分阶段排查效率能翻一倍。3. pg_hba.conf 配置详解一行放行十分钟搞定3.1 认识 pg_hba.conf 的条目格式聊完了排查链路接下来落到实际的配置文件。pg_hba.conf的位置取决于操作系统和安装方式。Linux 上如果是用 apt 安装的 PostgreSQL通常位于/etc/postgresql/版本号/main/pg_hba.conf如果是源码编译安装或者通过 Docker 部署一般在数据目录下比如/var/lib/postgresql/data/pg_hba.conf。文件里每一行非注释内容都是一条规则格式固定为五列或六列连接类型 数据库 用户 地址 认证方法还有一个可选列用来指定认证方法所需参数一般用不到看到最多的就是五列。我们来拆一下每一列的含义。连接类型主要有四种local本机通过 Unix Socket 连接。host通过 TCP/IP 连接不管是本机还是远程。hostssl仅允许 SSL 加密的 TCP/IP 连接。hostnossl仅允许非 SSL 的 TCP/IP 连接。数据库列可以写具体数据库名、用逗号分隔多个数据库名也可以用all表示所有数据库。用户列同理可以用all表示所有用户。地址列比较特殊它只在host类型中有效写法支持 IPv4 地址、IPv6 地址、CIDR 网段比如192.168.1.0/24表示整个 B 段子网也可以用0.0.0.0/0表示所有 IPv4 地址。认证方法这一列是核心决定这个连接怎么验证身份常见的有trust、reject、peer、md5、scram-sha-256等。文件里的规则是从上到下顺序匹配的匹配到第一条就不再看后面的规则了。也就是说文件的顺序很重要匹配优先权完全由行号决定。默认安装的 PostgreSQL 里文件末尾往往有一条host all all 0.0.0.0/0 scram-sha-256之类的规则如果前面的规则里没有你的组合最终会落到这条上面。如果这条规则用的是reject那不好意思直接拒绝。如果这条规则存在但认证方法不匹配那也会报认证失败。理解这个顺序逻辑后你就知道为什么有时候明明后面放行了某种连接前面一条更宽的规则反而先拦截了。3.2 常见认证方法选型trust、peer 与 scram-sha-256认证方法这块是很多人容易踩坑的地方。不同认证方法对应的登录方式和安全性差异非常大选错了要么导致连不上要么导致数据库裸奔。trust表示完全信任只要匹配这条规则的客户端连接不用输密码就能登录。这个认证方法只适合本地调试或者完全可信的内网环境生产环境千万别用。有人为了图省事把本机所有连接都配成trust结果数据库被局域网里其他人一个psql -U postgres就进去了数据随便看这是非常危险的操作。peer是 Linux 下本机 Socket 连接专用的认证方式它通过获取操作系统用户名来匹配数据库用户名同样不需要密码。比如你用系统用户postgres执行psql它会直接用postgres这个数据库角色登录前提是系统用户和数据库角色一致。这也是为什么很多人在服务器上用sudo -u postgres psql能直接进入数据库的原因。但peer只适用于local类型远程 TCP 连接不能用。md5和scram-sha-256都是密码认证方式区别在于密码存储和传输的加密算法。md5是旧版算法存在已知的安全缺陷PostgreSQL 14 之后的默认密码加密方式已经改成了scram-sha-256。如果你的数据库用户密码是用scram-sha-256算法存储的但pg_hba.conf里配的是md5认证就会失败反过来也一样。这里有一个常见误区看到密码错误其实不一定是密码本身错了而是密码版本和认证方法不一致。可以用下面这个 SQL 检查用户密码的存储方式SELECT rolname, rolpassword FROM pg_authid;rolpassword字段的开头会标明算法如果是SCRAM-SHA-256$开头说明这个用户的密码是 SCRAM 版本如果是md5开头则是 MD5 版本。新安装的 PostgreSQL 一般建议统一使用scram-sha-256。3.3 配置示例与生效方式看几个实际配置的例子。假设我的 PostgreSQL 服务器 IP 是192.168.1.200客户端 IP 是192.168.1.101本地调试用 Unix Socket远程连接需要密码登录。那pg_hba.conf里可以这样写# 本机 Socket 连接用 peer 认证方便管理员本地登录 local all all peer # 本机 TCP/IP 连接使用 scram 密码认证 host all all 127.0.0.1/32 scram-sha-256 # 局域网内网段放行允许 192.168.1.0 整个网段通过密码登录 host all all 192.168.1.0/24 scram-sha-256 # 其他来源全部拒绝 host all all 0.0.0.0/0 reject注意最后一条我并没有完全删掉默认的0.0.0.0/0规则而是把认证方法设置成reject相当于一个兜底策略防止未来不小心把整个 PostgreSQL 暴露出去。这个习惯在生产环境非常有价值。如果直接写成host all all 0.0.0.0/0 scram-sha-256放行全部来源万一服务器有公网 IP你的 PostgreSQL 就会面向整个互联网提供密码认证服务密码爆破风险会显著上升。修改完pg_hba.conf后不需要重启 PostgreSQL只需要重新加载配置文件。两条常用命令任选其一# 方式一通过 SQL 热加载 SELECT pg_reload_conf(); # 方式二通过系统服务 systemctl reload postgresql执行完 reload 之后新连接会按照新的规则进行校验。已经建立的连接不会受影响这点在维护生产环境时很关键你可以放心地修改配置再 reload不用担心正在跑的业务连接一瞬间全部断掉。不过 reload 之后如果有连接失败要及时看日志确认新的规则是否生效别改完了没加载空欢喜一场。4. 完整实操复盘从报错到恢复的完整过程4.1 场景设定与问题复现为了让你更直观地理解整个排查和修复过程我模拟一个实际场景。服务器是 Ubuntu 系统PostgreSQL 16 通过 apt 安装服务器 IP 是192.168.1.200。我的开发机是 Windows上面装了一个 DBeaver准备连接服务器上的 PostgreSQL 做开发。第一次连接时DBeaver 直接弹出了与开篇类似的错误只是在 GUI 里显示得更完整一点org.postgresql.util.PSQLException: 致命错误: 没有匹配 pg_hba.conf 的条目at 192.168.1.101, ...这个报错里的192.168.1.101就是我开发机的局域网 IP。报错信息已经把问题定位得很清楚了但我还是按照前面的排查流程做一遍确保不是其他问题导致误报。4.2 逐步排查与配置修复第一步我在 cmd 里执行telnet 192.168.1.200 5432确认 TCP 端口通不通。如果这里不通后面所有步骤都没有意义。在我这个场景里telnet 立即连上了说明网络和端口层面没问题。这个结果和报错细节也互相印证既然报错信息来自 PostgreSQL 服务端那么连接肯定到了数据库。第二步我 SSH 登录到服务器执行psql -U postgres看本地是否能连。这里要注意本机 Socket 连接走的是local规则能连不代表远程能连但能确认 PostgreSQL 服务本身是健康的。我执行了一下提示要输入密码输入密码后进去了说明服务端进程正常用户密码也正确。第三步查看当前的listen_addressesSHOW listen_addresses;返回结果是localhost。到这里远程连接失败的根源基本就水落石出了。即便pg_hba.conf里放行了远程 IPlisten_addresses没监听外部接口照样连不上。我先修改postgresql.conf把listen_addresses改成*表示监听所有网络接口。这个参数需要重启服务我执行了systemctl restart postgresql。第四步重启完再去编辑pg_hba.conf。我打开文件后看了一下默认配置里已经有一条host all all 127.0.0.1/32 scram-sha-256但确实没有针对局域网 IP 的规则。我在文件末尾加了一行host all all 192.168.1.0/24 scram-sha-256这里我放行的不是单个 IP而是整个局域网网段192.168.1.0/24。如果只放行自己这台开发机下次换个机器又得改配置所以在可信内网里用网段是更合理的做法。关于postgresql.conf的位置Ubuntu 上可以通过SHOW config_file;查询这一点非常方便。第五步执行SELECT pg_reload_conf();加载新的访问控制规则然后回到 Windows 开发机重新用 DBeaver 连接。这次连接成功了不再报任何异常。整个过程从排查到修复大概十分钟不到但付出的时间换来的是对整个 PostgreSQL 连接机制更清晰的认识。4.3 开发环境推荐配置模板如果你也在本地开发环境折腾 PostgreSQL可以参考我下面的配置模板它兼顾了本机管理和局域网内开发连接的需求# 本机 Socket 连接管理员免密登录 local all postgres peer local all all scram-sha-256 # 本机 TCP 连接 host all all 127.0.0.1/32 scram-sha-256 host all all ::1/128 scram-sha-256 # 局域网开发网段 host all all 192.168.1.0/24 scram-sha-256 # 其余来源一律拒绝 host all all 0.0.0.0/0 reject host all all ::/0 reject注意我在最后加上了 IPv6 的兜底规则::/0防止别人通过 IPv6 地址绕过限制。很多人在配置时只关注 IPv4IPv6 的坑往往要过好久才被发现。关于 IPv6 还有一个细节如果你在pg_hba.conf里放行了::1/128而客户端连接时用的是localhost系统可能解析成 IPv6 地址走::1这条规则也可能解析成 IPv4 走127.0.0.1这条规则很容易造成“刚才还能连为什么现在报没有匹配的条目”的困惑。排查时多看一眼连接串里实际解析出的 IP能少走很多弯路。5. 常见问题排查与避坑技巧5.1 高频问题速查表把常见的 PostgreSQL 连接失败问题整理成一个速查表方便你之后直接对照排查。报错特征常见原因解决方法没有匹配 pg_hba.conf 的条目客户端 IP/库/用户组合未放行修改pg_hba.conf添加对应规则密码认证失败密码错误或认证方法不匹配检查密码或用ALTER USER重置Connection refusedPostgreSQL 未启动或端口未监听检查服务状态和listen_addressesConnection timed out防火墙拦截或网络不通检查安全组、iptables、物理网络SSL connection required服务端强制 SSL 但客户端没启用开启 SSL 或在连接串加sslmoderequire角色不存在连接串用户名有误确认pg_user里是否存在该角色数据库不存在连接串数据库名有误确认库名可用\l查看数据库列表这张表里的问题我都实际遇到过尤其是“SSL connection required”那个PostgreSQL 某些发行版默认配置会强制 SSL客户端 JDBC 连接串如果没有额外设置可能会因为 SSL 协商失败被拒。解决办法是在 JDBC 连接串里加上sslmoderequire或sslmodedisable具体取决于你对加密的需求。如果只是开发环境sslmodedisable也能凑合。5.2 三个最容易被忽略的细节第一个细节是修改完配置忘记 reload。我见到太多人在pg_hba.conf里加了好几条规则客户端还是报同样的错最后发现配置文件里的新规则根本没生效。记住pg_hba.conf和postgresql.conf的生效方式不一样前者是 reload后者如果是listen_addresses这种参数还得重启。如果不确定改的是哪个参数最省事的办法是看 PostgreSQL 官方文档中的参数分类或者干脆执行systemctl restart postgresql一了百了代价是短暂中断所有连接不建议在生产环境这么做。第二个细节是认证方法和密码存储算法的匹配问题。PostgreSQL 14 之后scram-sha-256成为默认密码加密方式很多老配置里还是md5导致客户端用正确密码连接却提示认证失败。这个时候有两种解决路径把pg_hba.conf里的认证方法改成scram-sha-256或者把用户的密码重新设置一次让密码存储格式与认证方法一致。这里有个小技巧你可以先查一下pg_authid.rolpassword的前缀再对照pg_hba.conf的认证方法列两者对不上就改。第三个细节是规则顺序。pg_hba.conf是自上而下匹配的第一条匹配成功就不再往下看。我之前有过一次配置前面的规则写了host all all 0.0.0.0/0 reject后面才写host all all 192.168.1.0/24 scram-sha-256结果局域网连接一直失败。原因是0.0.0.0/0这条规则先匹配了所有来源直接拒绝根本轮不到后面放行规则的判断。文件的顺序逻辑就相当于代码里的 if-else后面的条件永远只在前面的条件不满足时才生效。理解了这一点你在编写规则时就要把更具体的网段放在前面把宽松的兜底规则放在后面。5.3 生产环境安全建议最后给生产环境提几点建议。第一不要轻易使用trust认证除非你的服务器处于完全隔离的内网且你清楚这样做的风险。第二pg_hba.conf的最小化授权原则是能精确到 IP 就不要用网段能精确到数据库就不要用all能精确到用户就不要放开所有角色。第三远程连接必须使用scram-sha-256如果有条件建议开启 SSL 连接。第四兜底规则一律用reject即使是在内网环境也不要配置成对全网开放密码认证。生产环境的 PostgreSQL 被扫描爆破是常态如果你在云服务器上运行数据库安全组只放行需要访问数据库的业务服务器 IP而不是对所有人开放 5432 端口。这样即使pg_hba.conf配置稍有疏忽云平台层面的网络隔离也能起到第二道防线的作用。说实话没有匹配 pg_hba.conf 的条目这个报错我第一次遇到时也是围着密码和驱动转了好几圈。后来把 PostgreSQL 的连接流程从头到尾捋了一遍发现所有问题都逃不过一个逻辑先网络再监听再访问控制最后才是认证。只要按照这个链路逐层排查大部分连接失败问题都能在几分钟内解决。希望这篇内容对你也有同样的帮助。
返回列表