Database audit fix

How to Configure MySQL SSL Certificate Settings

MySQL points at its server certificate with the ssl_cert variable, paired with ssl_key and ssl_ca, and a relative path there is resolved against the data directory. Renew before 30 days remain, then run ALTER INSTANCE RELOAD TLS.

Hiren KalariyaLast reviewed: Sep 28, 2026Check mysql-certificate
Medium
severity
Yes
needs root or sudo
3
results it can return
2 of 5
checks in this audit

Every threshold on this page is transcribed from the database-security-transport-encryption audit script that CtrlOps runs, and a build check fails if the two ever disagree. See all 5 Transport Encryption checks, or how the audit runs.

Medium severityNeeds sudo

What this check reads

Asks the running server for @@ssl_cert, the path to the certificate MySQL presents to clients. A value that does not begin with / is treated as relative and prefixed with @@datadir, which is where the auto-generated server-cert.pem normally sits. The resolved file is then opened as root and tested with openssl x509 -checkend 2592000, so the threshold between a pass and a warning is 2592000 seconds, 30 days, of remaining validity.

When it applies

Runs only when MySQL/MariaDB is detected and reachable as root over the socket (MYOK=1); otherwise SKIP. It only looks at a certificate if @@ssl_cert is set and non-NULL - a server with no configured certificate SKIPs rather than failing, and so does one whose configured file is missing on disk. Relative paths are resolved against @@datadir. Reading the file needs root. The only test is openssl x509 -checkend 2592000: 30 days of remaining validity. Nothing else about the certificate is examined - not the key size, the signature algorithm, the chain, the hostname, or whether clients trust it. An already-expired certificate also fails -checkend and so is reported as WARN, not FAIL.

What each result means

Result thresholds this check applies
ResultWhenWhat it means
PASSValid for more than 30 daysThe certificate the server presents is not about to expire, and nothing else about it was judged.
WARNExpires within 30 days (or already expired)Renewal is now on the clock: clients that verify the certificate will start refusing to connect on the expiry date.
SKIPNo `ssl_cert`, file not found, or not accessibleNo certificate was examined at all, so its expiry date is unknown rather than fine.

Why it matters

An expired server certificate makes clients with --ssl-mode=VERIFY_CA/VERIFY_IDENTITY refuse to connect, causing an outage, or pushes operators to disable verification, which defeats TLS. MySQL's auto-generated certificates are valid for 10 years; CA-issued ones are typically 1 year or shorter.

Why it fails, and when it is wrong

  • Auto-generated certs live in the data directory as server-cert.pem; the relative-path handling covers that.
  • ssl_cert pointing at a file the as_root shell cannot read (SELinux context, NFS root-squash) gives SKIP "file not found".
  • openssl x509 -checkend treats an already-expired cert the same as one expiring soon; the message says "expires within 30 days" in both cases.
  • Only the server certificate is checked, not the CA (ssl_ca) or client certs.

How to fix it

Find the file the server is using

Ask the running server for @@ssl_cert rather than trusting a config file on disk, because the two can disagree. A value that does not begin with / is relative and resolves against @@datadir, which is where the auto-generated server-cert.pem normally sits, so read that variable too and confirm you are looking at the same file the server is. The commands are in Verify the fix below.

Check the remaining validity

Test the resolved file with openssl x509 -checkend 2592000, which is the exact test the audit runs: 2592000 seconds is 30 days. A non-zero exit means under 30 days of validity remain, and an already-expired certificate fails the same way, so read the real dates with -dates before deciding how urgent this is.

Generate the replacement

Reissue the certificate (internal CA, or mysql_ssl_rsa_setup replacement: MySQL 8.4 generates missing files automatically at startup if you delete the old ones), set the key to mode 0600, chown both files to the mysql service account, and update ssl_cert/ssl_key if the paths changed.

Restart and confirm

MySQL loads certificates at startup, so a renewed file changes nothing until it is reloaded: run ALTER INSTANCE RELOAD TLS; (no restart needed on MySQL 8), or restart the server on an older release. Then re-run the -checkend test to confirm the server is presenting the new expiry rather than the old certificate it still has in memory.

Verify the fix

CERT=$(sudo mysql -N -e 'SELECT @@ssl_cert')
sudo openssl x509 -in "$CERT" -noout -subject -issuer -dates
# expected: notAfter comfortably in the future

# The exact test the check runs:
sudo openssl x509 -checkend 2592000 -noout -in "$CERT" && echo 'valid > 30 days'

# What clients actually negotiate, end to end:
openssl s_client -connect 127.0.0.1:3306 -starttls mysql -brief </dev/null

Debugging

@@ssl_cert is empty, so TLS is not set up at all. That is a TLS Enforcement finding, not a certificate one; fix that first.

The path in @@ssl_cert does not exist, or the audit could not read it even as root. Check with sudo ls -l "$(sudo mysql -N -e 'SELECT @@ssl_cert')". A relative path is resolved against @@datadir; confirm you are looking at the same file the server is.

MySQL loads certificates at startup. Restart the server, or run ALTER INSTANCE RELOAD TLS (MySQL 8.0+) to pick up new files without a restart, then re-check.

MySQL's auto-generated certificates are valid for one year and do age out. Regenerate them (mysql_ssl_rsa_setup, or provide your own CA-issued pair) rather than ignoring the warning; clients will start failing on the expiry date.

This check only tests the expiry date. Hostname mismatch, an untrusted issuer or a broken chain all pass here and fail in the client. Test end to end with the openssl s_client command above and with --ssl-mode=VERIFY_IDENTITY.

Sources

How the script reads this

Next

Re-run the Transport Encryption audit after applying the fix and confirm this check moves to PASS. CtrlOps runs all 5 checks over your existing SSH connection and scores the result, so the change is visible without reading another config file.

All 5 Transport Encryption fixes
Audit your fleet

Find every one of these on every server, in one click

CtrlOps runs this audit over your existing SSH connection - no agents, no scripts to manage. $7/user/month after a 1 month free trial - no credit card required.

Windows

✓ Start instantly·✓ No credit card·✓ No sneaky autorenewals