Skip to main content

How to Give a MySQL Docker Container a Certificate with the Right Host Name

· 5 min read
Jagdish Kumawat
Founder @ Dewiride

A client that checks host names can't pass against MySQL's automatic certificate, because that certificate has no name you could connect to. You don't need a new CA or any extra tools: the container has OpenSSL and the CA's key, so you can sign a certificate with the right name right there.

What You Need​

MySQL in Docker, started with its automatic certificates, as in How to Run MySQL in Docker with Docker Compose. Every command here runs inside the container, so nothing needs installing, and they're the same in bash, zsh and PowerShell.

Step 1: See What's There Now​

Terminal
docker compose exec mysql openssl x509 -in /var/lib/mysql/server-cert.pem -noout -subject -issuer -enddate -ext subjectAltName
Output
No extensions in certificate
subject=CN=MySQL_Server_9.7.4_Auto_Generated_Server_Certificate
issuer=CN=MySQL_Server_9.7.4_Auto_Generated_CA_Certificate
notAfter=Nov 3 06:02:30 2036 GMT

No extensions means there's no Subject Alternative Name (SAN), the list of names a certificate is valid for. And no client connects to a server called MySQL_Server_9.7.4_Auto_Generated_Server_Certificate.

Step 2: Sign a New One​

--user mysql makes the new files belong to the account MySQL runs as. Change the names on the second line to what your clients use. These two suit a server on your own computer:

Terminal
docker compose exec --user mysql mysql sh -c '
cd /var/lib/mysql &&
printf "subjectAltName=DNS:localhost,IP:127.0.0.1\n" > san.ext &&
openssl req -newkey rsa:2048 -nodes -keyout new-server-key.pem -out server.csr -subj "/CN=inventory-mysql" &&
openssl x509 -req -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -days 825 -extfile san.ext -out new-server-cert.pem &&
mv new-server-key.pem server-key.pem &&
mv new-server-cert.pem server-cert.pem &&
rm server.csr san.ext &&
ls -l server-cert.pem server-key.pem
'
Output (the end)
Certificate request self-signature ok
subject=CN=inventory-mysql
-rw-r--r-- 1 mysql mysql 1253 Nov 6 06:04 server-cert.pem
-rw------- 1 mysql mysql 1704 Nov 6 06:04 server-key.pem

The new files are written under temporary names, and moved into place only when everything before them has worked. So a failure partway leaves the old certificate in place. It's signed by the CA that was already there, so clients with ca.pem don't need anything new.

Step 3: Tell MySQL to Use It​

As root, with no restart:

mysql>
SHOW GLOBAL STATUS LIKE 'Ssl_server_not_after';
ALTER INSTANCE RELOAD TLS;
SHOW GLOBAL STATUS LIKE 'Ssl_server_not_after';
Output
+----------------------+--------------------------+
| Variable_name | Value |
+----------------------+--------------------------+
| Ssl_server_not_after | Nov 3 06:02:30 2036 GMT |
+----------------------+--------------------------+
1 row in set (0.007 sec)
Query OK, 0 rows affected (0.001 sec)

+----------------------+--------------------------+
| Variable_name | Value |
+----------------------+--------------------------+
| Ssl_server_not_after | Feb 8 06:04:42 2029 GMT |
+----------------------+--------------------------+
1 row in set (0.000 sec)

Step 4: Check the Result​

Terminal
docker compose exec mysql openssl x509 -in /var/lib/mysql/server-cert.pem -noout -subject -issuer -enddate -ext subjectAltName
Output
subject=CN=inventory-mysql
issuer=CN=MySQL_Server_9.7.4_Auto_Generated_CA_Certificate
notAfter=Feb 8 06:04:42 2029 GMT
X509v3 Subject Alternative Name:
DNS:localhost, IP Address:127.0.0.1

The new certificate with the subject CN=inventory-mysql, the same issuer as before, and a Subject Alternative Name of DNS and IP Address:127.0.0.1.

A client that checks names now connects, by either name. This one is a .NET app with SslMode=VerifyFull:

Output
Connected as inventory_app@% to MySQL 9.7.4
TLS: TLSv1.2

What This Is, and Isn't​

This is for a MySQL you run in Docker, where the CA's key is already sitting in the data folder. In production, keep a CA's key away from the server it signs for, which How to Use Your Own Certificates with the MySQL Docker Image does.

The new certificate lives in the data volume. It survives restarts and docker compose down, and docker compose down -v removes it, along with the CA.

Common Mistakes​

MistakeWhat happensDo this instead
Running it without --user mysqlThe files belong to root, and MySQL can't read the keyKeep --user mysql
Forgetting the reloadMySQL keeps the old certificateRun ALTER INSTANCE RELOAD TLS
Listing a name but connecting by IPRefusedList everything you connect by

Troubleshooting​

SymptomLikely causeFix
The host name does not match the name on the certificateThe name isn't in the SAN, or MySQL hasn't reloadedThe fix
openssl can't find ca-key.pemThe container mounts other certificatesUse your own CA
ERROR 3888 from the reloadThe key and certificate don't matchThe fix

Conclusion​

Inside the container, openssl and the CA's key can sign a certificate with the right names, and ALTER INSTANCE RELOAD TLS puts it to work. Your clients' ca.pem keeps working.

Additional Resources​

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.