简介

MySQL是最为常用的数据库服务,被广泛地用于存放数据,但是如果你的MySQL数据库和客户端需要通过非可信网络来进行远程通信的话,那么默认情况下通信方向和通信内容都将完全暴露在网路上,中间人可以轻松地监控、窃取与篡改数据。此时我们应当考虑使用SSL/TLS等端到端加密技术来保护通信安全。

幸运的是MySQL从5.7版本开始对SSL功能的支持,本篇文章用于记录如何配置MySQL的SSL与如何使用SSL连接到MySQL。

检查环境与配置证书

查看当前SSL开启状态

首先执行以下SQL,查询当前的SSL状态

show global variables like '%ssl%';
+---------------+----------+
| Variable_name | Value    |
+---------------+----------+
| have_openssl  | DISABLED |
| have_ssl      | DISABLED |
| ssl_ca        |          |
| ssl_capath    |          |
| ssl_cert      |          |
| ssl_cipher    |          |
| ssl_crl       |          |
| ssl_crlpath   |          |
| ssl_key       |          |
+---------------+----------+

我们可以看到,当前MySQL没有开启SSL功能,且没有安装OpenSSL。

查看数据库数据的存放路径

mysql> show variables like 'datadir';

安装openssl

yum install openssl -y

生成与配置证书

你可以选择直接执行mysql的bin目录下的mysql_ssl_rsa_setup脚本,可以自动生成根证书、服务端证书、与客户端证书。但是我推荐自己手动生成,自由度高一些。

更加详细的域名与ip证书的签署方法请看这篇文章:https://www.hash070.top/archives/openssl-note.html

生成CA根证书

#第一步:随机生成ca私钥
openssl genrsa -out ca.key 4096

#第二步:用ca私钥生成ca根证书
openssl req -x509 -new -nodes -key ca.key -subj "/CN=hash070" -days 3650 -out ca.crt
#CN指的是该证书的名称,可以换成你自己的

此时你拥有了一个有效期为10年的根证书与密钥,请妥善保管。你可以使用该根证书来签署其他的子证书。

生成MySQL服务端证书

openssl req -newkey rsa:2048 -days 3000 -nodes -keyout server-key.key -out server-req.pem
openssl rsa -in server-key.key -out server-key.key
openssl x509 -req -in server-req.pem -days 3000 -CA ca.crt -CAkey ca.key -set_serial 01 -out server-cert.crt

生成MySQL客户端证书

openssl req -newkey rsa:2048 -days 3000 -nodes -keyout client-key.key -out client-req.pem
openssl rsa -in client-key.key -out client-key.key
openssl x509 -req -in client-req.pem -days 3000 -CA ca.crt -CAkey ca.key -set_serial 01 -out client-cert.crt

配置证书

当执行完上面那些命令后,你当前的目录下应该有以下这些文件

ca.crt
ca.key
client-cert.crt
client-key.key
client-req.pem
server-cert.crt
server-key.key
server-req.pem

你需要修改这些文件的所有者为mysql并修改权限为500,或者直接调整权限为555以确保该证书能够被正常读取。

打开MySQL配置文件,找到[mysqld],配置证书与密钥的路径

示例:

[mysqld]
ssl-ca=/www/server/data/ca.crt
ssl-cert=/www/server/data/server-cert.crt
ssl-key=/www/server/data/server-key.key

其中ssl-ca指的是跟证书,ssl-cert指的是服务端证书,ssl-key指的是服务端密钥。

请自己证书文件的所在路径参照填写。

重启MySQL

systemctl restart mysqld

再次检查当前SSL开启状态

show global variables like '%ssl%';
+---------------+----------+
| Variable_name | Value    |
+---------------+----------+
| have_openssl  |    YES   |
| have_ssl      |    YES   |
| ssl_ca        |          |
| ssl_capath    |          |
| ssl_cert      |          |
| ssl_cipher    |          |
| ssl_crl       |          |
| ssl_crlpath   |          |
| ssl_key       |          |
+---------------+----------+

至此,MySQL服务端SSL证书配置完毕,客户端已经可以使用SSL连接到服务端了。

用户SSL配置

设置用户是否强制使用SSL

#强制用户'testtest'使用SSL连接到数据库
alter user 'testtest'@'%' require SSL;
#取消强制
alter user 'testtest'@'%' require NONE;
#强制用户'testtest'使用SSL并要求提供有效的客户端证书
alter user 'testtest'@'%' require X509;

客户端连接测试

当用户被强制使用SSL时

如果客户端不使用SSL连接到数据库,客户端会连不上

img

勾选上使用SSL复选框时,连接成功

img

当用户被强制要求提供客户端证书时

如果不勾选使用验证,即如果不提供客户端证书,只开始SSL的话,会报错

img

如果提供了合法的客户端证书,可以成功连接

img

JDBC使用SSL连接MySQL

在JDBC的url里添加上如下参数,要求使用SSL

useSSL=true

如果连接时报错

java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors

这意味着你没有安装根证书到客户端电脑上,客户端无法验证服务端证书的合法性。

此时你可以选择跳过证书验证

verifyServerCertificate=false

但这么做就不安全了,中间人可以发起劫持攻击而客户端却浑然不觉,这就违背了我们配置SSL的初衷了。
正确的做法应该是信任根证书
我们可以手动向JDK中导入根证书

keytool -import -trustcacerts -v -alias Mysql -file "C:\ProgramData\MySQL\MySQL Server 8.0\Data\ca.crt" -keystore "C:\Program Files\Java\jdk1.8.0_192\jre\lib\security\cacerts"

这是一个可行的办法但是太麻烦了

MySQL的JDBC驱动提供了另一个方便的办法
我们可以将证书导入一个证书库,设置好密码

keytool -import -trustcacerts -v -alias Mysql -file "C:\ProgramData\MySQL\MySQL Server 8.0\Data\ca.pem" -keystore "mysql.ks"

然后将证书库放http服务器上,或ftp服务器上
在JDBC的url里配置

trustCertificateKeyStoreUrl=https://1.1.1.1:8080/keys/mysql.ks

trustCertificateKeyStorePassword=123456

此时JDBC就能安全地连接到远程数据库了。

Q.E.D.