VPN 上的 MariaDB:服务器看到的是哪个 user@host
账户由用户名与主机共同决定。找到服务器实际看到的来源地址,读取 CURRENT_USER(),然后修复那一行,而不是那条授权。
从两条不同路径输入同一个用户名,MariaDB 可能把你认证成两个不同的账户。账户由用户名与主机共同确定,因此 'reporter'@'localhost' 与 'reporter'@'10.90.0.23' 是两行独立的记录,密码不同、授权也不同。走 VPN 时,真正值得问的不是用户输入了什么名字,而是服务器看到的源地址。
确定服务器实际看到的来源
要从数据库主机往回查,而不是从笔记本往前猜。客户端自认为拥有的地址只是几种可能之一:
| Path | Source MariaDB sees |
|---|---|
| Routed tunnel, no translation | The client's inner VPN address |
| Gateway source translation | The gateway's translated address |
| SSH local forward | The SSH server's address |
| Public exit, no tunnel | Whatever the ISP handed out |
用服务器侧的证据来判定。连接日志、活动会话列表或临时开启的通用日志会显示服务器接受的地址。浏览器里看到的笔记本公网 IP 是另一回事,而且经常误导人。
对上 MariaDB 实际选中的账户
账户选择遵循有文档规定的顺序,而不是把授权做并集。精确的主机匹配优先于通配符,匿名用户行也会以出人意料的方式参与匹配,认为只看名字就能决定的人往往会判断错。服务器只选一行,不会把同名各行的权限合并。读一遍参考文档,比花一下午试错更值。
也就是说,如果会话是以 'reporter'@'10.90.0.23' 认证的,往 'reporter'@'%' 上加授权可能什么都不会变。修复必须落在被选中的那一行上,所以先确认账户,再改授权。
读取会话实际使用的账户
SELECT USER(), CURRENT_USER();
SHOW GRANTS;
CURRENT_USER() 给出的是通过认证的账户;USER() 报告的是客户端请求的身份,两者可能不一致。这些要作为普通的最上层查询执行,而不是在带 definer 上下文的存储程序里执行,否则答案描述的是程序所有者,而不是你的会话。
登录失败时这些查询一条都跑不了,这正是管理员必须转而检查账户定义和服务器连接证据的情形。要提前想到这一点:排查被拒登录的人,通常根本没有可用的 SQL 会话。
把可达性与认证分开
报错文本会告诉你卡在哪一层。连接被拒或超时,说明客户端根本没到达监听端口,账户和密码还轮不到讨论。返回拒绝访问,说明服务器确实回应了,这证明网络可达,问题指向账户、主机匹配或凭据。
网络前提本身就是一份清单:监听要绑在预期地址上而不只是回环地址,配置端口要放行,回程路由要存在。每一项都可能独立失败,隧道通了并不代表其中任何一项成立。只绑回环的监听会接受本地连接、拒绝所有远程连接,表现出的连接被拒很像防火墙拦截。
在给网络团队开单之前,先确认绑定地址。数据库主机上的 ss -lntp 会显示究竟是哪个地址和端口在监听,这一行就能排除一整类猜测。
收窄修复范围,然后双向复测
通过组织认可的流程创建预期的、带主机限定的账户,凭据取自受支持的存储,而不是贴进工单里的共享口令。只授予任务所需的库表操作。把失败的远程登录改成 root 账户、全放开策略或大范围通配主机,能让测试通过,却拿掉了你本来要验证的那道控制。匆忙加上的授权,往往就是后来没人回收的那条。
用新会话沿预期路径复测,然后检查反向用例。本该被挡住的路径必须仍然被挡住,没有授予的操作必须仍然被拒绝。这两项检查,才是修好了和放开了的分界。
最后,把观察到的源地址和账户名一起记录下来。隧道只提供可达性;MariaDB 仍然自己选择和授权账户,下一个人需要这两项事实才能复现你的结果。