要实现仅允许本机TCP回环访问,必须创建'user'@'127.0.0.1'账号而非'localhost',因前者强制走TCP/IP协议栈并配合bind-address=127.0.0.1形成双重防护,而后者走Unix socket且无法限制TCP连接。

为什么用 'user'@'127.0.0.1' 而不是 'user'@'localhost'
因为你要的是「走 TCP 回环」的本地访问控制,不是 Unix 套接字。两者行为完全不同:'user'@'localhost' 会强制客户端跳过 TCP 栈、直连 /var/run/mysqld/mysqld.sock,而你目标是限制 TCP 层访问——只允许本机进程通过 127.0.0.1:3306 连接,拒绝所有外部 IP 的 TCP 请求。
常见错误是建了 'user'@'localhost' 后发现 DBeaver 或 Python 脚本连不上,其实是客户端默认解析 localhost 成 127.0.0.1 并走 TCP,结果匹配不到该账号。
创建用户时必须显式指定 host 为 127.0.0.1
不能靠改表或模糊匹配,MySQL 的权限校验是精确匹配 User 和 Host 字段:
- 先删掉已有宽泛账号(如
'user'@'%'或'user'@'localhost'):DROP USER 'user'@'%'; - 再创建严格账号:
CREATE USER 'user'@'127.0.0.1' IDENTIFIED BY 'strong_password'; - 授予权限时 host 必须完全一致:
GRANT SELECT, INSERT ON mydb.* TO 'user'@'127.0.0.1'; - MySQL 8.0.16+ 不需要
FLUSH PRIVILEGES,但执行也无害
bind-address = 127.0.0.1 是必要但不充分条件
仅设 bind-address = 127.0.0.1 只让 MySQL 监听回环接口,但它不阻止本机其他进程连 —— 你的账号权限才是第二道防线。验证是否生效:
- 检查监听:
ss -tlnp | grep :3306应只输出127.0.0.1:3306,不能是*:3306 - 测试连接:
mysql -h 127.0.0.1 -u user -p应成功;mysql -h 公网IP -u user -p应直接被内核拒绝(Connection refused),而非报 Access denied - 确认没漏掉 IPv6:若服务器启用了
::1,需额外建'user'@'::1'或在配置中加skip-networking(但会禁用所有 TCP)
容易被忽略的客户端兼容性问题
某些工具(如旧版 Navicat、HeidiSQL、部分 Docker 内应用)会把 host=localhost 自动转成 127.0.0.1 并走 TCP,但如果你只建了 'user'@'localhost',它就匹配失败。反过来,如果你建的是 'user'@'127.0.0.1',它们反而能连上 —— 这正是你想要的效果。但要注意:
- PHP 的
mysqli_connect('localhost', ...)在 Linux 下默认走 socket,要连127.0.0.1得显式写mysqli_connect('127.0.0.1', ...) - Python 的
mysql-connector-python默认解析localhost为 socket,需传host='127.0.0.1'参数 - 容器内应用若用
host.docker.internal或服务名,可能绕过本机绑定,得靠 Kubernetes NetworkPolicy 或 iptables 补防
真正起作用的是 bind-address + 'user'@'127.0.0.1' 组合,而不是单点配置;任何一环松动(比如忘了删 'user'@'%'),整个限制就失效。


















