MySQL本身无法阻止指定IP连接,必须用iptables或firewalld在网络层拦截TCP握手,配合bind-address和user@host精确匹配,并启用skip_name_resolve确保IP比对准确。

mysql 本身没有“阻止指定 IP 连接”的开关,只靠 GRANT 或 DROP USER 无法真正拦住 TCP 握手——只要 IP 能发包到 3306 端口,MySQL 就会响应 SYN,甚至返回 Access denied for user,反而暴露服务存在。
用 iptables 在网络层 DROP 最有效
这是唯一能确保对方“连都连不上”的方式,必须在 TCP 握手前拦截。
-
iptables -I INPUT -s 203.0.113.45 -p tcp --dport 3306 -j DROP:-I 插入链首,避免被前面的 ACCEPT 规则放过 - 别漏
--dport 3306:不加会误杀 SSH、HTTP 等其他端口 - 封完立刻验证:
telnet 203.0.113.45 3306应超时或无响应;若返回Connection refused,说明 MySQL 已响应,防火墙没生效 - 持久化保存:
iptables-save > /etc/iptables/rules.v4(Debian/Ubuntu)或service iptables save(CentOS 6)
firewalld 用户请用 rich rule 拒绝而非丢弃
firewalld 默认策略更偏向“拒绝并通知”,适合调试;但生产环境建议用 reject 配合 --permanent。
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.45" port port="3306" protocol="tcp" reject'- 务必加
--permanent,否则重启后失效 - 执行
firewall-cmd --reload生效 - 测试方式同上:
telnet 203.0.113.45 3306不应得到任何响应
别指望 GRANT 或 DROP USER 拦连接请求
它们只控制登录后的权限,对握手阶段完全无效。常见错误操作包括:
- 执行
REVOKE ALL ON *.* FROM 'user'@'203.0.113.45':如果该user@host组合根本不存在(比如只有'user'@'%'),这条命令不报错也不起作用 - 只删
'root'@'203.0.113.45'却留着'root'@'%':攻击者仍可用 root 从该 IP 登录 - 以为
bind_address = 127.0.0.1就安全了:它只限制监听地址,和“阻止某 IP”无关;且一旦设成0.0.0.0或内网 IP,就全靠防火墙兜底
Host 字段不是黑名单,是白名单匹配逻辑
MySQL 的 host 字段本质是“允许从哪来”,不是“禁止从哪来”。它只支持 % 和 _,不支持 CIDR、正则、子网掩码。
-
'app'@'192.168.1.%'匹配 192.168.1 开头的所有字符串(含192.168.1.999,MySQL 静默截断为192.168.1.99) -
'app'@'192.168.1.0/24'是无效写法,会被当字面量处理,几乎不可能匹配成功 - 想让某用户只能从特定 IP 连,必须显式创建
'app'@'203.0.113.45',再删掉所有宽泛账号如'app'@'%' - 改完必须
FLUSH PRIVILEGES,否则新规则不生效
真正起作用的永远是三层协同:防火墙规则 + bind-address 监听范围 + user@host 精确匹配。少一层,就可能被绕过。尤其注意 DNS 反查干扰——开启 skip_name_resolve=ON 后,MySQL 只比对原始 IP,更可控也更可预测。


















