mysql本身不支持cidr语法限制ip段,必须网络层(iptables/安全组)与数据库层(user@'x.x.x.%')双重控制;iptables需用-i插入链首并指定--dport 3306,host字段仅支持%和_通配符,不解析子网掩码。

mysql 本身不支持 CIDR(如 192.168.5.0/24)语法限制 IP 段,所谓“限制 IP 段”必须拆解为网络层拦截 + 数据库层账号控制,缺一不可。单靠任一层,都可能被绕过。
iptables 封禁 IP 段必须插在链首且指定端口
MySQL 连接建立前,TCP 握手就已发生;若防火墙规则位置不对,请求会直接抵达 mysqld,暴露服务甚至触发日志。
- 错误写法:
iptables -A INPUT -s 192.168.5.0/24 -j DROP——追加在末尾,大概率被前面的ACCEPT规则提前放行 - 正确写法:
iptables -I INPUT -s 192.168.5.0/24 -p tcp --dport 3306 -j DROP——-I确保插入链首,--dport 3306防止误杀其他服务 - 封完立刻验证:
telnet 192.168.5.10 3306应超时或拒绝连接;若返回Connection refused,说明 mysqld 已响应,防火墙没生效 - 持久化保存(Debian/Ubuntu):
iptables-save > /etc/iptables/rules.v4
mysql.user 的 Host 字段只认 % 和 _,不解析 CIDR
执行 UPDATE mysql.user SET Host = '192.168.5.0/24' 或 CREATE USER 'u'@'192.168.5.0/24' 全部无效——MySQL 把它当纯字符串匹配,而客户端发来的是 192.168.5.42,两者不等价。
- 允许 C 段的写法只能是:
'u'@'192.168.5.%'——这是前缀通配,不是子网计算 - 风险点:
'192.168.5.%'会匹配192.168.5.abc.example.com或192.168.5.1234(MySQL 静默截断非法部分后仍匹配) - 不能对已有
'u'@'%'执行REVOKE ... FROM 'u'@'192.168.5.%'——权限表里根本不存在这个 host 组合 - 安全做法:先
DROP USER 'u'@'%',再CREATE USER 'u'@'192.168.5.%'并GRANT
bind-address 和 skip_name_resolve 必须协同生效
bind-address 不是“IP 限制开关”,而是监听地址控制;若设为 0.0.0.0 或 *,又没配防火墙,Host 字段再严也没用。
- 确认实际监听地址:
ss -tlnp | grep :3306,输出含*:3306或0.0.0.0:3306才需防火墙干预 - 生产环境推荐设为内网 IP(如
bind-address = 192.168.1.10),避免监听公网接口 - 务必启用
skip_name_resolve = ON,否则 MySQL 会对客户端 IP 做 DNS 反查,导致延迟、失败甚至匹配错 host - 改完配置后必须重启:
systemctl restart mysql
localhost 和 127.0.0.1 是两个完全独立账号
很多本地应用连不上,就栽在这里。MySQL 对 'u'@'localhost' 强制走 Unix socket,而 'u'@'127.0.0.1' 走 TCP;协议不同,权限不互通。
- 应用连
host=127.0.0.1(比如 Docker 容器内、某些 ORM 默认行为),'u'@'localhost'的权限完全不生效 - 测试方式:
mysql -h 127.0.0.1 -u u -p→ 匹配'u'@'127.0.0.1';mysql -h localhost -u u -p→ 匹配'u'@'localhost' - 需要两者都支持?必须显式建两个账号:
CREATE USER 'u'@'localhost'和CREATE USER 'u'@'127.0.0.1',并分别授权 - MySQL 8.0+ 不支持直接
ALTER USER ... IDENTIFIED BY修改 host,删旧建新是唯一可靠路径
bind-address、user@host)中有一层没对齐,或者忘了 skip_name_resolve 导致 DNS 反查干扰 host 匹配。每次调整后,务必用真实客户端(而非仅 mysql -h 本机)从目标 IP 测试连接。











