MySQL本身不支持CIDR网段语法,限制IP段必须网络层(iptables/firewalld)与数据库层(mysql.user的Host字段)协同:iptables需用-I插入链首并限定--dport 3306,Host仅支持%和_通配符(如'user'@'192.168.1.%'),且须与bind-address、skip_name_resolve对齐。

MySQL 本身不支持 CIDR 网段语法(如 192.168.1.0/24),所谓“限制 IP 网段”必须靠网络层(iptables 或云安全组)和数据库层(mysql.user 的 Host 字段)协同生效,缺一不可。
CREATE USER 时 host 必须写成 'user'@'192.168.1.%',不能用 CIDR
MySQL 解析 Host 字段只认 SQL 模式通配符:% 匹配任意长度字符串,_ 匹配单字符,不识别斜杠、子网掩码或正则。写 'user'@'192.168.1.0/24' 或 'user'@'192.168.1.*' 全部无效——它会当纯字符串去匹配客户端发来的 192.168.1.42,必然失败。
-
'user'@'192.168.1.%'是唯一简洁且实际可用的写法,能匹配192.168.1.1到192.168.1.255,但也会静默接受192.168.1.999(MySQL 截断非法部分后仍匹配) - 别用
'user'@'192.168.%':它会放行192.168.10.5、192.168.255.255,甚至192.168.abc.example.com - 已有
'user'@'%'账号必须先DROP USER 'user'@'%',否则新旧账号共存,客户端可能优先匹配到%条目而绕过限制
iptables 封禁 IP 段必须用 -I 插入链首并限定 --dport 3306
MySQL 在 TCP 握手阶段就响应 SYN 包,防火墙规则若位置靠后或没限定端口,攻击者仍能探测服务存在。仅靠数据库权限层拦不住连接请求。
- 错误写法:
iptables -A INPUT -s 192.168.1.0/24 -j DROP——-A追加在末尾,大概率被前面的ACCEPT规则提前放行 - 正确写法:
iptables -I INPUT -s 192.168.1.0/24 -p tcp --dport 3306 -j DROP——-I确保插入最前,--dport 3306防止误杀 SSH/HTTP - 封完立刻验证:
telnet 192.168.1.10 3306应超时或直接拒绝;若返回Connection refused,说明 MySQL 已响应,防火墙没生效 - 持久化保存(Debian/Ubuntu):
iptables-save > /etc/iptables/rules.v4
CURRENT_USER() 和 bind_address 不对齐,限制就白设
权限配置看似完成,但连接仍不受控,往往卡在这两个点上。
- 连上后运行
SELECT USER(), CURRENT_USER();—— 如果CURRENT_USER()返回'user'@'%',说明你建的'user'@'192.168.1.%'根本没被匹配到,可能是旧账号残留或 DNS 反查干扰 - 检查 MySQL 实际监听地址:
ss -tlnp | grep :3306。若输出含*:3306或0.0.0.0:3306,说明它监听了所有接口,此时仅靠Host字段不够,必须配防火墙 - 务必设
skip_name_resolve = ON:否则 MySQL 会对客户端 IP 做 DNS 反查,可能把192.168.1.100解成host100.local,导致匹配失败 -
bind_address不是“IP 限制开关”,而是监听控制。生产环境推荐设为内网 IP(如bind_address = 192.168.1.10),避免监听公网接口
真正起作用的从来不是单点配置,而是 iptables 规则、mysql.user.Host、bind_address、skip_name_resolve 四者对齐。漏掉任意一个,都可能让限制形同虚设。


















