PostgreSQL 16私有IP访问密码验证失败,公网IP可正常连接
问题背景
部署PostgreSQL 16后,配置允许家庭公网IP和同私有网络的另一台VPS私有IP访问数据库。测试发现家庭网络可正常登录,但同私有网络的VPS使用私有IP连接时,出现密码验证失败错误(通过Python3/SQLAlchemy/psycopg2连接)。
将VPS连接地址改为数据库公网IP,并在pg_hba.conf中添加VPS的公网IP规则后,连接恢复正常,但这种方式违背了数据库与VPS处于同一私有网络的初衷(通过公网IP访问会走公网路由)。
相关配置与错误信息
初始pg_hba.conf配置
host all all 107.x.x.x/32 scram-sha-256 host all all 10.x.x.x/20 scram-sha-256
修改后正常的pg_hba.conf配置
host all all 107.x.x.x/32 scram-sha-256 host all all 10.x.x.x/20 scram-sha-256 host all all 138.x.x.x/32 scram-sha-256
客户端连接错误信息
conn = _connect(dsn, connection_factory=connection_factory, **kwasync) sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) connection to server at "10.108.0.2", port 6262 failed: FATAL: password authentication failed for user "myapp"
PostgreSQL日志记录
2023-10-03 02:31:47.832 EDT [12728] FATAL: no pg_hba.conf entry for host "138.x.x.x", user "siteapp", database "sitedata", SSL encryption 2023-10-03 02:31:47.837 EDT [12712] FATAL: no pg_hba.conf entry for host "138.197.16.205", user "siteapp", database "sitedata", no encryption 2023-10-03 02:36:15.206 EDT [12712] LOG: checkpoint starting: time 2023-10-03 02:36:15.240 EDT [12712] LOG: checkpoint complete: wrote 3 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.003 s, sync=0.012 s, total=0.034 s; sync files=2, longest=0.008 s, average=0.006 s; distance=0 kB, estimate=0 kB; lsn=0/1BCF0B0, redo lsn=0/1BCF078
排查与解决步骤
1. 确认PostgreSQL监听地址
检查postgresql.conf中的listen_addresses参数,确保其包含数据库的私有IP(如10.108.0.2)或设置为*(监听所有可用地址)。如果仅监听公网IP,私有IP的连接请求无法到达数据库,会直接触发验证失败。
2. 验证私有IP网段配置正确性
确认pg_hba.conf中配置的10.x.x.x/20网段是否真的包含VPS的私有IP。可以通过子网计算器计算网段范围,避免因子网掩码错误导致规则不匹配。
3. 检查用户密码加密方式
PostgreSQL 16默认使用scram-sha-256加密密码,但如果myapp用户的密码是用旧的md5加密创建的,当匹配pg_hba.conf中的scram-sha-256规则时,会出现验证失败。执行以下SQL查看用户加密方式:
SELECT usename, passwd FROM pg_shadow;
如果passwd字段以md5开头,重新设置密码为scram格式:
ALTER USER myapp WITH PASSWORD 'your_secure_password';
4. 确认VPS的网络路由走向
在VPS上执行traceroute 10.108.0.2(Linux)或tracert 10.108.0.2(Windows),检查数据包是否走私有网络路由。如果路径经过公网网关,说明VPS的私有网络配置存在问题,导致数据库收到的是VPS的公网IP,而非私有IP。
5. 启用详细日志排查
修改postgresql.conf中的日志配置:
log_connections = on log_line_prefix = '%t [%p]: [%c-%l] user=%u,db=%d,app=%a,client=%h '
重启PostgreSQL后,再次用私有IP连接,查看日志中记录的客户端IP和验证失败细节,定位具体原因。
内容的提问来源于stack exchange,提问作者Logos Masters

