简介
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连接到数据库,客户端会连不上
勾选上使用SSL复选框时,连接成功
当用户被强制要求提供客户端证书时
如果不勾选使用验证,即如果不提供客户端证书,只开始SSL的话,会报错
如果提供了合法的客户端证书,可以成功连接
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就能安全地连接到远程数据库了。