mysql 8.0 不支持真正双向 tls(mtls),因其协议层仅由服务端发起证书发送,客户端是否校验证书(如 --ssl-mode=verify_ca)完全自主,服务端无法强制或感知;require x509 等语法仅实现服务端单向验客户端证书,不构成双向身份互信。

MySQL 8.0 官方不支持双向 SSL(mTLS),所谓“双向 SSL”在 MySQL 场景中是常见误解——服务端可要求客户端提供证书并校验,但 MySQL 的 REQUIRE X509 / REQUIRE SUBJECT / REQUIRE ISSUER 等语法仅做单向验证(服务端验客户端证书),并不强制客户端验证服务端证书,且 MySQL 客户端本身不实现证书链校验逻辑。真正意义上的双向 TLS(即双方都校验证书、密钥交换、身份绑定)需依赖外部代理(如 HAProxy、Envoy)或应用层封装。
为什么 MySQL 8.0 没有真正的双向 SSL?
MySQL 协议层 TLS 握手只由服务端发起证书发送,客户端是否校验(如用 --ssl-mode=VERIFY_CA)完全取决于客户端自身行为,服务端无法强制。服务端的 REQUIRE X509 只表示“你必须提供一个有效客户端证书”,但不关心你是否也验证了我;反过来,客户端即使配置了 --ssl-ca,服务端也不会检查你有没有执行这步。这是协议设计决定的,不是配置能绕过的。
常见误判现象:
- 执行
CREATE USER 'u'@'%' REQUIRE X509;后以为“双向”已启用 —— 实际只是服务端单方面要求客户端送证书 - 客户端加了
--ssl-mode=VERIFY_IDENTITY就认为“双向认证完成” —— 服务端根本不知道你做了这事 - 日志里出现
Ssl_cipher: DHE-RSA-AES256-SHA就以为安全闭环 —— 加密成立,但身份互信未建立
如何模拟接近双向 SSL 的效果?
虽不能真双向,但可通过组合策略逼近等效安全水位:
-
REQUIRE X509+CERTIFICATE AUTHORITY:强制客户端提交由指定 CA 签发的证书,服务端用ssl_ca配置校验其签名有效性 - 配合
REQUIRE SUBJECT '/CN=client-app-01'或REQUIRE ISSUER '/CN=MyInternalCA',限制可接受的证书主体或签发者 - 服务端开启
require_secure_transport = ON,杜绝明文连接 - 客户端必须显式启用校验:
mysql -u u -p --ssl-mode=VERIFY_CA --ssl-ca=/path/to/ca.pem --ssl-cert=/path/to/client-cert.pem --ssl-key=/path/to/client-key.pem - 关键:客户端证书私钥
client-key.pem必须无密码、权限600,否则 MySQL 客户端静默失败
客户端证书生成与服务端配置要点
MySQL 不接受自签名客户端证书直接用于 REQUIRE X509,必须由服务端信任的 CA(即 ssl_ca 所指 PEM)签发:
- 先用
mysql_ssl_rsa_setup生成服务端证书和 CA(ca.pem),不要手动生成,避免subjectAltName缺失导致校验失败 - 用该 CA 签发客户端证书:
openssl x509 -req -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 365 -sha256 - 服务端
my.cnf中确保:ssl_ca = /var/lib/mysql/ca.pem(必须绝对路径,且 mysql 用户可读) - 用户创建语句必须含完整约束:
CREATE USER 'app'@'%' REQUIRE X509 AND SUBJECT '/CN=web-api' AND ISSUER '/CN=MyInternalCA'; - 验证是否生效:
SELECT user, host, ssl_type, ssl_cipher, ssl_issuer, ssl_subject FROM mysql.user WHERE user = 'app';,其中ssl_type应为X509,ssl_issuer和ssl_subject需匹配
最容易被忽略的三个硬性条件
缺一不可,否则看似配了,实则连接仍走明文或直接拒绝:
-
have_ssl必须为YES(不是DISABLED),否则整个 SSL 栈未加载 —— 查看方式:SHOW VARIABLES LIKE 'have_ssl'; -
server-key.pem权限必须是600且属主为mysql,否则 mysqld 启动时静默跳过 SSL 初始化,Ssl_cipher始终为空 - 客户端连接必须用 IP(如
127.0.0.1)而非localhost,因为后者走 Unix socket,完全绕过所有 SSL 设置











