配置带SSL的Nginx MySQL反向代理后无法连接,如何解决?
问题背景
不带SSL的Nginx MySQL反向代理可正常工作,但启用SSL后无法建立连接,SSL证书颁发给my.domain。
当前Nginx配置
stream { log_format main '$remote_addr - [$time_local] $status'; upstream demo-db { server 127.0.0.1:3300; } server { listen 6030 ssl so_keepalive=on; server_name my.domain; # 移除该行无变化 access_log /var/log/nginx/demo.db.log main; ssl_certificate <hidden>; ssl_certificate_key <hidden>; ssl_protocols TLSv1.3; proxy_socket_keepalive on; proxy_pass demo-db; } }
异常现象
- 访问日志出现500状态:
<ip> - [14/Aug/2024:11:57:16 +0200] 500
- Nginx错误日志无内容。
- 用Postman请求
https://my.domain:6030报错:
Parse Error:
The server returned a malformed response-Error: Parse Error: Expected HTTP/
- MySQL客户端DBeaver(无论是否启用SSL配置)报错:
Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.
详情:
Communications link failureThe last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
配置调整与排查步骤
1. 确认MySQL服务端SSL支持
MySQL自身需开启SSL,检查my.cnf(或my.ini)的[mysqld]段配置:
[mysqld] ssl_cert=/path/to/server-cert.pem ssl_key=/path/to/server-key.pem ssl_ca=/path/to/ca-cert.pem require_secure_transport=ON # 可选,强制所有连接使用SSL
配置后重启MySQL,执行SHOW VARIABLES LIKE '%ssl%';,确保have_ssl值为YES。
2. 明确Nginx的SSL处理逻辑
Nginx Stream模块处理TCP流量时,SSL配置有两种模式,需对应调整:
- SSL终止模式:Nginx处理SSL握手,向后端MySQL转发明文流量
- 确保Nginx的证书、密钥路径正确,文件权限为
root:root,密钥权限设为600 - MySQL客户端连接时不要启用SSL选项(因为Nginx已完成加密,转发的是明文)
- 确保Nginx的证书、密钥路径正确,文件权限为
- SSL透传模式:Nginx不处理SSL,直接转发加密流量到MySQL
- 移除Nginx配置中所有
ssl_*相关参数,恢复为普通TCP反向代理,让客户端与MySQL直接完成SSL握手
- 移除Nginx配置中所有
3. 兼容SSL协议版本
仅启用TLSv1.3可能导致兼容性问题,建议补充TLSv1.2支持并指定安全加密套件:
ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off;
4. 验证证书有效性
检查证书与私钥是否匹配:
openssl x509 -noout -modulus -in /path/to/your/cert.pem | openssl md5 openssl rsa -noout -modulus -in /path/to/your/key.pem | openssl md5
两次输出的MD5值必须完全一致,否则证书与密钥不匹配。
5. 端口与防火墙检查
确认6030端口已对外开放,防火墙(如ufw、iptables)允许该端口的TCP流量通过。
关键测试说明
Postman发送HTTP请求的测试方式错误,因为Nginx Stream模块处理的是TCP流量而非HTTP。正确测试方式用openssl s_client验证SSL握手:
openssl s_client -connect my.domain:6030
若握手成功,会显示SSL连接详情,后续可输入MySQL命令测试连通性。
内容的提问来源于stack exchange,提问作者MarekChr

