What this check reads
Runs, as the unprivileged audit user, MYSQL_PWD= mysql --no-defaults -u root -e 'SELECT 1'. --no-defaults ignores ~/.my.cnf, and the empty MYSQL_PWD clears any inherited password, so the only way this succeeds is if root@localhost has no password and uses a password plugin (not auth_socket/unix_socket). Success is the failure condition: if that command exits zero the check reports FAIL, and it is only attempted when the audit itself is not running as UID 0. If that login fails but the root-shell login (MYOK) works, root access is gated by the OS (socket auth) or by a password, and the check reports PASS.
When it applies
Runs only when MySQL or MariaDB is detected on the host (HAS_MY=1: a mysql/mysqld binary, a running mysqld/mariadbd, matching the shared engine probe). The FAIL branch is only reachable when the audit runs as a non-root user - as UID 0 the passwordless probe is skipped because socket auth would make it succeed regardless. The PASS branch needs MYOK=1, i.e. root or sudo to reach the socket. Containerised MySQL is invisible to this check.
What each result means
| Result | When | What it means |
|---|---|---|
| SKIP | MySQL/MariaDB not detected | Nothing was tested. No MySQL or MariaDB was found on this host, which is not the same as a database that passed. |
| FAIL | Non-root shell logged in as MySQL root with no password | Every local account on this server is already a database administrator. Set a root password or move root to socket authentication. |
| PASS | Passwordless login failed and root-shell access works | An ordinary shell cannot become MySQL root without a credential. |
| SKIP | Neither worked: server down, or sudo needed to reach the socket | Nothing was verified. The server could not be reached, so treat root as unchecked rather than safe. |
Why it matters
OWASP Database Security Cheat Sheet: always require authentication, including for local connections. A passwordless root@localhost means every Linux account on the host (including the web server user after an RCE) has full DBA rights. CIS MySQL Benchmark: "Ensure no users have an empty password" / "Ensure 'root' has a strong password or uses socket authentication".
Why it fails, and when it is wrong
- Fresh Ubuntu/Debian MariaDB installs use
unix_socketfor root and will PASS. MySQL 8 on Ubuntu usesauth_socketfor root and will PASS. Manually created installs (Docker entrypoints, old distros) often leave root with no password. - If the audit itself runs as UID 0 the passwordless probe is skipped (it would succeed via socket auth), so root-run audits cannot detect this case; they rely on
mysql-passwordless-accounts. - SKIP "not accessible":
mysqldis stopped, the socket path differs from the client default (/var/run/mysqld/mysqld.sockvs/tmp/mysql.sock), or the audit account has no sudo. - Containerised MySQL is not seen by the host probe.
How to fix it
Probe from an unprivileged shell
As a non-root user, this must fail. If it succeeds, root has no password and every local account on the host already owns the database.
mysql --no-defaults -u root -e 'SELECT 1'Set a password
ALTER USER 'root'@'localhost' IDENTIFIED BY 'long-random-password';
FLUSH PRIVILEGES;Or run mysql_secure_installation (MariaDB: mariadb-secure-installation), which walks the same change interactively.
Or use socket authentication instead
Recommended for local admin use: root logs in only when the OS user is already root, so there is no shared secret to rotate, store or leak.
ALTER USER 'root'@'localhost' IDENTIFIED WITH auth_socket; -- MySQL
ALTER USER 'root'@'localhost' IDENTIFIED VIA unix_socket; -- MariaDBRe-run the probe
Repeat step 1 as an ordinary user. The passwordless login must now be refused.
Verify the fix
# As an unprivileged user, a passwordless root login must be refused:
MYSQL_PWD= mysql --no-defaults -u root -e 'SELECT 1'
# expected: ERROR 1045 (access denied) or ERROR 1698 (auth_socket in use)
# Confirm which plugin root now uses:
sudo mysql -N -e "SELECT user,host,plugin FROM mysql.user WHERE user='root'"
# expected: auth_socket / unix_socket, or a password plugin with a non-empty authentication_stringDebugging
The engine probe found no client binary and no running server. Unlike the PostgreSQL, MongoDB and Redis probes it never looks for a config file, so HAS_MY depends entirely on mysql/mysqld being on PATH or mysqld/mariadbd being up. Confirm with command -v mysql mysqld mariadbd; pgrep -a mysqld mariadbd. If the database runs in a container, this check cannot see it; audit it from inside (docker exec -it <c> mysql ...) or use the Docker checks.
mysqld is stopped, or the audit account cannot reach the socket. Test the exact command the script runs: sudo mysql -N -e 'SELECT 1'. A "Can't connect through socket" error usually means the client default socket path (/var/run/mysqld/mysqld.sock) differs from the server's; compare with sudo mysqladmin variables | grep -w socket. If it is a sudo problem, see the privilege note in Script context.
Reproduce it verbatim as the audit user: MYSQL_PWD= mysql --no-defaults -u root -e 'SELECT 1'. If that succeeds, the finding is real; --no-defaults means a password in ~/.my.cnf is not what let it in.
Expected: the passwordless probe is skipped for UID 0. Re-run the probe as an ordinary user, or rely on the MySQL Passwordless Accounts check.
Sources
- MySQL 8.4 Reference: Socket Peer-Credential Pluggable Authentication
- MySQL 8.4 Reference: mysql_secure_installation
- MySQL 8.4 Reference: Making MySQL Secure Against Attackers
- MariaDB KB: Authentication Plugin - Unix Socket
- MariaDB KB: Securing MariaDB
- OWASP Database Security Cheat Sheet
- MySQL
--no-defaultsand option files
How the script reads this
Next
Re-run the Authentication audit after applying the fix and confirm this check moves to PASS. CtrlOps runs all 8 checks over your existing SSH connection and scores the result, so the change is visible without reading another config file.
All 8 Authentication fixes