iptables规则必须用-i插入链首才能生效,因匹配自上而下;mysql网段控制需结合iptables(网络层阻断)与'192.168.5.%'用户host(字符串匹配),并协同配置bind-address和skip_name_resolve。

iptables -I 必须插在链首,否则规则无效
MySQL 连接请求在 TCP 握手阶段就被拦截,靠的是 iptables 在网络层丢包。如果规则用 -A INPUT 追加到末尾,前面已有 ACCEPT 规则(比如默认放行所有 ESTABLISHED 连接),那你的 DROP 就永远不生效。
- 正确写法:
iptables -I INPUT -s 192.168.5.0/24 -p tcp --dport 3306 -j DROP——-I确保插入最前 - 必须指定
--dport 3306,否则会误杀 SSH、HTTP 等其他服务 - 封禁后立刻验证:
telnet 192.168.5.10 3306应该超时或被拒绝;如果返回Connection refused,说明 MySQL 已响应,iptables 没起作用 - 持久化保存:Debian/Ubuntu 执行
iptables-save > /etc/iptables/rules.v4;CentOS 6 用service iptables save
mysql.user 表的 Host 字段不支持 CIDR,只认 % 和 _
你不能写 UPDATE mysql.user SET Host = '192.168.5.0/24',MySQL 解析 Host 时根本不识别斜杠和数字。它只做字符串前缀匹配,不是子网计算。
- 想覆盖 C 类网段,只能用
'192.168.5.%'—— 注意是点加%,不是/24 - 这个写法有副作用:它也会匹配
192.168.5.abc.example.com或非法 IP192.168.5.999(MySQL 静默截断为192.168.5.99) - 别试图对已有
'user'@'%'账号执行REVOKE来“补救”——权限系统里根本不存在'user'@'192.168.5.%'这条记录,REVOKE无效 - 真正有效的是先
DROP USER 'user'@'%',再CREATE USER 'user'@'192.168.5.%'并重新GRANT
bind-address 和 skip_name_resolve 必须协同配置
只改 my.cnf 里的 bind-address 不等于安全。MySQL 监听地址和防火墙规则、用户 Host 匹配是三者联动关系。
-
bind-address = 127.0.0.1最彻底:外部 IP 根本连不上,但代价是放弃所有远程访问 -
bind-address = 0.0.0.0或未设置 → MySQL 监听所有接口,此时防火墙必须严格限制入向流量 -
skip_name_resolve = ON强制 MySQL 只用客户端原始 IP 匹配 Host 字段,禁用 DNS 反查——否则可能因反向解析失败或延迟导致连接异常或绕过限制 - 验证监听地址:
ss -tlnp | grep :3306,输出含*:3306或0.0.0.0:3306才需防火墙干预;若为127.0.0.1:3306,防火墙规则基本无意义
localhost 和 127.0.0.1 是两个完全不同的账号
很多本地脚本连不上,就卡在这里。MySQL 把 'user'@'localhost' 和 'user'@'127.0.0.1' 当作两个独立账号,行为完全不同。
-
'localhost'强制走 Unix socket,跳过 TCP/IP 栈;'127.0.0.1'明确走 TCP,受 Host 字段和防火墙双重约束 - 应用代码里写
host=127.0.0.1(如 Docker 容器内连接),'user'@'localhost'的权限完全不生效 - 要两者都支持,得分别建账号:
CREATE USER 'dev'@'localhost'和CREATE USER 'dev'@'127.0.0.1',并各自GRANT - 检查当前账号:
SELECT User, Host FROM mysql.user WHERE User = 'dev';,确认是否存在对应 Host 记录
'192.168.5.%' 是字符串模糊匹配,iptables 才是真正在网络层阻断。最容易被忽略的是 skip_name_resolve 开关和 bind-address 实际监听状态——这两项不配好,前面所有操作都可能白忙。











