MySQL GRANT不支持CIDR网段语法,仅接受具体IP(如'192.168.1.10')或通配符主机模式(如'192.168.1.%'),%匹配单段任意字符串,\_仅匹配单字符,无法表达/24等子网逻辑。

MySQL 的 GRANT 不支持 CIDR 网段语法
MySQL 原生的 GRANT 语句不识别 192.168.1.0/24 这类 CIDR 表示法,直接写会报错 ERROR 1133 (HY000): Can't find any matching row in the user table 或权限拒绝。它只认两种形式:具体 IP(如 '192.168.1.10')或带通配符的主机模式(如 '192.168.1.%'),且通配符仅限 % 和 _,不能嵌套或组合成子网逻辑。
这意味着你无法用一条 GRANT 覆盖标准的 /24、/27 等网段,必须手动拆解或换思路。
-
%匹配任意长度字符串(包括空),但只匹配一个“段”——'192.168.1.%'可匹配192.168.1.5,但不匹配192.168.10.5(因为10≠1) -
_只匹配单个字符,'192.168.1._'实际上等价于'192.168.1.x',但 x 必须是单数字,无法覆盖192.168.1.100 - IPv6 地址在旧版本 MySQL('2001:db8::%' 可能被截断或解析失败
用 host 字段手动构造网段匹配(适用于已知固定网段)
如果授权对象是稳定内网段(比如运维服务器统一走 10.20.30.0/26),最稳妥的做法是反向推导出所有合法的 host 模式,填入 mysql.user 表,再刷新权限。这不是“优雅”,但可控、无歧义。
例如 10.20.30.0/26 对应 IP 范围是 10.20.30.0–10.20.30.63,可拆成:
-
'10.20.30.0'到'10.20.30.9'→ 用'10.20.30.0','10.20.30.1', ...,'10.20.30.9'(共 10 条) -
'10.20.30.10'到'10.20.30.19'→ 用'10.20.30.1_'(覆盖 10–19) -
'10.20.30.20'到'10.20.30.29'→ 同样用'10.20.30.2_' -
'10.20.30.30'到'10.20.30.63'→ 拆成'10.20.30.3%','10.20.30.4%','10.20.30.5%','10.20.30.6%'(注意'10.20.30.6%'会匹配到10.20.30.60–63,刚好够用)
然后逐条执行:
INSERT INTO mysql.user (Host, User, authentication_string, ssl_type) VALUES ('10.20.30.1_', 'app', PASSWORD('xxx'), 'ANY');
最后别忘 FLUSH PRIVILEGES;。
用代理层或防火墙做真实网段控制(推荐用于生产)
MySQL 本身不是网络网关,硬在 SQL 层模拟子网逻辑既难维护又易出错。更合理的方式是把访问控制下沉:
- 在数据库服务器操作系统上用
iptables或nftables限制源 IP 段,例如:iptables -A INPUT -p tcp --dport 3306 -s 192.168.5.0/24 -j ACCEPT
再加一条默认 DROP - 用 HAProxy 或 Cloudflare Tunnel 做前置代理,在代理层做 CIDR 匹配,MySQL 只对代理 IP 授权(如
'127.0.0.1'或'10.0.0.10') - 云厂商 RDS 通常提供“白名单 IP 段”功能(如阿里云的
192.168.1.0/24),那是他们代理层实现的,不是 MySQL 本体能力
这种分层做法让 MySQL 专注数据逻辑,网络策略由专业组件承担,也避免了 mysql.user 表膨胀和权限缓存不一致问题。
MySQL 8.0+ 的 VALIDATE PASSWORD 和连接限制无关,别混淆
有人看到 validate_password 插件或 max_connections 就想凑合用,但这些跟 IP 段控制完全无关:validate_password 只校验密码强度,max_connections 是总连接数上限,max_user_connections 是用户级并发数,都不检查来源 IP。
试图用它们替代网段控制,结果就是:权限看似给了,但任意公网 IP 都能连上(只要知道账号密码),或者干脆连不上却查不出原因。
真正要拦 IP,就得在连接建立前完成判断——要么 MySQL 的 host 字段(粗糙但直接),要么网络层(精确且可审计),没有中间路线。











