必须先确认have_ssl=YES且ssl_ca、ssl_cert、ssl_key为非空绝对路径并权限正确,再启用require_secure_transport=ON,客户端还需显式指定--ssl-mode=REQUIRED等参数,否则连接被拒。

确认服务端 SSL 真正启用(不是“配了就行”)
很多配置失败的根源是 have_ssl 显示 DISABLED 或空值——这意味着 MySQL 根本没加载 OpenSSL 模块,后续所有设置都无效。必须先验证:
- 执行
SHOW VARIABLES LIKE 'have_ssl';,结果必须是YES - 检查
ssl_ca、ssl_cert、ssl_key三个变量是否为非空的**绝对路径**(如/var/lib/mysql/ca.pem),不能是相对路径、软链接或放在用户家目录 - 证书文件权限必须严格:私钥
server-key.pem需600,且mysql用户能读:sudo -u mysql ls -l /var/lib/mysql/server-key.pem - Linux 下 SELinux 或 AppArmor 常拦截访问,临时运行
setenforce 0测试可快速定位
开启全局强制开关 require_secure_transport=ON
仅靠 REQUIRE SSL 用户权限不够,它只约束单个账号;真正“一刀切”禁止明文 TCP 连接的是 require_secure_transport 参数。它不作用于 Unix socket(如 localhost),所以生产环境务必配合网络层控制。
- 运行时启用(推荐):
SET PERSIST require_secure_transport = ON;(自动写入配置,重启不失效) - 配置文件启用:在
[mysqld]段添加require_secure_transport = ON,然后systemctl restart mysql - 验证是否生效:
SELECT @@GLOBAL.require_secure_transport;返回1 - 生效后,任何未走 TLS 的 TCP 连接(包括
root@127.0.0.1)都会被直接拒绝,错误提示仍是Access denied for user,但实际是协议层断连
客户端必须显式发起 TLS 握手(服务端开了≠客户端就用)
MySQL 不会主动“推”加密,客户端不发 Client Hello,连接就永远是明文。哪怕服务端开了 require_secure_transport=ON,客户端仍需明确声明。
- 命令行:
mysql -u user -p --ssl-mode=REQUIRED(--ssl-mode=VERIFY_IDENTITY配自签名证书会因 CN 不匹配失败) - JDBC URL 必须含
&useSSL=true&requireSSL=true&enabledTLSProtocols=TLSv1.2;旧驱动用usessl=true无效 - PHP mysqli 需传
MYSQLI_CLIENT_SSL标志;PDO DSN 加sslmode=require或完整证书路径 - 注意 TLS 协议版本兼容性:
SELECT @@GLOBAL.tls_version;查服务端支持范围,老客户端可能需服务端临时放宽到TLSv1,TLSv1.1,TLSv1.2
容易被忽略的细节和常见陷阱
配置看似简单,但几个点一错就全盘失效:
-
require_secure_transport对 Unix socket 连接(localhost)完全无效,测试必须用127.0.0.1或远程 IP - 证书路径写错、权限不对、SELinux 拦截,会导致
mysqld启动失败或日志报SSL error: Unable to get private key - MySQL 8.0 默认插件
caching_sha2_password与某些旧 JDBC/pymysql 驱动 + SSL 组合会报认证插件加载失败,可改用mysql_native_password - 自签名证书的 CN 必须匹配客户端连接地址(如连
192.168.1.100,CN 就不能是localhost),否则--ssl-mode=VERIFY_IDENTITY直接拒连











