From 935218f451cd8dd3ef6ca436c4dab462b9620d2c Mon Sep 17 00:00:00 2001 From: xfwduke Date: Tue, 24 Dec 2024 10:13:50 +0800 Subject: [PATCH] =?UTF-8?q?feat(mysql):=20=E4=B8=8B=E5=8F=91nginx=20addrs?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=20#8766?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pkg/components/mysql/install_mysql.go | 20 ++ .../components/peripheraltools/crond/init.go | 68 +++- .../core/staticembed/default_sys_schema.go | 4 + .../core/staticembed/default_sys_schema.sql | 311 ------------------ .../pkg/core/staticembed/procedure.sql | 297 +++++++++++++++++ .../flow/utils/mysql/mysql_act_playload.py | 2 + 6 files changed, 382 insertions(+), 320 deletions(-) create mode 100644 dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/procedure.sql diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_mysql.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_mysql.go index 4519d0e7cc..01871bfad1 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_mysql.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/mysql/install_mysql.go @@ -901,6 +901,26 @@ func (i *InstallMySQLComp) InitDefaultPrivAndSchemaWithResetMaster() (err error) initSQLs = append(initSQLs, staticembed.SpiderInitSQL) } + if bsql, err = staticembed.ProcedureSQL.ReadFile(staticembed.GrantProcedureSQLFileName); err != nil { + logger.Error("读取存储过程嵌入文件%s失败", staticembed.ProcedureSQL) + return err + } + logger.Info("read embed procedure sql success: %s", bsql) + + for _, value := range strings.SplitAfterN(string(bsql), `#`, -1) { + if !regexp.MustCompile(`^\\s*$`).MatchString(value) { + initSQLs = append(initSQLs, value) + } + } + // 剔除最后一个空字符,splits 会多分割出一个空字符 + if len(initSQLs) < 2 { + return fmt.Errorf("初始化sql为空%v", initSQLs) + } + + if i.Params.GetPkgTypeName() == cst.PkgTypeTdbctl { + initSQLs = append(initSQLs, staticembed.SpiderInitSQL) + } + // 调用 mysql-monitor 里的主从复制延迟检查心跳表, infodba_schema.master_slave_heartbeat initSQLs = append(initSQLs, masterslaveheartbeat.DropTableSQL, masterslaveheartbeat.CreateTableSQL) if i.Params.GetPkgTypeName() == cst.PkgTypeMysql { // 避免迁移实例时,新机器还没有这个表,会同步失败 diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/components/peripheraltools/crond/init.go b/dbm-services/mysql/db-tools/dbactuator/pkg/components/peripheraltools/crond/init.go index 430b32e1cf..84d15a07c2 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/components/peripheraltools/crond/init.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/components/peripheraltools/crond/init.go @@ -1,8 +1,14 @@ package crond import ( + "dbm-services/common/go-pubpkg/logger" + rcnf "dbm-services/common/reverse-api/config" "dbm-services/mysql/db-tools/dbactuator/pkg/components" "dbm-services/mysql/db-tools/dbactuator/pkg/tools" + "dbm-services/mysql/db-tools/dbactuator/pkg/util/osutil" + "fmt" + "os" + "path/filepath" ) type MySQLCrondComp struct { @@ -17,20 +23,64 @@ func (c *MySQLCrondComp) Init() error { if err != nil { return err } + + if c.Params.NginxAddrs == nil || len(c.Params.NginxAddrs) <= 0 { + err := fmt.Errorf("nginx addresses are required") + logger.Error(err.Error()) + return err + } + + // 初始化 nginx addrs 配置文件 + err = os.MkdirAll(rcnf.CommonConfigDir, 0777) + if err != nil { + logger.Error("mkdir failed", "err", err) + return err + } + + f, err := os.OpenFile( + filepath.Join( + rcnf.CommonConfigDir, + rcnf.NginxProxyAddrsFileName), + os.O_CREATE|os.O_TRUNC|os.O_WRONLY, + 0777, + ) + if err != nil { + logger.Error("open file failed", "err", err) + return err + } + defer func() { + _ = f.Close() + }() + + for _, addr := range c.Params.NginxAddrs { + if _, err := f.Write([]byte(addr + "\n")); err != nil { + logger.Error("write addr failed", "err", err) + return err + } + } + + chownCmd := fmt.Sprintf(`chown -R mysql %s`, rcnf.CommonConfigDir) + _, err = osutil.ExecShellCommand(false, chownCmd) + if err != nil { + logger.Error("exec command failed", "err", err) + return err + } + return nil } type MySQLCrondParam struct { components.Medium - Ip string `json:"ip"` - BkCloudId int `json:"bk_cloud_id"` - EventDataId int `json:"event_data_id"` - EventDataToken string `json:"event_data_token"` - MetricsDataId int `json:"metrics_data_id"` - MetricsDataToken string `json:"metrics_data_token"` - BeatPath string `json:"beat_path"` - AgentAddress string `json:"agent_address"` - BkBizId int `json:"bk_biz_id"` + Ip string `json:"ip"` + BkCloudId int `json:"bk_cloud_id"` + EventDataId int `json:"event_data_id"` + EventDataToken string `json:"event_data_token"` + MetricsDataId int `json:"metrics_data_id"` + MetricsDataToken string `json:"metrics_data_token"` + BeatPath string `json:"beat_path"` + AgentAddress string `json:"agent_address"` + BkBizId int `json:"bk_biz_id"` + NginxAddrs []string `json:"nginx_addrs"` } type runtimeConfig struct { diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/default_sys_schema.go b/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/default_sys_schema.go index 7c466c3749..b02d50eacc 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/default_sys_schema.go +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/default_sys_schema.go @@ -4,12 +4,16 @@ import "embed" // DefaultSysSchemaSQLFileName TODO const DefaultSysSchemaSQLFileName = "default_sys_schema.sql" +const GrantProcedureSQLFileName = "procedure.sql" // DefaultSysSchemaSQL TODO // //go:embed default_sys_schema.sql var DefaultSysSchemaSQL embed.FS +//go:embed procedure.sql +var ProcedureSQL embed.FS + // SpiderInitSQL TODO const SpiderInitSQL = `CREATE TABLE if not exists infodba_schema.tscc_schema_checksum( db char(64) NOT NULL, diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/default_sys_schema.sql b/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/default_sys_schema.sql index 7cf0d9e975..b1d8fce767 100644 --- a/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/default_sys_schema.sql +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/default_sys_schema.sql @@ -117,314 +117,3 @@ CREATE TABLE IF NOT EXISTS infodba_schema.proxy_user_list( flush privileges; flush logs; - - -SET SESSION sql_log_bin = 0; --- 授权, 检查 ERROR_MSG 来判断是否成功 --- ERROR STATE CODE --- 32401 参数验证错误 --- 32402 冲突检测错误 --- 中控不会转发授权语句, 当普通 MySQL 就好 -DROP PROCEDURE IF EXISTS infodba_schema.dba_grant; -DELIMITER // -CREATE PROCEDURE infodba_schema.dba_grant( - IN username VARCHAR(128), - IN ip_list VARCHAR(3000), -- 但是限制最大只能传入 2000 - IN db_list VARCHAR(3000), -- 但是限制最大只能传入 2000 - IN long_psw VARCHAR(128), -- 密码是密文 - IN short_psw VARCHAR(32), -- 密码是密文 - IN priv_str VARCHAR(4096), - IN global_priv_str VARCHAR(4096) -) -SQL SECURITY INVOKER -BEGIN - IF LENGTH(ip_list) >= 2000 OR LENGTH(db_list) >= 2000 THEN - SIGNAL SQLSTATE '32401' SET MESSAGE_TEXT = "input ip_list or db_list too long, max length is 2000"; - END IF; - - IF NOT(long_psw LIKE '*%' AND LENGTH(long_psw) = 41) THEN - SET @msg = CONCAT('bad password: ', long_psw); - SIGNAL SQLSTATE '32401' SET MESSAGE_TEXT = @msg; - END IF; - - -- 不同版本授权语句不兼容, 所以干脆不写 - SET SESSION sql_log_bin = 0; - - -- 初始化结果表 - CALL init_report_table(); - - -- 初始化结果标识 - SET @uuid = UUID(); - SET @grant_time = NOW(); - - SET ip_list = TRIM(BOTH ',' FROM ip_list); - SET ip_list = CONCAT(ip_list, ","); - - SET db_list = TRIM(BOTH ',' FROM db_list); - SET db_list = CONCAT(db_list, ","); - - -- 先做检查 - SET @is_check_failed = 0; - CALL check_all(@uuid, @grant_time, username, ip_list, db_list, long_psw, short_psw, @is_check_failed); - - IF @is_check_failed = 1 THEN - SIGNAL SQLSTATE '32402' SET MESSAGE_TEXT = @uuid; - END IF; - - WHILE (LOCATE(',', ip_list) > 0) - DO - SET @ip = TRIM(SUBSTRING(ip_list, 1, LOCATE(',', ip_list) - 1)); - SET ip_list = SUBSTRING(ip_list, LOCATE(',', ip_list) + 1); - -- 如果涉及新增账号, 只使用新版本密码 - CALL dba_grant_one_ip(username, @ip, db_list, long_psw, priv_str, global_priv_str); - END WHILE; - - FLUSH PRIVILEGES; -END// -DELIMITER ; - -DROP PROCEDURE IF EXISTS infodba_schema.init_report_table; -DROP TABLE IF EXISTS infodba_schema.dba_grant_result; -DELIMITER // -CREATE PROCEDURE infodba_schema.init_report_table() -SQL SECURITY INVOKER -BEGIN - -- 不同版本授权语句不兼容, 所以干脆不写 - SET SESSION sql_log_bin = 0; - - CREATE TABLE IF NOT EXISTS infodba_schema.dba_grant_result( - id VARCHAR(64), - grant_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - username VARCHAR(128), - client_ip VARCHAR(32), - dbname VARCHAR(64), - long_psw VARCHAR(128), - short_psw VARCHAR(32), - priv VARCHAR(4096), - global_priv VARCHAR(4096), - msg VARCHAR(4096) - ) ENGINE = InnoDB; - DELETE FROM dba_grant_result WHERE grant_time < DATE_SUB(NOW(), INTERVAL 1 DAY); -END// -DELIMITER ; - --- 检查入口 -DROP PROCEDURE IF EXISTS infodba_schema.check_all; -DELIMITER // -CREATE PROCEDURE infodba_schema.check_all( - IN uuid VARCHAR(64), - IN grant_time TIMESTAMP, - IN username VARCHAR(128), - IN ip_list VARCHAR(3000), - IN db_list VARCHAR(3000), - IN long_psw VARCHAR(128), - IN short_psw VARCHAR(32), - OUT is_check_failed INT -) -SQL SECURITY INVOKER -BEGIN - -- 全量检查入口 - CALL check_password(uuid, grant_time, username, ip_list, long_psw, short_psw, is_check_failed); - CALL check_db_conflict(uuid, grant_time, username, ip_list, db_list, is_check_failed); -END // -DELIMITER ; - --- 密码一致性检查 -DROP PROCEDURE IF EXISTS infodba_schema.check_password; -DELIMITER // -CREATE PROCEDURE infodba_schema.check_password( - IN uuid VARCHAR(64), - IN grant_time TIMESTAMP, - IN username VARCHAR(128), - IN ip_list VARCHAR(3000), - IN long_psw VARCHAR(128), - IN short_psw VARCHAR(32), - OUT is_check_failed INT -) -SQL SECURITY INVOKER -BEGIN - -- 不同版本授权语句不兼容, 所以干脆不写 - SET SESSION sql_log_bin = 0; - - WHILE (LOCATE(',', ip_list) > 0) - DO - SET @ip = TRIM(SUBSTRING(ip_list, 1, LOCATE(',', ip_list) - 1)); - SET ip_list = SUBSTRING(ip_list, LOCATE(',', ip_list) + 1); - - SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = username AND host = @ip) INTO @user_host_exists; - IF @user_host_exists THEN - -- 用户存在, 检查密码 - -- 5.6 及之前还有 old_password 函数, 也就是 < 5.7 可能有 old_password - IF SUBSTRING_INDEX(@@version, ".", 2) < 5.7 THEN - SELECT password = long_psw OR password = short_psw INTO @psw_match FROM mysql.user WHERE user = username AND host = @ip; - ELSE - SELECT authentication_string = long_psw INTO @psw_match FROM mysql.user WHERE user = username AND host =@ip; - END IF; - - IF NOT @psw_match THEN - SET is_check_failed = is_check_failed OR 1; - INSERT INTO dba_grant_result(id, grant_time, username, client_ip, long_psw, short_psw, msg) - VALUES (uuid, grant_time, username, @ip, long_psw, short_psw, 'password not match'); - END IF; - END IF; - END WHILE; -END // -DELIMITER ; - --- 库模式冲突检查入口 -DROP PROCEDURE IF EXISTS infodba_schema.check_db_conflict; -DELIMITER // -CREATE PROCEDURE infodba_schema.check_db_conflict( - IN uuid VARCHAR(64), - IN grant_time TIMESTAMP, - IN username VARCHAR(128), - IN ip_list VARCHAR(3000), - IN db_list VARCHAR(3000), - OUT is_check_failed INT -) -SQL SECURITY INVOKER -BEGIN - WHILE (LOCATE(',', ip_list) > 0) - DO - SET @ip = TRIM(SUBSTRING(ip_list, 1, LOCATE(',', ip_list) - 1)); - SET ip_list = SUBSTRING(ip_list, LOCATE(',', ip_list) + 1); - - SELECT EXISTS(SELECT 1 FROM mysql.db WHERE user = username AND host = @ip) INTO @db_priv_applied; - IF @db_priv_applied THEN - CALL check_db_conflict_one_ip(uuid, grant_time, username, @ip, db_list, is_check_failed); - END IF; - END WHILE; -END // -DELIMITER ; - --- 单 IP 库模式冲突检查 -DROP PROCEDURE IF EXISTS infodba_schema.check_db_conflict_one_ip; -DELIMITER // -CREATE PROCEDURE infodba_schema.check_db_conflict_one_ip( - IN uuid VARCHAR(64), - IN grant_time TIMESTAMP, - IN username VARCHAR(128), - IN ip VARCHAR(15), - IN db_list VARCHAR(3000), - OUT is_check_failed INT -) -SQL SECURITY INVOKER -BEGIN - DECLARE applied_dbname VARCHAR(64); - DECLARE cursor_done INT DEFAULT FALSE; - DECLARE db_cursor CURSOR FOR SELECT db FROM mysql.db WHERE user = username AND host = @ip; - DECLARE CONTINUE HANDLER FOR NOT FOUND SET cursor_done = TRUE; - - -- 不同版本授权语句不兼容, 所以干脆不写 - SET SESSION sql_log_bin = 0; - - OPEN db_cursor; - fetch_loop: LOOP - FETCH db_cursor INTO applied_dbname; - IF cursor_done THEN - LEAVE fetch_loop; - END IF; - - SET @loop_db_list = db_list; - - WHILE (LOCATE(',', @loop_db_list) > 0) - DO - SET @dbname = TRIM(SUBSTRING(@loop_db_list, 1, LOCATE(',', @loop_db_list) - 1)); - SET @loop_db_list = SUBSTRING(@loop_db_list, LOCATE(',', @loop_db_list) + 1); - - -- 申请库名和已有库名不等并且能模式匹配 - IF @dbname <> applied_dbname AND (@dbname LIKE applied_dbname OR applied_dbname LIKE @dbname) THEN - SET is_check_failed = is_check_failed OR 1; - INSERT INTO dba_grant_result(id, grant_time, username, client_ip, dbname, long_psw, short_psw, msg) - VALUES (uuid, grant_time, username, @ip, @dbname, long_psw, short_psw, CONCAT("conflict with applied db [", applied_dbname, "]")); - END IF; - END WHILE; - - END LOOP fetch_loop; - CLOSE db_cursor; -END // -DELIMITER ; - --- 单 IP 授权 -DROP PROCEDURE IF EXISTS infodba_schema.dba_grant_one_ip; -DELIMITER // -CREATE PROCEDURE infodba_schema.dba_grant_one_ip( - IN username VARCHAR(128), - IN ip VARCHAR(15), - IN db_list VARCHAR(3000), - IN psw VARCHAR(128), - IN priv_str VARCHAR(4096), - IN global_priv_str VARCHAR(4096) -) -SQL SECURITY INVOKER -BEGIN - -- 不同版本授权语句不兼容, 所以干脆不写 - SET SESSION sql_log_bin = 0; - - SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = username AND host = ip) INTO @user_host_exists; - IF NOT @user_host_exists THEN - -- 用户不存在, 直接创建, 只使用新版本密码, 传入的密码是密文 - IF SUBSTRING_INDEX(@@version, ".", 2) < 5.7 THEN - SET @create_user_sql = CONCAT("GRANT USAGE ON *.* TO '", username, "'@'", ip, "' IDENTIFIED BY PASSWORD '", psw, "'"); - PREPARE stmt FROM @create_user_sql; - EXECUTE stmt; - ELSE - SET @create_user_sql = CONCAT("CREATE USER IF NOT EXISTS '", username, "'@'", ip, "' IDENTIFIED WITH mysql_native_password AS '", psw, "'"); - PREPARE stmt FROM @create_user_sql; - EXECUTE stmt; - END IF; - END IF; - - WHILE (LOCATE(',', db_list) > 0) - DO - SET @db = TRIM(SUBSTRING(db_list, 1, LOCATE(',', db_list) - 1)); - SET db_list = SUBSTRING(db_list, LOCATE(',', db_list) + 1); - CALL dba_grant_one_ip_db(username, ip, @db, psw, priv_str, global_priv_str); - END WHILE; -END// -DELIMITER ; - --- 单 IP 单 DB 授权 -DROP PROCEDURE IF EXISTS infodba_schema.dba_grant_one_ip_db; -DELIMITER // -CREATE PROCEDURE infodba_schema.dba_grant_one_ip_db( - IN username VARCHAR(128), - IN ip VARCHAR(15), - IN dbname VARCHAR(64), - IN psw VARCHAR(128), - IN priv_str VARCHAR(4096), - IN global_priv_str VARCHAR(4096) -) -SQL SECURITY INVOKER -BEGIN - DECLARE exists_db VARCHAR(64); - DECLARE cursor_done INT DEFAULT FALSE; - DECLARE db_cursor CURSOR FOR SELECT db FROM mysql.db WHERE user = username AND host = ip; - DECLARE CONTINUE HANDLER FOR NOT FOUND SET cursor_done = TRUE; - - -- 不同版本授权语句不兼容, 所以干脆不写 - SET SESSION sql_log_bin = 0; - - SET priv_str = TRIM(BOTH ',' FROM TRIM(priv_str)); - SET global_priv_str = TRIM(BOTH ',' FROM TRIM(global_priv_str)); - - -- 全局权限 - IF global_priv_str IS NOT NULL AND global_priv_str <> '' THEN - SET @global_grant_sql = CONCAT("GRANT ", global_priv_str, " ON *.* TO '", username, "'@'", ip, "'"); - PREPARE stmt FROM @global_grant_sql; - EXECUTE stmt; - END IF; - - -- 非全局权限 - IF priv_str IS NOT NULL AND priv_str <> '' THEN - SET @grant_sql = ""; - if dbname = '*' OR dbname = '%' THEN - SET @grant_sql = CONCAT("GRANT ", priv_str, " ON *.* TO '", username, "'@'", ip, "'"); - ELSE - SET @grant_sql = CONCAT("GRANT ", priv_str, " ON `", dbname, "`.* TO '", username, "'@'", ip, "'"); - END IF; - PREPARE stmt FROM @grant_sql; - EXECUTE stmt; - END IF; -END// -DELIMITER ; diff --git a/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/procedure.sql b/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/procedure.sql new file mode 100644 index 0000000000..46a80138b7 --- /dev/null +++ b/dbm-services/mysql/db-tools/dbactuator/pkg/core/staticembed/procedure.sql @@ -0,0 +1,297 @@ +SET SESSION sql_log_bin = 0# +-- 授权, 检查 ERROR_MSG 来判断是否成功 +-- ERROR STATE CODE +-- 32401 参数验证错误 +-- 32402 冲突检测错误 +-- 中控不会转发授权语句, 当普通 MySQL 就好 +DROP PROCEDURE IF EXISTS infodba_schema.dba_grant# +CREATE PROCEDURE infodba_schema.dba_grant( + IN username VARCHAR(128), + IN ip_list VARCHAR(3000), -- 但是限制最大只能传入 2000 + IN db_list VARCHAR(3000), -- 但是限制最大只能传入 2000 + IN long_psw VARCHAR(128), -- 密码是密文 + IN short_psw VARCHAR(32), -- 密码是密文 + IN priv_str VARCHAR(4096), + IN global_priv_str VARCHAR(4096) +) +SQL SECURITY INVOKER +BEGIN + IF LENGTH(ip_list) >= 2000 OR LENGTH(db_list) >= 2000 THEN + SIGNAL SQLSTATE '32401' SET MESSAGE_TEXT = "input ip_list or db_list too long, max length is 2000"; + END IF; + + IF NOT(long_psw LIKE '*%' AND LENGTH(long_psw) = 41) THEN + SET @msg = CONCAT('bad password: ', long_psw); + SIGNAL SQLSTATE '32401' SET MESSAGE_TEXT = @msg; + END IF; + + -- 不同版本授权语句不兼容, 所以干脆不写 + SET SESSION sql_log_bin = 0; + + -- 初始化结果表 + CALL init_report_table(); + + -- 初始化结果标识 + SET @uuid = UUID(); + SET @grant_time = NOW(); + + SET ip_list = TRIM(BOTH ',' FROM ip_list); + SET ip_list = CONCAT(ip_list, ","); + + SET db_list = TRIM(BOTH ',' FROM db_list); + SET db_list = CONCAT(db_list, ","); + + -- 先做检查 + SET @is_check_failed = 0; + CALL check_all(@uuid, @grant_time, username, ip_list, db_list, long_psw, short_psw, @is_check_failed); + + IF @is_check_failed = 1 THEN + SIGNAL SQLSTATE '32402' SET MESSAGE_TEXT = @uuid; + END IF; + + WHILE (LOCATE(',', ip_list) > 0) + DO + SET @ip = TRIM(SUBSTRING(ip_list, 1, LOCATE(',', ip_list) - 1)); + SET ip_list = SUBSTRING(ip_list, LOCATE(',', ip_list) + 1); + -- 如果涉及新增账号, 只使用新版本密码 + CALL dba_grant_one_ip(username, @ip, db_list, long_psw, priv_str, global_priv_str); + END WHILE; + + FLUSH PRIVILEGES; +END# + +DROP PROCEDURE IF EXISTS infodba_schema.init_report_table# +DROP TABLE IF EXISTS infodba_schema.dba_grant_result# + +CREATE PROCEDURE infodba_schema.init_report_table() +SQL SECURITY INVOKER +BEGIN + -- 不同版本授权语句不兼容, 所以干脆不写 + SET SESSION sql_log_bin = 0; + + CREATE TABLE IF NOT EXISTS infodba_schema.dba_grant_result( + id VARCHAR(64), + grant_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + username VARCHAR(128), + client_ip VARCHAR(32), + dbname VARCHAR(64), + long_psw VARCHAR(128), + short_psw VARCHAR(32), + priv VARCHAR(4096), + global_priv VARCHAR(4096), + msg VARCHAR(4096) + ) ENGINE = InnoDB; + DELETE FROM dba_grant_result WHERE grant_time < DATE_SUB(NOW(), INTERVAL 1 DAY); +END# + + +-- 检查入口 +DROP PROCEDURE IF EXISTS infodba_schema.check_all# +CREATE PROCEDURE infodba_schema.check_all( + IN uuid VARCHAR(64), + IN grant_time TIMESTAMP, + IN username VARCHAR(128), + IN ip_list VARCHAR(3000), + IN db_list VARCHAR(3000), + IN long_psw VARCHAR(128), + IN short_psw VARCHAR(32), + OUT is_check_failed INT +) +SQL SECURITY INVOKER +BEGIN + -- 全量检查入口 + CALL check_password(uuid, grant_time, username, ip_list, long_psw, short_psw, is_check_failed); + CALL check_db_conflict(uuid, grant_time, username, ip_list, db_list, is_check_failed); +END # + + +-- 密码一致性检查 +DROP PROCEDURE IF EXISTS infodba_schema.check_password# +CREATE PROCEDURE infodba_schema.check_password( + IN uuid VARCHAR(64), + IN grant_time TIMESTAMP, + IN username VARCHAR(128), + IN ip_list VARCHAR(3000), + IN long_psw VARCHAR(128), + IN short_psw VARCHAR(32), + OUT is_check_failed INT +) +SQL SECURITY INVOKER +BEGIN + -- 不同版本授权语句不兼容, 所以干脆不写 + SET SESSION sql_log_bin = 0; + + WHILE (LOCATE(',', ip_list) > 0) + DO + SET @ip = TRIM(SUBSTRING(ip_list, 1, LOCATE(',', ip_list) - 1)); + SET ip_list = SUBSTRING(ip_list, LOCATE(',', ip_list) + 1); + + SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = username AND host = @ip) INTO @user_host_exists; + IF @user_host_exists THEN + -- 用户存在, 检查密码 + -- 5.6 及之前还有 old_password 函数, 也就是 < 5.7 可能有 old_password + IF SUBSTRING_INDEX(@@version, ".", 2) < 5.7 THEN + SELECT password = long_psw OR password = short_psw INTO @psw_match FROM mysql.user WHERE user = username AND host = @ip; + ELSE + SELECT authentication_string = long_psw INTO @psw_match FROM mysql.user WHERE user = username AND host =@ip; + END IF; + + IF NOT @psw_match THEN + SET is_check_failed = is_check_failed OR 1; + INSERT INTO dba_grant_result(id, grant_time, username, client_ip, long_psw, short_psw, msg) + VALUES (uuid, grant_time, username, @ip, long_psw, short_psw, 'password not match'); + END IF; + END IF; + END WHILE; +END # + + +-- 库模式冲突检查入口 +DROP PROCEDURE IF EXISTS infodba_schema.check_db_conflict# +CREATE PROCEDURE infodba_schema.check_db_conflict( + IN uuid VARCHAR(64), + IN grant_time TIMESTAMP, + IN username VARCHAR(128), + IN ip_list VARCHAR(3000), + IN db_list VARCHAR(3000), + OUT is_check_failed INT +) +SQL SECURITY INVOKER +BEGIN + WHILE (LOCATE(',', ip_list) > 0) + DO + SET @ip = TRIM(SUBSTRING(ip_list, 1, LOCATE(',', ip_list) - 1)); + SET ip_list = SUBSTRING(ip_list, LOCATE(',', ip_list) + 1); + + SELECT EXISTS(SELECT 1 FROM mysql.db WHERE user = username AND host = @ip) INTO @db_priv_applied; + IF @db_priv_applied THEN + CALL check_db_conflict_one_ip(uuid, grant_time, username, @ip, db_list, is_check_failed); + END IF; + END WHILE; +END # + +-- 单 IP 库模式冲突检查 +DROP PROCEDURE IF EXISTS infodba_schema.check_db_conflict_one_ip# +CREATE PROCEDURE infodba_schema.check_db_conflict_one_ip( + IN uuid VARCHAR(64), + IN grant_time TIMESTAMP, + IN username VARCHAR(128), + IN ip VARCHAR(15), + IN db_list VARCHAR(3000), + OUT is_check_failed INT +) +SQL SECURITY INVOKER +BEGIN + DECLARE applied_dbname VARCHAR(64); + DECLARE cursor_done INT DEFAULT FALSE; + DECLARE db_cursor CURSOR FOR SELECT db FROM mysql.db WHERE user = username AND host = @ip; + DECLARE CONTINUE HANDLER FOR NOT FOUND SET cursor_done = TRUE; + + -- 不同版本授权语句不兼容, 所以干脆不写 + SET SESSION sql_log_bin = 0; + + OPEN db_cursor; + fetch_loop: LOOP + FETCH db_cursor INTO applied_dbname; + IF cursor_done THEN + LEAVE fetch_loop; + END IF; + + SET @loop_db_list = db_list; + + WHILE (LOCATE(',', @loop_db_list) > 0) + DO + SET @dbname = TRIM(SUBSTRING(@loop_db_list, 1, LOCATE(',', @loop_db_list) - 1)); + SET @loop_db_list = SUBSTRING(@loop_db_list, LOCATE(',', @loop_db_list) + 1); + + -- 申请库名和已有库名不等并且能模式匹配 + IF @dbname <> applied_dbname AND (@dbname LIKE applied_dbname OR applied_dbname LIKE @dbname) THEN + SET is_check_failed = is_check_failed OR 1; + INSERT INTO dba_grant_result(id, grant_time, username, client_ip, dbname, long_psw, short_psw, msg) + VALUES (uuid, grant_time, username, @ip, @dbname, long_psw, short_psw, CONCAT("conflict with applied db [", applied_dbname, "]")); + END IF; + END WHILE; + + END LOOP fetch_loop; + CLOSE db_cursor; +END # + +-- 单 IP 授权 +DROP PROCEDURE IF EXISTS infodba_schema.dba_grant_one_ip# +CREATE PROCEDURE infodba_schema.dba_grant_one_ip( + IN username VARCHAR(128), + IN ip VARCHAR(15), + IN db_list VARCHAR(3000), + IN psw VARCHAR(128), + IN priv_str VARCHAR(4096), + IN global_priv_str VARCHAR(4096) +) +SQL SECURITY INVOKER +BEGIN + -- 不同版本授权语句不兼容, 所以干脆不写 + SET SESSION sql_log_bin = 0; + + SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = username AND host = ip) INTO @user_host_exists; + IF NOT @user_host_exists THEN + -- 用户不存在, 直接创建, 只使用新版本密码, 传入的密码是密文 + IF SUBSTRING_INDEX(@@version, ".", 2) < 5.7 THEN + SET @create_user_sql = CONCAT("GRANT USAGE ON *.* TO '", username, "'@'", ip, "' IDENTIFIED BY PASSWORD '", psw, "'"); + PREPARE stmt FROM @create_user_sql; + EXECUTE stmt; + ELSE + SET @create_user_sql = CONCAT("CREATE USER IF NOT EXISTS '", username, "'@'", ip, "' IDENTIFIED WITH mysql_native_password AS '", psw, "'"); + PREPARE stmt FROM @create_user_sql; + EXECUTE stmt; + END IF; + END IF; + + WHILE (LOCATE(',', db_list) > 0) + DO + SET @db = TRIM(SUBSTRING(db_list, 1, LOCATE(',', db_list) - 1)); + SET db_list = SUBSTRING(db_list, LOCATE(',', db_list) + 1); + CALL dba_grant_one_ip_db(username, ip, @db, psw, priv_str, global_priv_str); + END WHILE; +END# + +-- 单 IP 单 DB 授权 +DROP PROCEDURE IF EXISTS infodba_schema.dba_grant_one_ip_db# +CREATE PROCEDURE infodba_schema.dba_grant_one_ip_db( + IN username VARCHAR(128), + IN ip VARCHAR(15), + IN dbname VARCHAR(64), + IN psw VARCHAR(128), + IN priv_str VARCHAR(4096), + IN global_priv_str VARCHAR(4096) +) +SQL SECURITY INVOKER +BEGIN + DECLARE exists_db VARCHAR(64); + DECLARE cursor_done INT DEFAULT FALSE; + DECLARE db_cursor CURSOR FOR SELECT db FROM mysql.db WHERE user = username AND host = ip; + DECLARE CONTINUE HANDLER FOR NOT FOUND SET cursor_done = TRUE; + + -- 不同版本授权语句不兼容, 所以干脆不写 + SET SESSION sql_log_bin = 0; + + SET priv_str = TRIM(BOTH ',' FROM TRIM(priv_str)); + SET global_priv_str = TRIM(BOTH ',' FROM TRIM(global_priv_str)); + + -- 全局权限 + IF global_priv_str IS NOT NULL AND global_priv_str <> '' THEN + SET @global_grant_sql = CONCAT("GRANT ", global_priv_str, " ON *.* TO '", username, "'@'", ip, "'"); + PREPARE stmt FROM @global_grant_sql; + EXECUTE stmt; + END IF; + + -- 非全局权限 + IF priv_str IS NOT NULL AND priv_str <> '' THEN + SET @grant_sql = ""; + if dbname = '*' OR dbname = '%' THEN + SET @grant_sql = CONCAT("GRANT ", priv_str, " ON *.* TO '", username, "'@'", ip, "'"); + ELSE + SET @grant_sql = CONCAT("GRANT ", priv_str, " ON `", dbname, "`.* TO '", username, "'@'", ip, "'"); + END IF; + PREPARE stmt FROM @grant_sql; + EXECUTE stmt; + END IF; +END# diff --git a/dbm-ui/backend/flow/utils/mysql/mysql_act_playload.py b/dbm-ui/backend/flow/utils/mysql/mysql_act_playload.py index 2a857ba246..2befdc2c27 100644 --- a/dbm-ui/backend/flow/utils/mysql/mysql_act_playload.py +++ b/dbm-ui/backend/flow/utils/mysql/mysql_act_playload.py @@ -29,6 +29,7 @@ from backend.db_meta.exceptions import DBMetaException from backend.db_meta.models import Cluster, Machine, ProxyInstance, StorageInstance, StorageInstanceTuple from backend.db_package.models import Package +from backend.db_proxy.reverse_api.common.impl import list_nginx_addrs from backend.db_services.mysql.sql_import.constants import BKREPO_DBCONSOLE_DUMPFILE_PATH, BKREPO_SQLFILE_PATH from backend.flow.consts import ( CHECKSUM_DB, @@ -1582,6 +1583,7 @@ def get_deploy_mysql_crond_payload(self, **kwargs) -> dict: "beat_path": env.MYSQL_CROND_BEAT_PATH, "agent_address": env.MYSQL_CROND_AGENT_ADDRESS, "bk_biz_id": int(self.ticket_data["bk_biz_id"]), + "nginx_addrs": list_nginx_addrs(bk_cloud_id=self.bk_cloud_id), }, }, }