
如何在 MySQL 客户端和服务端启用双向 SSL(mTLS)
MySQL 原生支持双向 SSL,但默认不开启,且服务端必须先配置好 CA、服务端证书和私钥,客户端也必须提供有效证书——缺一不可。否则会报错 SSL connection error: protocol version mismatch 或更常见的 Access denied for user 'xxx'@'xxx' (using password: YES)(注意:这个错误可能根本不是密码问题,而是 SSL 握手失败被静默降级或拒绝)。
实操关键点:
- 服务端必须设置
require_secure_transport = ON强制所有连接走加密通道,否则即使配了证书,非 SSL 连接仍能成功 - 客户端连接时必须显式指定
--ssl-mode=VERIFY_IDENTITY(或更低的REQUIRED),不能只靠--ssl-ca - CA 证书(
ca.pem)在服务端和客户端必须完全一致;服务端证书(server-cert.pem)和私钥(server-key.pem)不能用自签名工具随意生成——需确保subjectAltName匹配服务端实际域名/IP,否则客户端校验失败
生成兼容 MySQL 的双向证书链(OpenSSL 实操要点)
MySQL 对证书格式敏感:不接受 PKCS#12,只认 PEM;私钥不能加密(即不能有 passphrase),否则启动 mysqld 时会卡住;服务端证书的 extendedKeyUsage 必须包含 serverAuth,客户端证书必须含 clientAuth。
常见翻车点:
- 用
openssl req -x509直接生成自签名证书 → 缺少subjectAltName,导致客户端连接时报ERROR 2026 (HY000): SSL connection error: Certificate verification failed - 私钥用
openssl genrsa -aes256生成 → mysqld 启动失败,日志里只有模糊的Failed to set up SSL - 客户端证书没加
clientAuth扩展 → 即使证书存在,服务端日志显示SSL error: SSL is required and the client does not support it
推荐最小可行命令流(以服务端 IP 192.168.1.10 为例):
openssl req -x509 -newkey rsa:2048 -days 3650 -nodes \ -keyout ca-key.pem -out ca.pem -subj "/CN=MySQL-CA" <p>openssl req -newkey rsa:2048 -nodes -keyout server-key.pem -out server-req.pem \ -subj "/CN=localhost" openssl x509 -req -in server-req.pem -days 3650 -CA ca.pem -CAkey ca-key.pem \ -set_serial 01 -out server-cert.pem -extfile <(printf "subjectAltName=IP:192.168.1.10\nextendedKeyUsage=serverAuth")</p><p>openssl req -newkey rsa:2048 -nodes -keyout client-key.pem -out client-req.pem \ -subj "/CN=mysql-client" openssl x509 -req -in client-req.pem -days 3650 -CA ca.pem -CAkey ca-key.pem \ -set_serial 02 -out client-cert.pem -extfile <(printf "extendedKeyUsage=clientAuth")
MySQL 服务端配置文件关键项(my.cnf)
只写真正影响双向校验的几项,多余参数反而引发冲突。尤其注意路径权限:MySQL 进程用户(如 mysql)必须对证书文件有读权限,且不能是 root-only 可读(chmod 600 是安全的,但 chmod 400 可能因 SELinux 拒绝)。
必须配置:
-
ssl_ca = /etc/mysql/ssl/ca.pem—— CA 证书路径,服务端和客户端共用同一份 -
ssl_cert = /etc/mysql/ssl/server-cert.pem—— 服务端证书 -
ssl_key = /etc/mysql/ssl/server-key.pem—— 服务端私钥(无密码) -
require_secure_transport = ON—— 强制加密,这是双向校验的前提
可选但建议加:
-
tls_version = TLSv1.2,TLSv1.3—— 禁用老旧协议,避免降级攻击
验证双向 SSL 是否真生效(不只是“连得上”)
连得上 ≠ 双向校验开启。MySQL 提供两个关键状态变量来确认:
- 执行
SHOW STATUS LIKE 'Ssl_cipher';—— 返回非空值(如TLS_AES_256_GCM_SHA384)说明 SSL 已启用 - 执行
SELECT * FROM performance_schema.status_by_thread WHERE VARIABLE_NAME = 'Ssl_client_verify';—— 若值为FAILED或空,说明客户端证书未通过校验;若为SUCCESS,才代表双向认证完成 - 检查服务端错误日志:成功双向握手时会有类似
SSL connection established with cipher TLS_AES_256_GCM_SHA384, peer certificate CN: mysql-client的记录
最容易被忽略的是:客户端证书的 CN 或 subjectAltName 必须与 MySQL 用户的 REQUIRE X509 或 REQUIRE SUBJECT 约束严格匹配。例如创建用户时用了 REQUIRE SUBJECT '/CN=mysql-client',那客户端证书的 subject 就必须一字不差,包括斜杠和顺序。










