mysql最小权限部署账号应禁用root和all privileges,仅授予ci/cd所需对象级权限(如flyway_schema_history表的select/insert/update、业务表指定dml、information_schema.select等),限定ip访问,强制使用caching_sha2_password插件并外部注入密码,且须通过完整部署流程验证权限。

MySQL 创建最小权限部署账号要避开 root 和 ALL PRIVILEGES
直接用 root 或 GRANT ALL PRIVILEGES 给 CI/CD 账号,等于把数据库钥匙塞进流水线日志里——只要一次配置泄露、一次构建机被入侵,整个库就裸奔。真实场景里,CI/CD 账号只需要「执行预设 SQL」和「查版本/状态」这两类动作,其他一律禁掉。
- 只允许连接本地(
localhost)或指定跳板 IP,禁止'deploy'@'%' - 不给
CREATE USER、GRANT OPTION、DROP DATABASE权限——自动化脚本不该有删库能力 - 如果用 Flyway/Liquibase,只需
SELECT、INSERT、UPDATE、CREATE、ALTER在目标库内,且仅限表/视图/存储过程等对象级权限 - 用
SHOW DATABASES和SELECT VERSION()做前置检查时,需显式授予SELECT权限到mysql库的db、user表(但别给password字段)
GRANT 语句必须按对象粒度写,不能靠 database.* 模糊授权
写成 GRANT SELECT, INSERT ON myapp.* TO 'ci_deploy'@'10.20.30.40' 看似省事,但一旦后续在 myapp 下建了新表,权限自动生效——这违反最小权限原则。CI 流水线该操作哪些表,得白纸黑字列清楚。
- 对 Flyway 的
flyway_schema_history表:单独授SELECT, INSERT, UPDATE - 对业务表(如
users,orders):只授迁移脚本实际涉及的 DML,不加DELETE(除非明确需要回滚数据) - 存储过程部署需
EXECUTE权限,但仅限具体过程名,如GRANT EXECUTE ON PROCEDURE myapp.calc_revenue TO 'ci_deploy'@'10.20.30.40' - 避免用
FLUSH PRIVILEGES——GRANT后权限立即生效,手动刷反而可能掩盖语法错误
密码管理别硬编码,用 MySQL 8.0+ 的 caching_sha2_password 插件 + 外部凭据注入
CI 配置里写明文密码是高危操作,而 MySQL 5.7 默认的 mysql_native_password 插件在某些新版客户端(如 Node.js 8.0+ 的 mysql2)里会握手失败。必须统一用 caching_sha2_password 并配合外部凭据注入。
- 创建账号时强制指定插件:
CREATE USER 'ci_deploy'@'10.20.30.40' IDENTIFIED WITH caching_sha2_password BY 'xxx' - CI 环境中通过环境变量传密码(如
MYSQL_PASSWORD),再由启动脚本注入连接字符串,绝不落盘 - 若用 GitHub Actions,用
secrets注入;Jenkins 则用 Credentials Binding 插件,避免出现在env日志里 - 测试连接时用
mysql -u ci_deploy -h db-host -P 3306 --connect-expired-password验证过期策略是否干扰自动化
权限验证要用真实部署流程跑,不是只连上就完事
很多人 mysql -u ci_deploy -p 成功就连喊 OK,结果上线时 Flyway 报 Access denied for INSERT into flyway_schema_history。权限是否生效,得走一遍完整部署链路:从拉代码、解析 SQL、连库、校验 checksum、执行变更,每步都看报错。
- 在 CI 机器上用部署账号手动执行一遍迁移命令(如
flyway migrate -user=ci_deploy -password=xxx),观察是否卡在权限检查阶段 - 开启 MySQL 的 general log(
SET GLOBAL general_log = 'ON')抓取实际执行的语句,确认没触发隐式元数据查询(如SHOW CREATE TABLE)导致权限不足 - 注意时区和 SQL mode 差异:CI 容器默认
sql_mode可能比生产库严格,STRICT_TRANS_TABLES开启时字段超长会直接报错,看起来像权限问题
最常漏的是对 information_schema 的 SELECT 权限——很多 ORM 和迁移工具会查这个库判断表结构,但它不属于任何用户数据库,得单独授:GRANT SELECT ON information_schema.* TO 'ci_deploy'@'10.20.30.40'。这点容易被忽略,因为本地连得通,一上 CI 就静默失败。











