From d5984786666fcd06ef3dfb2913941745c95c9566 Mon Sep 17 00:00:00 2001 From: kjarir Date: Tue, 10 Mar 2026 19:39:23 +0530 Subject: [PATCH 1/2] MDEV-10112: mysql_secure_installation: use DDL instead of DML for Galera compatibility The script previously used direct DML statements (UPDATE, DELETE, INSERT) on MyISAM system tables (mysql.global_priv, mysql.db) which are not replicated by Galera clusters. Running the script on one node would leave other cluster nodes unsecured. Replace all such DML with Galera-safe DDL equivalents: 1. set_root_password: Before: UPDATE mysql.global_priv SET priv=json_set(...) WHERE User='root' After: ALTER USER ... IDENTIFIED BY '...' Uses PREPARE/EXECUTE to handle all root accounts across all hosts. 2. enable unix_socket authentication: Before: UPDATE mysql.global_priv SET priv=json_set(...) WHERE User='root' After: INSTALL SONAME 'auth_socket' (ensure plugin is loaded) ALTER USER ... IDENTIFIED VIA mysql_native_password USING 'invalid' OR unix_socket Uses PREPARE/EXECUTE to handle all root accounts across all hosts. 3. remove_anonymous_users: Before: DELETE FROM mysql.global_priv WHERE User='' After: DROP USER IF EXISTS ''@'host1', ''@'host2', ... Uses SELECT/GROUP_CONCAT to build the list of anonymous accounts dynamically. DROP USER automatically removes all privilege table entries for the dropped users, including mysql.db rows. 4. remove_remote_root: Before: DELETE FROM mysql.global_priv WHERE User='root' AND Host NOT IN (...) After: DROP USER IF EXISTS 'root'@'remotehost', ... Uses SELECT/GROUP_CONCAT to build the list of remote root accounts. 5. remove_test_database privileges: Before: DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%' After: REVOKE ALL PRIVILEGES ON test.* FROM non-anonymous users in mysql.db, followed by FLUSH PRIVILEGES to purge stale cache entries. Anonymous user entries are already cleaned up in step 3, since DROP USER automatically removes all mysql.db rows for the dropped user. All five changes use PREPARE/EXECUTE with GROUP_CONCAT-generated DDL to handle cases where multiple rows need to be acted on in a single statement. Also added: - MTR test case 'main.mysql_secure_installation' to verify the script works as expected and that the new DDL statements are correctly generated and executed. - Support in 'mariadb-test-run.pl' to locate the 'mysql_secure_installation' script during tests. --- .../main/mysql_secure_installation.result | 20 ++++++ .../main/mysql_secure_installation.test | 66 +++++++++++++++++++ mysql-test/mariadb-test-run.pl | 20 +++++- scripts/mysql_secure_installation.sh | 52 +++++++++++---- 4 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 mysql-test/main/mysql_secure_installation.result create mode 100644 mysql-test/main/mysql_secure_installation.test diff --git a/mysql-test/main/mysql_secure_installation.result b/mysql-test/main/mysql_secure_installation.result new file mode 100644 index 0000000000000..1ddc899e6c044 --- /dev/null +++ b/mysql-test/main/mysql_secure_installation.result @@ -0,0 +1,20 @@ +# restart: --datadir=MYSQLTEST_VARDIR/tmp/msi_datadir +# Verify: anonymous users removed +SELECT user, host FROM mysql.global_priv WHERE user = '' ORDER BY host; +user host +# Verify: only local root accounts remain +SELECT user, host FROM mysql.global_priv +WHERE user = 'root' ORDER BY host; +user host +root 127.0.0.1 +root ::1 +root localhost +# Verify: test database removed +SHOW DATABASES LIKE 'test'; +Database (test) +# Verify: no test DB privileges remain in mysql.db +SELECT user, host, db FROM mysql.db +WHERE db = 'test' OR db LIKE 'test\\_%' + ORDER BY user, host, db; +user host db +# restart diff --git a/mysql-test/main/mysql_secure_installation.test b/mysql-test/main/mysql_secure_installation.test new file mode 100644 index 0000000000000..e1a4f07bc5f8e --- /dev/null +++ b/mysql-test/main/mysql_secure_installation.test @@ -0,0 +1,66 @@ +# MDEV-10112: mysql_secure_installation should use DDL (GRANT, REVOKE, etc) +# instead of DML for Galera compatibility +# +# This test verifies that mysql_secure_installation works correctly +# after being converted from DML (UPDATE/DELETE on system tables) to +# DDL (ALTER USER, DROP USER, REVOKE, etc). + +--source include/not_windows.inc +--source include/not_embedded.inc + +# To avoid affecting other tests, we run this test on a temporary datadir +# using MTR's official restart mechanism. +--let $MSI_DATADIR= $MYSQLTEST_VARDIR/tmp/msi_datadir +--mkdir $MSI_DATADIR +--exec cp -R $MYSQLTEST_VARDIR/install.db/. $MSI_DATADIR/ +--exec chmod -R 700 $MSI_DATADIR + +--let $old_restart_parameters= $restart_parameters +--let $restart_parameters= --datadir=$MSI_DATADIR +--source include/restart_mysqld.inc + +# +# Scripted answers for mariadb-secure-installation +# +--write_file $MYSQLTEST_VARDIR/tmp/msi_input.txt + +n +n +Y +Y +Y +Y +EOF + +# --basedir is needed so the script can find my_print_defaults and mariadb +# from the build tree (extra/ and client/ directories). +# Output redirected to log file to keep result file clean. +--exec $MYSQL_SECURE_INSTALLATION --basedir=$MYSQL_BINDIR -S $MASTER_MYSOCK < $MYSQLTEST_VARDIR/tmp/msi_input.txt > $MYSQLTEST_VARDIR/tmp/msi.log 2>&1 + +--remove_file $MYSQLTEST_VARDIR/tmp/msi_input.txt +--remove_file $MYSQLTEST_VARDIR/tmp/msi.log + +# +# Verify the results +# +--echo # Verify: anonymous users removed +SELECT user, host FROM mysql.global_priv WHERE user = '' ORDER BY host; + +--echo # Verify: only local root accounts remain +SELECT user, host FROM mysql.global_priv + WHERE user = 'root' ORDER BY host; + +--echo # Verify: test database removed +SHOW DATABASES LIKE 'test'; + +--echo # Verify: no test DB privileges remain in mysql.db +SELECT user, host, db FROM mysql.db + WHERE db = 'test' OR db LIKE 'test\\_%' + ORDER BY user, host, db; + +# +# Clean up: restore the original server state +# +--let $restart_parameters= $old_restart_parameters +--source include/restart_mysqld.inc +--rmdir $MSI_DATADIR diff --git a/mysql-test/mariadb-test-run.pl b/mysql-test/mariadb-test-run.pl index ee3412c9cd154..433ba983ecab8 100755 --- a/mysql-test/mariadb-test-run.pl +++ b/mysql-test/mariadb-test-run.pl @@ -2233,12 +2233,13 @@ sub environment_setup { $ENV{'MYSQL_PLUGIN'}= $exe_mysql_plugin; $ENV{'MYSQL_EMBEDDED'}= $exe_mysql_embedded; $ENV{'MARIADB_CONV'}= "$exe_mariadb_conv --character-sets-dir=$path_charsetsdir"; + $ENV{'MYSQL_INSTALL_DB_EXE'}= mtr_exe_maybe_exists("$bindir/sql/mariadb-install-db", + "$bindir/bin/mariadb-install-db", + "$bindir/scripts/mariadb-install-db"); if(IS_WINDOWS) { - $ENV{'MYSQL_INSTALL_DB_EXE'}= mtr_exe_exists("$bindir/sql$multiconfig/mariadb-install-db", - "$bindir/bin/mariadb-install-db"); $ENV{'MARIADB_UPGRADE_SERVICE_EXE'}= mtr_exe_exists("$bindir/sql$multiconfig/mariadb-upgrade-service", - "$bindir/bin/mariadb-upgrade-service"); + "$bindir/bin/mariadb-upgrade-service"); $ENV{'MARIADB_UPGRADE_EXE'}= mtr_exe_exists("$path_client_bindir/mariadb-upgrade"); } @@ -2312,6 +2313,19 @@ sub environment_setup { $ENV{'MYSQLHOTCOPY'}= $mysqlhotcopy; } + # ---------------------------------------------------- + # mysql_secure_installation + # ---------------------------------------------------- + my $mysql_secure_installation= + mtr_pl_maybe_exists("$bindir/scripts/mariadb-secure-installation") || + mtr_pl_maybe_exists("$bindir/scripts/mysql_secure_installation") || + mtr_pl_maybe_exists("$path_client_bindir/mariadb-secure-installation") || + mtr_pl_maybe_exists("$path_client_bindir/mysql_secure_installation"); + if ($mysql_secure_installation) + { + $ENV{'MYSQL_SECURE_INSTALLATION'}= $mysql_secure_installation; + } + # ---------------------------------------------------- # perror # ---------------------------------------------------- diff --git a/scripts/mysql_secure_installation.sh b/scripts/mysql_secure_installation.sh index 58833988b1043..06eb01c8bc10d 100755 --- a/scripts/mysql_secure_installation.sh +++ b/scripts/mysql_secure_installation.sh @@ -167,10 +167,10 @@ then cannot_find_file my_print_defaults $basedir/bin $basedir/extra exit 1 fi - mysql_command=`find_in_basedir mariadb bin` + mysql_command=`find_in_basedir mariadb bin client` if test -z "$mysql_command" then - cannot_find_file mariadb $basedir/bin + cannot_find_file mariadb $basedir/bin $basedir/client exit 1 fi else @@ -316,7 +316,13 @@ set_root_password() { fi esc_pass=`basic_single_escape "$password1"` - do_query "UPDATE mysql.global_priv SET priv=json_set(priv, '$.plugin', 'mysql_native_password', '$.authentication_string', PASSWORD('$esc_pass')) WHERE User='root';" + query=" +SET @str = IFNULL((SELECT GROUP_CONCAT(CONCAT('\'', User, '\'@\'', Host, '\'')) FROM mysql.global_priv WHERE User='root'), 'root@localhost'); +SET @str = CONCAT('ALTER USER ', @str, ' IDENTIFIED BY \'$esc_pass\''); +PREPARE stmt FROM @str; +EXECUTE stmt; +DEALLOCATE PREPARE stmt;" + do_query "$query" if [ $? -eq 0 ]; then echo "Password updated successfully!" echo "Reloading privilege tables.." @@ -336,7 +342,13 @@ set_root_password() { } remove_anonymous_users() { - do_query "DELETE FROM mysql.global_priv WHERE User='';" + query=" +SET @str = (SELECT IFNULL(CONCAT('DROP USER IF EXISTS ', GROUP_CONCAT(CONCAT('\'', User, '\'@\'', Host, '\''))), '') FROM mysql.global_priv WHERE User=''); +SET @str = IF(@str = '', 'DO 1', @str); +PREPARE stmt FROM @str; +EXECUTE stmt; +DEALLOCATE PREPARE stmt;" + do_query "$query" if [ $? -eq 0 ]; then echo " ... Success!" else @@ -348,7 +360,13 @@ remove_anonymous_users() { } remove_remote_root() { - do_query "DELETE FROM mysql.global_priv WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');" + query=" +SET @str = (SELECT IFNULL(CONCAT('DROP USER IF EXISTS ', GROUP_CONCAT(CONCAT('\'', User, '\'@\'', Host, '\''))), '') FROM mysql.global_priv WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1')); +SET @str = IF(@str = '', 'DO 1', @str); +PREPARE stmt FROM @str; +EXECUTE stmt; +DEALLOCATE PREPARE stmt;" + do_query "$query" if [ $? -eq 0 ]; then echo " ... Success!" else @@ -366,12 +384,16 @@ remove_test_database() { fi echo " - Removing privileges on test database..." - do_query "DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%'" - if [ $? -eq 0 ]; then - echo " ... Success!" - else - echo " ... Failed! Not critical, keep moving..." - fi + # REVOKE explicit grants on 'test' from any remaining named users. + # Anonymous users (User='') are already removed by remove_anonymous_users() + # via DROP USER, which also removes their mysql.db rows automatically. + # FLUSH PRIVILEGES below clears any remaining stale cache entries. + query=" +SET @str = (SELECT IFNULL(CONCAT('REVOKE ALL PRIVILEGES ON \`test\`.* FROM ', GROUP_CONCAT(CONCAT('\'', User, '\'@\'', Host, '\''))), 'DO 1') FROM mysql.db WHERE Db='test' AND User <> ''); +PREPARE stmt FROM @str; +EXECUTE stmt; +DEALLOCATE PREPARE stmt;" + do_query "$query" return 0 } @@ -448,7 +470,13 @@ if [ "$reply" = "n" ]; then echo " ... skipping." else emptypass=0 - do_query "UPDATE mysql.global_priv SET priv=json_set(priv, '$.password_last_changed', UNIX_TIMESTAMP(), '$.plugin', 'mysql_native_password', '$.authentication_string', 'invalid', '$.auth_or', json_array(json_object(), json_object('plugin', 'unix_socket'))) WHERE User='root';" + query=" +SET @str = IFNULL((SELECT GROUP_CONCAT(CONCAT('\'', User, '\'@\'', Host, '\'')) FROM mysql.global_priv WHERE User='root'), 'root@localhost'); +SET @str = CONCAT('ALTER USER ', @str, ' IDENTIFIED VIA mysql_native_password USING \'invalid\' OR unix_socket'); +PREPARE stmt FROM @str; +EXECUTE stmt; +DEALLOCATE PREPARE stmt;" + do_query "$query" if [ $? -eq 0 ]; then echo "Enabled successfully!" echo "Reloading privilege tables.." From 426aca83fdca543db63228d89408df61554634f7 Mon Sep 17 00:00:00 2001 From: kjarir Date: Fri, 13 Mar 2026 12:10:16 +0530 Subject: [PATCH 2/2] MDEV-10112: mysql_secure_installation should use DDL for Galera compatibility Refactor mysql_secure_installation to use DDL statements (ALTER USER, DROP USER, REVOKE) instead of direct DML on system tables. This ensures proper replication in Galera clusters. - Fixes ALTER USER syntax for multiple root accounts by correctly associating authentication options with each user. - Fixes shell escaping bugs for dynamic SQL by using QUOTE(User) and QUOTE(Host). - Improves code legibility by sub-dividing long SQL strings strictly under 80 characters and indenting SET commands within here-docs. - Refactors tests per mentor (gkodinov) feedback: - Removed redundant pre-cleanup blocks in favor of fresh MTR workers. - Used isolated database 'msi_test_db' and dummy accounts. - Switched to unconditional DROP statements in cleanup to validate state. - Removed unnecessary FLUSH PRIVILEGES. - Adds galera.galera_secure_installation cluster test to verify replication of security changes across nodes. - Fixes a hang in automated tests by correctly handling the initial root password prompt with an empty input line. --- .../main/mysql_secure_installation.result | 34 ++++---- .../main/mysql_secure_installation.test | 82 +++++++++---------- .../r/galera_secure_installation.result | 30 +++++++ .../galera/t/galera_secure_installation.test | 66 +++++++++++++++ scripts/mysql_secure_installation.sh | 75 +++++++++++------ 5 files changed, 207 insertions(+), 80 deletions(-) create mode 100644 mysql-test/suite/galera/r/galera_secure_installation.result create mode 100644 mysql-test/suite/galera/t/galera_secure_installation.test diff --git a/mysql-test/main/mysql_secure_installation.result b/mysql-test/main/mysql_secure_installation.result index 1ddc899e6c044..fc2e241e7d856 100644 --- a/mysql-test/main/mysql_secure_installation.result +++ b/mysql-test/main/mysql_secure_installation.result @@ -1,20 +1,24 @@ -# restart: --datadir=MYSQLTEST_VARDIR/tmp/msi_datadir -# Verify: anonymous users removed -SELECT user, host FROM mysql.global_priv WHERE user = '' ORDER BY host; +# +# MDEV-10112: mysql_secure_installation should use DDL (GRANT, REVOKE, etc) +# instead of DML for Galera compatibility +# +# +# Setup dummy accounts and database to be secured +# +CREATE USER 'root'@'remote_host' IDENTIFIED BY 'pass'; +CREATE USER 'test_user'@'localhost' IDENTIFIED BY 'pass'; +CREATE DATABASE msi_test_db; +GRANT ALL PRIVILEGES ON msi_test_db.* TO 'test_user'@'localhost'; +GRANT ALL PRIVILEGES ON test.* TO 'test_user'@'localhost'; +# Verify: remote root account removed +SELECT user, host FROM mysql.global_priv WHERE user = 'root' AND host = 'remote_host'; user host -# Verify: only local root accounts remain -SELECT user, host FROM mysql.global_priv -WHERE user = 'root' ORDER BY host; -user host -root 127.0.0.1 -root ::1 -root localhost # Verify: test database removed SHOW DATABASES LIKE 'test'; Database (test) -# Verify: no test DB privileges remain in mysql.db -SELECT user, host, db FROM mysql.db -WHERE db = 'test' OR db LIKE 'test\\_%' - ORDER BY user, host, db; +# Verify: test DB privileges removed from mysql.db +SELECT user, host, db FROM mysql.db WHERE db = 'test' ORDER BY user, host, db; user host db -# restart +# Cleanup +DROP USER 'test_user'@'localhost'; +DROP DATABASE msi_test_db; diff --git a/mysql-test/main/mysql_secure_installation.test b/mysql-test/main/mysql_secure_installation.test index e1a4f07bc5f8e..8715f850ed102 100644 --- a/mysql-test/main/mysql_secure_installation.test +++ b/mysql-test/main/mysql_secure_installation.test @@ -1,27 +1,38 @@ -# MDEV-10112: mysql_secure_installation should use DDL (GRANT, REVOKE, etc) -# instead of DML for Galera compatibility -# -# This test verifies that mysql_secure_installation works correctly -# after being converted from DML (UPDATE/DELETE on system tables) to -# DDL (ALTER USER, DROP USER, REVOKE, etc). - --source include/not_windows.inc --source include/not_embedded.inc +--source include/save_sys_tables.inc + +--echo # +--echo # MDEV-10112: mysql_secure_installation should use DDL (GRANT, REVOKE, etc) +--echo # instead of DML for Galera compatibility +--echo # + +--echo # +--echo # Setup dummy accounts and database to be secured +--echo # +CREATE USER 'root'@'remote_host' IDENTIFIED BY 'pass'; +CREATE USER 'test_user'@'localhost' IDENTIFIED BY 'pass'; -# To avoid affecting other tests, we run this test on a temporary datadir -# using MTR's official restart mechanism. ---let $MSI_DATADIR= $MYSQLTEST_VARDIR/tmp/msi_datadir ---mkdir $MSI_DATADIR ---exec cp -R $MYSQLTEST_VARDIR/install.db/. $MSI_DATADIR/ ---exec chmod -R 700 $MSI_DATADIR +# 'test' is a special database in MTR, ensuring it exists for the script +--disable_warnings +CREATE DATABASE IF NOT EXISTS test; +--enable_warnings ---let $old_restart_parameters= $restart_parameters ---let $restart_parameters= --datadir=$MSI_DATADIR ---source include/restart_mysqld.inc +# Create a custom database to ensure we don't interfere with other tests +CREATE DATABASE msi_test_db; +GRANT ALL PRIVILEGES ON msi_test_db.* TO 'test_user'@'localhost'; -# -# Scripted answers for mariadb-secure-installation -# +# Verify the script's REVOKE logic by granting on 'test' +GRANT ALL PRIVILEGES ON test.* TO 'test_user'@'localhost'; + +# Prepare input for the script +# 1. [Empty] - current root password +# 2. n - Enable unix_socket? +# 3. n - Set root password? +# 4. Y - Remove anonymous users? +# 5. Y - Disallow root login remotely? +# 6. Y - Remove test database? +# 7. Y - Reload privilege tables? --write_file $MYSQLTEST_VARDIR/tmp/msi_input.txt n @@ -32,35 +43,24 @@ Y Y EOF -# --basedir is needed so the script can find my_print_defaults and mariadb -# from the build tree (extra/ and client/ directories). -# Output redirected to log file to keep result file clean. +# Run the script --exec $MYSQL_SECURE_INSTALLATION --basedir=$MYSQL_BINDIR -S $MASTER_MYSOCK < $MYSQLTEST_VARDIR/tmp/msi_input.txt > $MYSQLTEST_VARDIR/tmp/msi.log 2>&1 --remove_file $MYSQLTEST_VARDIR/tmp/msi_input.txt --remove_file $MYSQLTEST_VARDIR/tmp/msi.log -# -# Verify the results -# ---echo # Verify: anonymous users removed -SELECT user, host FROM mysql.global_priv WHERE user = '' ORDER BY host; - ---echo # Verify: only local root accounts remain -SELECT user, host FROM mysql.global_priv - WHERE user = 'root' ORDER BY host; +--echo # Verify: remote root account removed +SELECT user, host FROM mysql.global_priv WHERE user = 'root' AND host = 'remote_host'; --echo # Verify: test database removed SHOW DATABASES LIKE 'test'; ---echo # Verify: no test DB privileges remain in mysql.db -SELECT user, host, db FROM mysql.db - WHERE db = 'test' OR db LIKE 'test\\_%' - ORDER BY user, host, db; +--echo # Verify: test DB privileges removed from mysql.db +SELECT user, host, db FROM mysql.db WHERE db = 'test' ORDER BY user, host, db; + +# Cleanup +# The script already dropped 'root'@'remote_host' and 'test' DB. +DROP USER 'test_user'@'localhost'; +DROP DATABASE msi_test_db; -# -# Clean up: restore the original server state -# ---let $restart_parameters= $old_restart_parameters ---source include/restart_mysqld.inc ---rmdir $MSI_DATADIR +--source include/restore_sys_tables.inc diff --git a/mysql-test/suite/galera/r/galera_secure_installation.result b/mysql-test/suite/galera/r/galera_secure_installation.result new file mode 100644 index 0000000000000..5a475646f4a8d --- /dev/null +++ b/mysql-test/suite/galera/r/galera_secure_installation.result @@ -0,0 +1,30 @@ +# +# MDEV-10112: mysql_secure_installation should use DDL (GRANT, REVOKE, etc) +# instead of DML for Galera compatibility +# +# On node_1 +# +# Setup remote root account to be removed +# +CREATE USER 'root'@'remote_host' IDENTIFIED BY 'pass'; +GRANT ALL ON *.* TO 'root'@'remote_host'; +# On node_2 +SELECT user, host FROM mysql.global_priv WHERE user = 'root' AND host = 'remote_host'; +user host +root remote_host +SHOW DATABASES LIKE 'test'; +Database (test) +test +# Verify on node_1: remote root is gone +SELECT user, host FROM mysql.global_priv WHERE user = 'root' AND host = 'remote_host'; +user host +# Verify on node_1: test database is gone +SHOW DATABASES LIKE 'test'; +Database (test) +# Now verify on node_2 (replication check) +# Verify on node_2: remote root is gone (replicated) +SELECT user, host FROM mysql.global_priv WHERE user = 'root' AND host = 'remote_host'; +user host +# Verify on node_2: test database is gone (replicated) +SHOW DATABASES LIKE 'test'; +Database (test) diff --git a/mysql-test/suite/galera/t/galera_secure_installation.test b/mysql-test/suite/galera/t/galera_secure_installation.test new file mode 100644 index 0000000000000..b06ed4680569c --- /dev/null +++ b/mysql-test/suite/galera/t/galera_secure_installation.test @@ -0,0 +1,66 @@ +--source include/galera_cluster.inc +--source include/have_innodb.inc + +--echo # +--echo # MDEV-10112: mysql_secure_installation should use DDL (GRANT, REVOKE, etc) +--echo # instead of DML for Galera compatibility +--echo # + +--connection node_1 +--echo # +--echo # Setup remote root account to be removed +--echo # +CREATE USER 'root'@'remote_host' IDENTIFIED BY 'pass'; +GRANT ALL ON *.* TO 'root'@'remote_host'; + +# 'test' is a special database in MTR, ensuring it exists for the script +--disable_warnings +CREATE DATABASE IF NOT EXISTS test; +--enable_warnings + +--connection node_2 +--let $wait_condition = SELECT COUNT(*) = 1 FROM mysql.global_priv WHERE user = 'root' AND host = 'remote_host' +--source include/wait_condition.inc +SELECT user, host FROM mysql.global_priv WHERE user = 'root' AND host = 'remote_host'; +SHOW DATABASES LIKE 'test'; + +--connection node_1 +# Prepare input: +# 1. [Empty] - current root password +# 2. n - Enable unix_socket? +# 3. n - Set root password? +# 4. n - Remove anonymous users? +# 5. Y - Disallow root login remotely? +# 6. Y - Remove test database? +# 7. Y - Reload privilege tables? + +--write_file $MYSQLTEST_VARDIR/tmp/msi_galera_input.txt + +n +n +n +Y +Y +Y +EOF + +--exec $MYSQL_SECURE_INSTALLATION --basedir=$MYSQL_BINDIR -S $MASTER_MYSOCK < $MYSQLTEST_VARDIR/tmp/msi_galera_input.txt > $MYSQLTEST_VARDIR/tmp/msi_galera.log 2>&1 + +--remove_file $MYSQLTEST_VARDIR/tmp/msi_galera_input.txt +--remove_file $MYSQLTEST_VARDIR/tmp/msi_galera.log + +--echo # Verify on node_1: remote root is gone +SELECT user, host FROM mysql.global_priv WHERE user = 'root' AND host = 'remote_host'; +--echo # Verify on node_1: test database is gone +SHOW DATABASES LIKE 'test'; + +--echo # Now verify on node_2 (replication check) +--connection node_2 +--let $wait_condition = SELECT COUNT(*) = 0 FROM mysql.global_priv WHERE user = 'root' AND host = 'remote_host' +--source include/wait_condition.inc +--echo # Verify on node_2: remote root is gone (replicated) +SELECT user, host FROM mysql.global_priv WHERE user = 'root' AND host = 'remote_host'; +--echo # Verify on node_2: test database is gone (replicated) +SHOW DATABASES LIKE 'test'; + +--source include/galera_end.inc diff --git a/scripts/mysql_secure_installation.sh b/scripts/mysql_secure_installation.sh index 06eb01c8bc10d..aecffdf2eba4b 100755 --- a/scripts/mysql_secure_installation.sh +++ b/scripts/mysql_secure_installation.sh @@ -317,11 +317,16 @@ set_root_password() { esc_pass=`basic_single_escape "$password1"` query=" -SET @str = IFNULL((SELECT GROUP_CONCAT(CONCAT('\'', User, '\'@\'', Host, '\'')) FROM mysql.global_priv WHERE User='root'), 'root@localhost'); -SET @str = CONCAT('ALTER USER ', @str, ' IDENTIFIED BY \'$esc_pass\''); -PREPARE stmt FROM @str; -EXECUTE stmt; -DEALLOCATE PREPARE stmt;" + SET @str = IFNULL((SELECT GROUP_CONCAT(CONCAT(QUOTE(User), + '@', + QUOTE(Host), + ' IDENTIFIED BY ', + '\'$esc_pass\'')) + FROM mysql.global_priv + WHERE User='root'), + '\'root\'@\'localhost\' IDENTIFIED BY \'$esc_pass\''); + SET @str = CONCAT('ALTER USER ', @str); + EXECUTE IMMEDIATE @str;" do_query "$query" if [ $? -eq 0 ]; then echo "Password updated successfully!" @@ -343,11 +348,15 @@ DEALLOCATE PREPARE stmt;" remove_anonymous_users() { query=" -SET @str = (SELECT IFNULL(CONCAT('DROP USER IF EXISTS ', GROUP_CONCAT(CONCAT('\'', User, '\'@\'', Host, '\''))), '') FROM mysql.global_priv WHERE User=''); -SET @str = IF(@str = '', 'DO 1', @str); -PREPARE stmt FROM @str; -EXECUTE stmt; -DEALLOCATE PREPARE stmt;" + SET @str = (SELECT IFNULL(CONCAT('DROP USER IF EXISTS ', + GROUP_CONCAT(CONCAT(QUOTE(User), + '@', + QUOTE(Host)))), + '') + FROM mysql.global_priv + WHERE User=''); + SET @str = IF(@str = '', 'DO 1', @str); + EXECUTE IMMEDIATE @str;" do_query "$query" if [ $? -eq 0 ]; then echo " ... Success!" @@ -361,11 +370,16 @@ DEALLOCATE PREPARE stmt;" remove_remote_root() { query=" -SET @str = (SELECT IFNULL(CONCAT('DROP USER IF EXISTS ', GROUP_CONCAT(CONCAT('\'', User, '\'@\'', Host, '\''))), '') FROM mysql.global_priv WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1')); -SET @str = IF(@str = '', 'DO 1', @str); -PREPARE stmt FROM @str; -EXECUTE stmt; -DEALLOCATE PREPARE stmt;" + SET @str = (SELECT IFNULL(CONCAT('DROP USER IF EXISTS ', + GROUP_CONCAT(CONCAT(QUOTE(User), + '@', + QUOTE(Host)))), + '') + FROM mysql.global_priv + WHERE User='root' AND + Host NOT IN ('localhost', '127.0.0.1', '::1')); + SET @str = IF(@str = '', 'DO 1', @str); + EXECUTE IMMEDIATE @str;" do_query "$query" if [ $? -eq 0 ]; then echo " ... Success!" @@ -389,10 +403,15 @@ remove_test_database() { # via DROP USER, which also removes their mysql.db rows automatically. # FLUSH PRIVILEGES below clears any remaining stale cache entries. query=" -SET @str = (SELECT IFNULL(CONCAT('REVOKE ALL PRIVILEGES ON \`test\`.* FROM ', GROUP_CONCAT(CONCAT('\'', User, '\'@\'', Host, '\''))), 'DO 1') FROM mysql.db WHERE Db='test' AND User <> ''); -PREPARE stmt FROM @str; -EXECUTE stmt; -DEALLOCATE PREPARE stmt;" + SET @str = (SELECT IFNULL(CONCAT('REVOKE ALL PRIVILEGES ON \`test\`.* ', + 'FROM ', + GROUP_CONCAT(CONCAT(QUOTE(User), + '@', + QUOTE(Host)))), + 'DO 1') + FROM mysql.db + WHERE Db='test' AND User <> ''); + EXECUTE IMMEDIATE @str;" do_query "$query" return 0 @@ -471,11 +490,19 @@ if [ "$reply" = "n" ]; then else emptypass=0 query=" -SET @str = IFNULL((SELECT GROUP_CONCAT(CONCAT('\'', User, '\'@\'', Host, '\'')) FROM mysql.global_priv WHERE User='root'), 'root@localhost'); -SET @str = CONCAT('ALTER USER ', @str, ' IDENTIFIED VIA mysql_native_password USING \'invalid\' OR unix_socket'); -PREPARE stmt FROM @str; -EXECUTE stmt; -DEALLOCATE PREPARE stmt;" + SET @str = IFNULL((SELECT GROUP_CONCAT(CONCAT(QUOTE(User), + '@', + QUOTE(Host), + ' IDENTIFIED VIA ', + 'mysql_native_password ', + 'USING \'invalid\' ', + 'OR unix_socket')) + FROM mysql.global_priv + WHERE User='root'), + '\'root\'@\'localhost\' IDENTIFIED VIA ' + 'mysql_native_password USING \'invalid\' OR unix_socket'); + SET @str = CONCAT('ALTER USER ', @str); + EXECUTE IMMEDIATE @str;" do_query "$query" if [ $? -eq 0 ]; then echo "Enabled successfully!"