数据库跑在公网或者内网跨网段传输时,数据包默认是明文裸奔的。用抓包工具随便一抓,SQL语句、账号密码、业务数据全都一览无余。这不是危言耸听,而是每天都在发生的真实情况。解决这个问题的标准做法就一个:给数据库连接套上SSL/TLS加密层。但很多人在配置时容易踩坑,证书过期了也不知道怎么换,导致业务中断。下面直接把配置方法和证书轮换流程讲清楚。
数据库SSL加密到底保护了什么SSL/TLS作用于传输层,在TCP握手完成后、数据库协议交互前插入一层加密协商。一旦加密通道建立,客户端和服务端之间传输的所有数据都会被加密,包括登录认证包、SQL查询语句和返回的结果集。抓包的人只能看到一堆乱码,无法还原出原始内容。
更重要的是它还提供身份验证。客户端可以通过验证服务端证书来确认自己连的是不是真正的数据库服务器,而不是某个中间人伪造的节点。同样,服务端也可以要求客户端出示证书,实现双向认证。这种机制在金融、政务等强监管场景下几乎是强制要求。
MySQL 8.0 SSL配置全流程MySQL从5.7版本开始默认自动生成自签名证书,但那个只能用于测试,生产环境必须用正规CA签发的证书或者企业内部CA签发的证书。先看服务端配置。
第一步,准备证书文件。你需要三个东西:CA证书、服务端证书、服务端私钥。如果企业有自建CA,就用CA签发;没有的话可以用OpenSSL自己搭建一个内部CA。生产环境不建议用自签名证书,因为每个客户端都得手动信任,管理成本太高。
第二步,把证书放到MySQL的配置目录,通常是/etc/mysql/certs/或者/var/lib/mysql/。然后修改my.cnf配置文件:
[mysqld] ssl_ca=/etc/mysql/certs/ca-cert.pem ssl_cert=/etc/mysql/certs/server-cert.pem ssl_key=/etc/mysql/certs/server-key.pem require_secure_transport=ON
注意require_secure_transport=ON这个参数,它强制所有连接必须使用SSL,不加密的连接直接拒绝。如果你的业务中还有少量无法使用SSL的旧客户端,可以先设成OFF,等迁移完再打开。
第三步,重启MySQL服务,然后登录进去验证SSL是否生效:
SHOW VARIABLES LIKE '%ssl%'; SHOW STATUS LIKE 'Ssl_cipher';
看到have_ssl显示YES,Ssl_cipher显示当前使用的加密套件名称,就说明SSL已经启用了。
第四步,创建必须使用SSL连接的用户。MySQL支持在用户级别强制SSL:
CREATE USER 'appuser'@'%' IDENTIFIED BY 'strong_password' REQUIRE SSL;
如果要双向认证,也就是要求客户端也出示证书,用这个语法:
CREATE USER 'appuser'@'%' IDENTIFIED BY 'strong_password' REQUIRE X509;
客户端连接时需要在连接串里指定证书路径。以mysql命令行客户端为例:
mysql -h dbhost -u appuser -p \ --ssl-ca=/path/to/ca-cert.pem \ --ssl-cert=/path/to/client-cert.pem \ --ssl-key=/path/to/client-key.pem
JDBC连接串的写法稍有不同,需要把SSL相关参数拼在URL里:
jdbc:mysql://dbhost:3306/mydb?useSSL=true&requireSSL=true& trustCertificateKeyStoreUrl=file:/path/to/truststore.jks& trustCertificateKeyStorePassword=changeit& clientCertificateKeyStoreUrl=file:/path/to/keystore.jks& clientCertificateKeyStorePassword=changeit
这里有个容易忽略的点:MySQL的JDBC驱动默认会验证服务端证书的hostname,如果你的证书CN字段和实际主机名不一致,连接会失败。可以在连接串里加verifyServerCertificate=false跳过验证,但生产环境不建议这样做。
PostgreSQL SSL配置要点PostgreSQL的SSL配置思路和MySQL类似,但细节上有不少差异。配置文件是postgresql.conf,相关参数如下:
ssl = on ssl_ca_file = '/etc/ssl/pgsql/root.crt' ssl_cert_file = '/etc/ssl/pgsql/server.crt' ssl_key_file = '/etc/ssl/pgsql/server.key'
PostgreSQL对私钥文件的权限检查非常严格,必须设置为0600,属主必须是postgres用户,否则服务启动时会报错。这个坑很多人都踩过。
更精细的控制在pg_hba.conf文件里。你可以针对不同的数据库、用户、来源IP设置不同的SSL策略:
# 强制SSL连接 hostssl all all 192.168.0.0/24 md5 clientcert=verify-full # 允许非SSL连接 host all all 10.0.0.0/8 md5
hostssl表示只接受SSL连接,host表示SSL和非SSL都可以。clientcert=verify-full表示要求客户端证书并且验证证书中的CN字段与数据库用户名一致,这是双向认证的严格模式。
客户端连接时,psql命令行的写法:
psql "host=dbhost port=5432 dbname=mydb user=appuser \ sslmode=verify-full \ sslrootcert=/path/to/root.crt \ sslcert=/path/to/client.crt \ sslkey=/path/to/client.key"
sslmode参数有几个级别:disable是不用SSL,require是要求SSL但不验证证书,verify-ca是验证证书但不验证主机名,verify-full是完整验证。生产环境至少要用verify-ca,有条件就用verify-full。
MongoDB SSL配置的特殊之处MongoDB的SSL配置稍微复杂一些,因为它支持集群内部通信和客户端通信分别设置证书。配置文件mongod.conf里的写法:
net:
tls:
mode: requireTLS
certificateKeyFile: /etc/mongo/server.pem
CAFile: /etc/mongo/ca.pem
注意MongoDB要求证书文件和私钥文件合并成一个pem文件,证书内容在前,私钥内容在后。如果你手里是分开的两个文件,需要手动合并:
cat server.crt server.key > server.pem chmod 600 server.pem
如果是副本集或分片集群,还需要配置集群内部通信的SSL:
net:
tls:
mode: requireTLS
certificateKeyFile: /etc/mongo/server.pem
CAFile: /etc/mongo/ca.pem
clusterFile: /etc/mongo/cluster.pem
clusterCAFile: /etc/mongo/cluster-ca.pem
MongoDB Shell连接时的写法:
mongosh --host dbhost --tls \ --tlsCAFile /path/to/ca.pem \ --tlsCertificateKeyFile /path/to/client.pem
这里有个关键点:MongoDB默认启用TLS协议,如果你用的是旧版本或者某些特殊场景需要指定TLS版本,可以加net.tls.disabledProtocols参数来禁用不安全的旧版本协议。
证书轮换的完整流程证书都是有有效期的,到期不换服务就会中断。很多团队因为怕出问题,选择给证书设一个很长的有效期,比如十年。这其实是在埋雷,时间越长越容易忘记,到时候翻车的影响面更大。建议生产证书有效期控制在一年以内,并且建立标准化的轮换流程。
证书轮换的核心难点在于:数据库服务不能停,客户端连接不能断,新旧证书要平滑过渡。不同数据库的实现方式略有不同,但整体思路一致。
MySQL的证书轮换相对简单。MySQL支持动态重新加载SSL证书,不需要重启服务。步骤如下:
第一步,准备好新的证书文件,放到服务器上,注意不要覆盖旧证书,用不同的文件名区分,比如server-cert-2025.pem。
第二步,登录MySQL执行重新加载命令:
ALTER INSTANCE RELOAD TLS;
这个命令会让MySQL重新读取配置文件里指定的证书文件。所以你需要先把my.cnf里的证书路径指向新文件,然后再执行这条命令。从MySQL 8.0.21开始,还可以指定从哪个文件重新加载,不用改配置文件:
ALTER INSTANCE RELOAD TLS FOR CHANNEL mysql_main CERTIFICATE '/etc/mysql/certs/server-cert-2025.pem' KEY '/etc/mysql/certs/server-key-2025.pem';
第三步,验证新证书是否生效。新建一个SSL连接,查看连接使用的证书信息:
SELECT ssl_version, ssl_cipher FROM performance_schema.session_status
WHERE VARIABLE_NAME IN ('Ssl_version', 'Ssl_cipher');
已有的老连接不会受影响,它们继续使用旧证书建立的加密通道,直到连接断开重连后自动使用新证书。这样就实现了平滑过渡。
PostgreSQL的证书轮换稍微麻烦一点。PostgreSQL不支持动态重载SSL证书,必须重启服务或者发送SIGHUP信号让进程重新加载配置。但SIGHUP只能重载部分配置,SSL证书不在可重载范围内,所以实际上需要重启。
为了减少重启带来的影响,可以配合连接池和负载均衡来做。具体做法是:先把要重启的节点从负载均衡里摘掉,等现有连接自然耗尽,然后重启该节点加载新证书,验证通过后再加回负载均衡。逐个节点滚动操作,整个集群对外不中断。
如果只有单实例,那就只能选业务低峰期操作。重启前务必通知相关业务方,准备好回滚方案。回滚就是把配置文件里的证书路径改回旧证书,再重启一次。
MongoDB的证书轮换在4.2版本之后支持了TLS证书的动态轮换,不需要重启mongod进程:
db.adminCommand({
rotateCertificates: 1,
certificateKeyFile: "/etc/mongo/server-2025.pem",
CAFile: "/etc/mongo/ca-2025.pem"
});
执行这个命令后,MongoDB会加载新证书,新进来的连接使用新证书,已有连接保持不变。这个功能在生产环境中非常实用。
对于副本集,建议先在从节点上逐个执行证书轮换,观察一段时间确认没有问题后,再对主节点执行。如果主节点做了轮换后发现异常,可以快速切主回滚。
证书轮换自动化与监控靠人记证书到期时间不靠谱,必须上自动化。基本思路是:用监控系统定期检查证书有效期,提前告警;用自动化脚本或配置管理工具执行轮换操作。
证书有效期检查可以用OpenSSL命令:
openssl x509 -in /path/to/server-cert.pem -noout -enddate
把这个命令集成到监控脚本里,计算剩余天数,少于30天就发告警。Prometheus有现成的ssl_exporter可以采集证书信息,接入Grafana做可视化展示和告警。
自动化轮换方面,如果企业有自建CA,可以用cert-manager这类工具自动申请和续签证书。证书签发后通过Ansible或SaltStack推送到数据库服务器,然后调用数据库的证书重载接口完成切换。整个流程可以写成CI/CD流水线,定时触发或者人工审批后一键执行。
还有一个容易被忽视的点:客户端证书也要轮换。如果启用了双向认证,客户端证书过期同样会导致连接失败。客户端证书的轮换策略需要和业务发布流程结合,在发布新版本时同步更新证书文件。
常见问题与排查思路SSL配置出问题时的报错信息通常比较明确,但新手可能看不懂。下面列几个高频问题。
问题一:连接时报"SSL connection error: unable to verify the first certificate"。这通常是因为客户端没有信任服务端证书的签发CA。检查客户端的ssl_ca参数是否指向了正确的CA证书文件。
问题二:报"certificate subject name does not match target host name"。证书的CN或SAN字段里没有包含客户端连接时使用的主机名。要么重新签发证书加上正确的主机名,要么在连接串里关闭hostname验证(不推荐)。
问题三:MySQL的ALTER INSTANCE RELOAD TLS执行失败,报权限不足。这个操作需要CONNECTION_ADMIN权限,确认当前用户是否有这个权限。
问题四:PostgreSQL启动失败,日志里报"private key file has group or world access"。私钥文件权限不对,必须设成0600,并且属主是postgres用户。
问题五:配置了SSL但性能明显下降。SSL握手和加解密确实会消耗CPU资源,高并发场景下影响更明显。可以通过启用SSL会话复用、选择性能更好的加密套件、或者使用硬件加速卡来缓解。MySQL的ssl_session_cache_mode参数可以控制会话缓存的启用。
问题六:内网环境要不要开SSL?很多人的误区是内网就安全了,不需要加密。但实际上内网横向攻击是常见的安全威胁,攻击者一旦进入内网就可以随意抓包。所以即使是内网,敏感数据库也应该开启SSL。如果确实觉得性能开销太大,可以只在跨网段或跨机房的连接上强制SSL,同机房的连接允许非SSL,通过pg_hba.conf或MySQL的用户权限做精细化控制。
数据库SSL加密不是什么高深技术,但细节多、坑也多。关键是建立一套从证书签发、配置部署、到期监控到自动轮换的完整闭环。把这套流程跑顺了,数据库传输层的安全才算真正落地。
