ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MySQL ERROR 1290:skip-grant-tables权限修复指南

MySQL ERROR 1290:skip-grant-tables权限修复指南 1. 为什么[ERROR 1290 (HY000)]偏偏只拦权限语句先说结论这个报错不是密码错了也不是账号被锁而是当前MySQL实例正处于一种“裸奔”启动状态——--skip-grant-tables。我第一次遇到ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement是在帮客户调整一个业务账号权限的时候。当时执行一条很普通的GRANT SELECT ON ... TO app%结果直接被MySQL拦下来了。报错信息其实写得很直白MySQL告诉我当前服务是用--skip-grant-tables方式启动的所以“不能执行这条语句”。--skip-grant-tables是MySQL启动参数里的一个“紧急逃生口”。正常情况下mysqld启动时会去加载mysql库里的授权表包括user、db、tables_priv、columns_priv等用这些表来鉴权所有连接。但加上这个参数后MySQL会跳过授权表的加载和校验结果就是任何用户、从任何客户端连上来都不需要密码以任意用户名都可以进库。听起来很方便对吧对于忘记root密码的情况这确实是最常用的救命方案。但问题在于这个模式下能做的事很有限权限管理相关的语句基本全被禁用。1.1 哪些语句会触发1290MySQL官方把这些语句统一归类为“privilege statements”简单说就是所有跟账号、授权、角色相关的操作CREATE USER、DROP USER、ALTER USERGRANT、REVOKESET PASSWORDCREATE ROLE、DROP ROLERENAME USER这些语句在--skip-grant-tables模式下执行大概率都会报ERROR 1290 (HY000)。为什么MySQL要拦逻辑其实很简单权限表的全部内容都没有加载进内存此时去执行“创建用户”“修改密码”“授予权限”这类操作服务端既没有可靠的权限对象可以校验也没办法保证修改后的结果能正确落到内存权限结构中。与其让数据处于不可控状态不如直接拒绝执行。这里有个容易误解的地方拦截的是“权限管理语句”不是所有语句。SELECT、INSERT、UPDATE、CREATE TABLE这些普通SQL在skip模式下照样能跑前提是你确实有旧权限表的底层访问能力实际上因为无需鉴权所以默认都能执行。所以很多DBA会误以为“既然都能查那改密码应该也行吧”——然后就在ALTER USER上撞了墙。1.2 哪些场景最容易“中招”我总结了一下碰到这个报错的人基本逃不出下面三种情况场景典型现象易错点忘记root密码按教程重置后忘了恢复改完密码重启服务后执行GRANT仍然报1290启动脚本或配置里还残留skip-grant-tables手敲命令行临时启动mysqldmysqld_safe --skip-grant-tables 用完就关其他同事不知道有人复用这条历史命令把服务带进skip模式配置文件my.cnf里残留参数[mysqld]段有skip-grant-tables所有重启都会进入skip不仔细看配置文件只查启动命令行还有一种比较隐蔽的情况MySQL 5.7开始支持/etc/my.cnf通过!include引入其他配置文件比如!includedir /etc/mysql/conf.d/。如果conf.d目录下有个文件写了skip-grant-tables你在主配置文件里翻半天也找不到但服务每次启动依然会带这个参数。2. 先判断当前实例到底“停留在哪个模式”一把show variables探明真相遇到1290报错第一反应不是急着改配置而是先确认当前实例是不是真的处于skip模式。2.1 用一条SQL确认状态登录MySQL后执行SHOW VARIABLES LIKE skip_grant_tables;如果返回的Value是ON那没跑了实例确实以--skip-grant-tables模式运行。也可以用更简洁的写法SELECT GLOBAL.skip_grant_tables;返回1同样表示开启状态。另一个很直观的印证方式用mysql -uroot不加密码直接登录。如果不需要密码就进去了说明当前没有加载授权表任何人都能连进来。这种情况要立刻意识到风险如果你的实例绑定了对外IP、防火墙没拦那现在整个互联网都有可能连上这台数据库。2.2 确认状态后先想清楚你的目标同样是1290报错处理思路分两条路径A当前就在skip模式下而你的目标就是重置root密码。那就不要试图“退出去再说”直接在skip模式下借道完成密码修改再把服务恢复正常启动。这个路径我会在下一节完整展开。路径B实例本应该正常运行结果因为某个地方残留了skip参数导致所有权限操作都被拦截。这种情况的重点是找出参数来源、去掉它、恢复正式模式而不是去管密码。很多人在这一步犯迷糊明明自己只是重置密码结果改完密码还是报1290以为密码没改对反复重置越搞越乱。实际上只是没把残留的skip-grant-tables参数清掉。2.3 怎么找到残留参数先查进程看mysqld实际的启动参数ps -ef | grep mysqld关注输出里有没有--skip-grant-tables。如果命令行里没有那就查配置文件mysqld --verbose --help | grep -A 1 skip-grant-tables这个命令会读取默认配置文件并显示生效值。也可以直接手动检查常见路径cat /etc/my.cnf cat /etc/mysql/my.cnf cat /etc/mysql/conf.d/*.cnf重点看[mysqld]段下面有没有skip-grant-tables。有时候配置文件里写的是skip-grant-tables有时候写成skip_grant_tables两种写法MySQL都认注意别漏。3. 顺着skip模式重置root密码完整命令链路与版本差异现在聊路径A如果目标就是重置root密码而你正处在--skip-grant-tables模式下最稳妥的操作顺序是这样的。3.1 停止MySQL服务# 先停服务 systemctl stop mysqld # 或者 service mysqld stop如果进程一直停不掉先看有没有残留进程手动清理后再继续ps -ef | grep mysqld kill -9 pid3.2 以skip模式启动建议加--skip-networkingmysqld_safe --skip-grant-tables --skip-networking 这里强烈建议加上--skip-networking。它的作用是禁止MySQL监听TCP端口只允许本地通过socket连接。这样做的原因很现实skip模式下不需要密码如果还开着3306端口而你的机器又有公网IP那等于把数据库裸奔在公网上风险完全不可控。如果你需要远程操作比如数据库在云上只能从跳板机连那就不能加这个参数但一定要确认安全组、防火墙已经把所有外部来源都挡掉只在受信内网环境操作。3.3 本地免密登录mysql -uroot因为skip模式跳过了授权表校验所以不需要密码就能进来。3.4 先FLUSH PRIVILEGES再改密码这是整个流程里最关键的一步也是很多人踩坑的地方FLUSH PRIVILEGES;执行完之后MySQL会把磁盘上的授权表重新加载到内存中让后续的账号管理语句有可操作的对象。不执行这一步直接执行ALTER USER还是会报1290。然后改密码ALTER USER rootlocalhost IDENTIFIED BY YourNewStrongPassword;在MySQL 5.7和8.0里这一步是通用的。如果MySQL 5.7报这个语句有问题也可以用传统方式UPDATE mysql.user SET authentication_stringPASSWORD(YourNewStrongPassword) WHERE Userroot; FLUSH PRIVILEGES;但说实话8.0开始PASSWORD()函数已经被弃用强烈不建议再用UPDATE ... authentication_string的方式去改密码容易把认证插件搞乱。统一用ALTER USER最省心。3.5 重启MySQL恢复正常模式这一步不用再带--skip-grant-tables了。如果之前是用mysqld_safe临时启动的先把进程杀掉再正常启动# 杀掉临时启动的进程 ps -ef | grep mysqld kill -9 pid # 正常启动 systemctl start mysqld如果MySQL是注册成系统服务的在/etc/my.cnf里确认没有残留参数后直接重启即可systemctl restart mysqld3.6 验证密码是否生效mysql -uroot -p输入刚才设置的新密码能正常登录就说明重置成功。然后再执行一次SHOW VARIABLES LIKE skip_grant_tables;确认Value为OFF这一步是为了确保服务真的回到了正常授权模式而不是仍在裸奔。3.7 8.0版本的老客户端兼容问题8.0默认的认证插件是caching_sha2_password一些老版本工具比如旧版Navicat、老Python驱动连接时会报类似“Authentication plugin caching_sha2_password cannot be loaded”的错误。如果遇到这种情况可以考虑把账号的认证插件改为mysql_native_passwordALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY YourNewStrongPassword;不过mysql_native_password在MySQL 8.4里已经被标记为废弃能升级客户端还是优先升级客户端不要为了兼容老版本长期用旧插件。4. 把实例拉回正常授权模式配置排查与一次线上翻车复盘上面这节讲的是“顺着skip模式改密码”。但更多生产环境遇到的1290其实属于路径B服务本来应该正常跑结果某个环节多了一个--skip-grant-tables导致业务方加权限时被拦。4.1 排查链路我处理这类问题一般按这个顺序往下走登录实例执行SHOW VARIABLES LIKE skip_grant_tables确认状态为ON。用mysql -uroot测试是否免密登录评估当前暴露面。查看进程命令行ps -ef | grep mysqld判断参数是否由启动命令直接带入。检查配置文件/etc/my.cnf、/etc/mysql/my.cnf、/etc/mysql/conf.d/、/etc/mysql/mysql.conf.d/。找到残留参数后注释掉或删除对应行。重启MySQL再执行SHOW VARIABLES LIKE skip_grant_tables确认输出为OFF。复查mysql.user表确认没有多余的空密码账号、匿名账号。4.2 一次线上翻车的完整复盘有一次处理客户问题时业务方反馈“给新同事开通数据库账号GRANT语句一直报1290”。我登上去一看skip_grant_tables确实是ON而且免密就能登录。继续往下查发现客户这台机器几天前有人重置过root密码当时图省事直接在命令行用mysqld_safe --skip-grant-tables 启动改完密码后没有杀干净进程后来服务又被系统守护进程拉起来结果就一直以skip模式跑着。更严重的是这台MySQL绑定了公网IP防火墙策略还开着3306端口。也就是说在那一整天里任何拿到这个IP的人都可以免密连接这台数据库。我第一件事是把外网访问先掐掉然后才去处理配置文件。虽然最后没有发现数据异常但这个教训印象太深刻了。所以这里想多说一句改完密码之后确认服务恢复正常模式这件事不是可做可不做的收尾而是必需步骤。你多执行一次SHOW VARIABLES LIKE skip_grant_tables就能避免数据库在不知情的情况下裸奔。4.3 要检查的“隐藏入口”除了命令行和配置文件还有几个地方可能把skip-grant-tables带进来环境变量如果启用了systemd查看/etc/systemd/system/mysql.service里有没有EnvironmentMYSQLD_OPTS--skip-grant-tables这类配置。改完要systemctl daemon-reload再重启。多配置文件include[mysqld]段里如果有!includedir对应目录下所有.cnf文件都会被扫描残留参数很可能藏在里面。容器环境如果MySQL跑在Docker里检查docker run命令和command参数以及挂载进来的/etc/mysql/conf.d目录。5. 1290和相邻权限报错的快速区分清单做运维这些年我发现很多人分不清几个长得有点像的权限报错绕了不少弯路。这里给一个快速区分清单。报错含义优先排查方向ERROR 1290 (HY000): running with the --skip-grant-tables实例处于skip模式权限语句被禁用检查启动参数和配置文件里的skip残留ERROR 1045 (28000): Access denied for user账号密码错误或账号无权限检查账号、密码、host匹配规则ERROR 2002 (HY000): Cant connect through socket服务没起来或socket路径不对检查mysqld进程和socket配置ERROR 1130 (HY000): Host not allowed to connectTCP连接被拒账号不允许该来源IP连接检查user表host字段、bind-addressERROR 1862 (HY000): Your password has expired密码过期需要重新设置执行ALTER USER或设置过期策略容易混淆的是1290和1045。一个是“权限语句不能执行”另一个是“登录被拒绝”本质是两码事。如果你在skip模式下想改密码又一直报1290这说明你还在“权限语句被禁用”的状态不是密码错不用反复去试新密码先把FLUSH PRIVILEGES做掉再改就顺了。还有一个小细节ERROR 1290 (HY000)和ERROR 1290 (ER_UNKNOWN_ERROR)在某些驱动里显示一样但后者是通用错误码不一定和skip模式相关。判断标准还是看错误信息里有没有--skip-grant-tables这几个字有就是本文说的场景。另外一个误区值得单独拎出来看到1290后有人会去执行GRANT ALL PRIVILEGES ON *.* TO rootlocalhost WITH GRANT OPTION想着“我给root加上所有权限总行了吧”。结果一样报1290因为这条语句本身就是被拦截的权限语句。在skip模式下不要试图用GRANT来修复权限思路完全相反——要么用FLUSH PRIVILEGES加载授权表后改密码要么直接退出skip模式。6. 改完密码之后我建议你顺手做这几件事每次处理完1290我都会多花几分钟做一轮“安全确认”这几件事看着小但确实能救命第一确认skip_grant_tables已经关闭。执行SELECT GLOBAL.skip_grant_tables;结果为0才是正常状态。第二检查有没有“后门账号”。在skip模式运行期间如果服务器暴露过理论上任何人都有可能连进来创建过账号。所以重置完密码后建议仔细过一遍mysql.user表SELECT User, Host, authentication_string FROM mysql.user;重点看有没有你不知道的账号、空密码账号、Host是%的账号。发现可疑账号直接DROP USER 可疑账号来源host;第三清理shell历史。如果你是在命令行里直接敲的mysqld_safe --skip-grant-tables 这条命令很可能被记录在~/.bash_history里。虽然不是严重安全问题但如果这台机器有多个运维在维护留着容易让其他人误操作。手动清理一下比较稳妥。第四把“重置密码后的检查项”固化成一个清单。我自己整理了一份每次操作完照着走一遍[ ] 确认服务以正常模式重启skip_grant_tables为OFF[ ] 确认root新密码可以正常登录[ ] 确认没有多余的空密码账号和%来源账号[ ] 确认防火墙只开放了必要的来源IP和端口[ ] 确认配置文件里没有残留的skip-grant-tables[ ] 确认启动命令含systemd unit、docker run等没有带skip参数说实话ERROR 1290 (HY000)这个报错本身并不难解决真正可怕的是--skip-grant-tables这个状态在你不知情的情况下一直开着而数据库却照常对外提供服务。只要每次操作完多留一个心眼顺手确认一下实例的运行模式这个坑就完全能避开。
返回列表