require_secure_transport = on 是双向tls认证的强制前提,必须在[mysqld]段配置并重启,否则require subject等校验不会触发;它拒绝所有非ssl tcp连接,是服务端硬性守门员。

require_secure_transport=ON 是强制双向验证的前提
没有这一步,服务端根本不会拒绝明文连接,所有 REQUIRE SUBJECT 或 REQUIRE X509 都不会触发。它不是“可选增强”,而是守门员。
必须在 [mysqld] 段中配置并重启:
require_secure_transport = ON运行时设置也行:
SET PERSIST require_secure_transport = ON;,但需确认 SELECT @@GLOBAL.require_secure_transport; 返回 1。
注意:require_secure_transport 对 Unix socket(如 localhost)无效,生产环境务必配合防火墙或 bind-address 限制 TCP 访问。
CREATE USER 必须用 REQUIRE SUBJECT,且 DN 字符串要逐字匹配
REQUIRE SSL 或 REQUIRE X509 只校验证书签名链是否有效,不绑定具体客户端身份;真正实现“谁是谁”的,是 REQUIRE SUBJECT。
创建用户时必须写全 DN 字符串,且与客户端证书输出完全一致:
- 用
openssl x509 -in client-cert.pem -subject -noout查看真实值,例如:subject= /CN=webapp1/O=myorg/OU=prod - 创建语句必须严格复制该输出(包括空格、斜杠顺序、大小写):
CREATE USER 'webapp'@'%' REQUIRE SUBJECT '/CN=webapp1/O=myorg/OU=prod'; -
GRANT或ALTER USER不能补救:只有CREATE USER时指定才生效;ALTER USER ... REQUIRE SUBJECT ''会清空全部约束
客户端连接必须显式传三个 PEM 文件,且权限为 600
mysql 命令行不读 [client] 段的 SSL 配置,漏掉任一参数都会静默失败,报错仍是 Access denied for user,而非明确 SSL 错误。
最小必要参数组合:
mysql --ssl-mode=REQUIRED \ --ssl-ca=/path/to/ca.pem \ --ssl-cert=/path/to/client-cert.pem \ --ssl-key=/path/to/client-key.pem \ -u webapp -p
关键细节:
-
--ssl-mode=REQUIRED不可省略,设成PREFERRED或DISABLED会退化为非 SSL 连接 -
--ssl-cert和--ssl-key必须成对出现,私钥不能加密(生成时必须加-nodes) - 所有 PEM 文件在 Linux 上权限必须是
600,且属主为运行 mysql 客户端的用户(否则直接拒绝读取) - 服务端
ssl_ca必须包含签发客户端证书的那个 CA —— 如果用了中间 CA,客户端--ssl-ca要拼入根 CA + 中间 CA
服务端证书路径和权限常被忽略
即使 have_ssl 显示 YES,如果 ssl_ca、ssl_cert、ssl_key 指向错误路径或权限不对,MySQL 启动时会静默跳过加载,后续所有 SSL 行为都失效。
验证方式:
- 检查变量值:
SHOW VARIABLES LIKE 'ssl_%';,确保ssl_ca、ssl_cert、ssl_key是非空绝对路径 - 用 mysql 用户身份测试读取:
sudo -u mysql ls -l /var/lib/mysql/ca.pem,私钥server-key.pem必须是-rw------- - SELinux/AppArmor 常拦截:临时执行
setenforce 0测试,若此时have_ssl变为YES,说明是策略拦截 - 不要把证书放
/etc/mysql/或家目录下——mysqld 默认只从数据目录或显式配置的绝对路径加载
最易被绕过的点是:以为生成了证书就等于启用了双向验证。其实三者缺一不可——服务端强制、用户级证书绑定、客户端显式传参。少一个,连接就失败,但错误信息永远不告诉你缺了哪块。











