隧道起来了,QGIS 也连上了,可登录却因证书错误失败——错误里写的是 127.0.0.1,而不是数据库主机。接下来很想做的事是放宽 sslmode,这会让检查闭嘴,也把你一开始想要的身份保证扔掉。真正的问题是本地端口和服务器名被存进了同一个字段。libpq 的服务条目把两者分开,于是套接字连到转发端口,而 TLS 仍然校验证书上印着的名字。

客户端以为自己连的是什么

在 sslmode=verify-full 下,libpq 会把服务器出示的证书与所请求的主机名作比较。当这个主机名是 loopback 地址时,即使证书对真实数据库主机有效,比较也会失败。SSH 转发改变的是哪个套接字承载字节,而不是客户端应当期待的身份。把这两件事分开之后,错误就不再像 TLS 故障了。

转发也不是授权。PostgreSQL 对 SSH 服务器建立的连接仍然施加自己的规则,所以在改动任何东西之前,先把传输层和数据库层分开。

只把转发绑定到 loopback

监听应当只存在于 QGIS 所在的机器上,别处不该有。显式写出 127.0.0.1 能让它避开其他接口;而省略绑定地址时,端口可能因 SSH 客户端的网关设置而对外暴露。

ssh -N -L 127.0.0.1:15432:127.0.0.1:5432 [email protected]

第二个冒号之后的目标由 SSH 服务器一侧解析,所以本例假设 PostgreSQL 与 sshd 在同一台主机上;若数据库在另一台机器上,就需要那个地址及其权限。用 -N 启动会话,这样不会打开 shell;别动主机密钥校验;在怪罪 QGIS 之前先确认监听确实出现了。如果端口已被占用,转发会在启动时就失败,而不是等到查询时。

把名字和套接字放进同一个服务条目

服务文件让同一个连接既带校验用的名字,又指向不同的套接字目标。把条目保存在 libpq 查找服务的位置,或者把 PGSERVICEFILE 指向它。

[gis-via-ssh]
host=db.geo.example
hostaddr=127.0.0.1
port=15432
dbname=gisdb
user=gis_reader
sslmode=verify-full
gssencmode=disable
sslrootcert=/path/to/approved-root.crt

这里 host 保留用于证书校验的身份,而 hostaddr 把数据包送到转发出来的 loopback 端口,两者都要有,这套安排才能成立。gssencmode=disable 在那些否则会先协商 GSSAPI 加密的构建上很重要,因为那条路径会忽略刚刚设好的 TLS 设置。在 QGIS 的 PostgreSQL 连接对话框里按名字引用该服务,并通过常规提示提供口令,而不是写在共享文件里。上面的名字和路径都是占位符,请换成你自己获批的取值。

隧道之后 PostgreSQL 还会检查什么

服务器看到的是来自 SSH 主机的连接(在这套安排里就是 loopback),并依次按基于主机的认证规则去匹配。你的数据库角色仍然要认证,SSH 登录本身并不赋予数据库身份。要去核实服务器认为什么,而不是客户端假设了什么。

psql "service=gis-via-ssh" -c "select inet_server_addr(), current_user"

工作完成后关闭转发会话;本地条目随之失效,其他都不变。一条数据库通路并不是整个 GIS 客户端的 VPN,把它当成 VPN,正是下一次连接被误诊的原因。

相关文章