Type the same user name from two different paths and MariaDB may authenticate you as two different accounts. Accounts are keyed on user and host together, so 'reporter'@'localhost' and 'reporter'@'10.90.0.23' are separate rows with separate passwords and separate grants. Over a VPN, the interesting question is not which name the user typed but which source address the server observed.

Determine the source the server actually sees

Work backwards from the database host, not from the laptop. The address the client believes it has is only one of several possibilities:

PathSource MariaDB sees
Routed tunnel, no translationThe client's inner VPN address
Gateway source translationThe gateway's translated address
SSH local forwardThe SSH server's address
Public exit, no tunnelWhatever the ISP handed out

Use server-side evidence to settle it. A connection log, an active session listing, or a temporary general log entry shows the address the server accepted. The laptop's public IP from a browser is a different fact and often a misleading one.

Match the account MariaDB selected

Account selection follows documented ordering rather than a union of grants. An exact host match takes precedence over a wildcard, and anonymous user rows participate in matching in ways that surprise people who assume the name alone decides. The server picks one row; it does not merge the privileges of every similarly named row. Reading the reference once is worth more than an afternoon of testing guesses.

That means adding a grant to 'reporter'@'%' may change nothing if the session authenticated as 'reporter'@'10.90.0.23'. The fix has to land on the row that was selected, which is why identifying the account comes before editing grants.

Read the account the session actually uses

SELECT USER(), CURRENT_USER();
SHOW GRANTS;

CURRENT_USER() names the account that authenticated the session; USER() reports what the client asked for and can differ. Run these as ordinary top-level queries, not from inside a stored routine with a definer context, or the answers describe the routine's owner instead of your session.

A failed login cannot run any of this, which is the case where an administrator has to inspect the account definitions and the server's connection evidence instead. Plan for that: the person debugging a rejected login usually has no SQL session to query.

Separate reachability from authentication

The error text tells you which layer you are in. A refused connection or a timeout means the client never reached a listener, so the account and its password are not yet in question. An access-denied reply means the server answered, which proves reachability and points at the account, the host match, or the credential.

Network prerequisites are their own list: a listener bound to the intended address rather than only to loopback, permitted traffic to the configured port, and a return route for the reply. Each can fail independently, and a working tunnel does not imply any of them. A listener on loopback accepts local connections and refuses every remote one, which produces a refused connection that looks like a firewall block.

Check the bind address before opening a ticket with the network team. ss -lntp on the database host shows which address and port are actually listening, and that single line rules out a whole category of guesses.

Repair narrowly, then retest both directions

Create the intended host-qualified account through the organisation's approved process, with a credential from the supported store rather than a shared one pasted into a ticket. Grant only the schema operations the task needs. Turning a failed remote login into a root account, a grant-all policy, or a blanket wildcard host makes the test pass and removes the control you were testing. The grant you added in a hurry is the one nobody removes later.

Retest with a fresh session through the intended path, then check the negative cases. A path that should be blocked must still be blocked, and an operation that was not granted must still be denied. Those two checks are what separate a repaired account from a widened one.

Finally, record the observed source address and the account name together. The tunnel supplies reachability; MariaDB still chooses and authorises its own account, and the next person needs both facts to reproduce your result.

פֿאַרבונדענע אַרטיקלען