MySQL安全配置需同步校准bind-address、用户host权限和防火墙三层策略:应设bind-address为内网IP(如192.168.10.5),用户host限定为'192.168.10.%',防火墙仅放行内网段3306端口。

bind-address 配置错成 0.0.0.0 就等于裸奔
MySQL 默认监听 127.0.0.1(仅本地),但很多一键安装包或 Docker 镜像会默认设成 0.0.0.0,导致所有网卡都开放,只要知道端口和账号密码,外网就能连——这根本不是“内网访问”,是全网可连。
正确做法是显式指定内网网段的 IP,比如服务器实际内网地址是 192.168.10.5,就在 my.cnf 的 [mysqld] 段写:
bind-address = 192.168.10.5
常见错误现象:netstat -tlnp | grep :3306 显示监听的是 *:3306 或 0.0.0.0:3306;用外网机器 telnet 你的IP 3306 能通。
- 不要用
127.0.0.1代替内网 IP——它只允许本机连接,其他内网机器连不上 - 如果服务器有多个内网网卡(如
eth0和eth1),必须写具体 IP,不能写网卡名 - 改完要
systemctl restart mysqld,且确认进程重启后lsof -i :3306输出里 bind 地址已更新
用户权限没按 host 限制,bind-address 白配
即使 bind-address 锁死了网卡,如果 MySQL 用户的 host 字段是 '%' 或 'localhost' 以外的模糊值,依然可能被绕过。关键在 mysql.user 表的 host 列。
例如,给运维账号 dba 开放内网所有机器访问,应该这样建:
CREATE USER 'dba'@'192.168.10.%' IDENTIFIED BY 'strong-pass';
而不是:
CREATE USER 'dba'@'%' IDENTIFIED BY 'strong-pass';
常见错误现象:内网某台机器能连上,但另一台同网段机器连不上;或者明明绑定了内网 IP,却还能从外网用某个账号登录。
-
'192.168.10.%'匹配192.168.10.1~192.168.10.254,但不匹配192.168.11.5 -
'192.168.10.0/24'这种 CIDR 写法 MySQL 不支持,只能靠%或具体 IP - 执行完
CREATE USER或GRANT后,必须运行FLUSH PRIVILEGES;,否则权限不生效
防火墙没关 3306,bind-address 和权限都白忙
Linux 系统级防火墙(iptables 或 nftables)和云厂商安全组,才是最外层防线。MySQL 自身配置再严,只要防火墙放行了 3306 端口,外网照样能发 SYN 包——哪怕 MySQL 拒绝认证,也会暴露服务指纹和版本信息。
检查当前 iptables 规则是否放行了非内网来源:
iptables -L INPUT -n | grep 3306
如果看到类似 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:3306,说明全网开放。
- 只允许内网段访问的规则应为:
iptables -A INPUT -p tcp --dport 3306 -s 192.168.10.0/24 -j ACCEPT - 这条规则必须放在所有
REJECT或DROP规则之前,顺序错了就无效 - 云服务器(阿里云、腾讯云等)务必去控制台关掉安全组里对 3306 的 “0.0.0.0/0” 授权,只留内网网段
MySQL 8.0+ 的 caching_sha2_password 插件影响内网连接稳定性
MySQL 8.0 默认用 caching_sha2_password 认证插件,某些旧版客户端(如老版本 PHP mysqli、部分 shell 工具)不支持,连内网也会报 Authentication plugin 'caching_sha2_password' cannot be loaded。这不是网络问题,但容易误判为“内网连不通”。
临时解法是降级插件(仅限可信内网环境):
ALTER USER 'your_user'@'192.168.10.%' IDENTIFIED WITH mysql_native_password BY 'password';
更稳妥的做法是升级客户端驱动,或在连接字符串里显式指定插件(如 JDBC 加 &serverRSAPublicKeyFile=...)。
- 该插件需要 OpenSSL 支持 RSA 密钥交换,内网低配容器若没装
openssl包,也可能握手失败 -
skip_ssl不能解决这个问题,SSL 和认证插件是两层机制 - 检查当前用户插件:查询
SELECT user, host, plugin FROM mysql.user WHERE user='xxx';
真正卡住的地方往往不在单点配置,而在于 bind-address、用户 host、防火墙三层策略没对齐——改一个,漏两个,结果还是半开状态。


















