Skip to content
/ server Public
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions mysql-test/main/mysql_secure_installation.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#
# 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: test database removed
SHOW DATABASES LIKE 'test';
Database (test)
# 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
# Cleanup
DROP USER 'test_user'@'localhost';
DROP DATABASE msi_test_db;
66 changes: 66 additions & 0 deletions mysql-test/main/mysql_secure_installation.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
--source include/not_windows.inc
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this would be at the top. However many galera tests would be doing these already.

--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';

# 'test' is a special database in MTR, ensuring it exists for the script
--disable_warnings
CREATE DATABASE IF NOT EXISTS test;
--enable_warnings

# 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';

# 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
n
Y
Y
Y
Y
EOF

# 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

--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: 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;

--source include/restore_sys_tables.inc
20 changes: 17 additions & 3 deletions mysql-test/mariadb-test-run.pl
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}

Expand Down Expand Up @@ -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
# ----------------------------------------------------
Expand Down
30 changes: 30 additions & 0 deletions mysql-test/suite/galera/r/galera_secure_installation.result
Original file line number Diff line number Diff line change
@@ -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)
66 changes: 66 additions & 0 deletions mysql-test/suite/galera/t/galera_secure_installation.test
Original file line number Diff line number Diff line change
@@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same remarks as the other test.

--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
79 changes: 67 additions & 12 deletions scripts/mysql_secure_installation.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -316,7 +316,18 @@ 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(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!"
echo "Reloading privilege tables.."
Expand All @@ -336,7 +347,17 @@ 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(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!"
else
Expand All @@ -348,7 +369,18 @@ 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(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!"
else
Expand All @@ -366,12 +398,21 @@ 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(QUOTE(User),
'@',
QUOTE(Host)))),
'DO 1')
FROM mysql.db
WHERE Db='test' AND User <> '');
EXECUTE IMMEDIATE @str;"
do_query "$query"

return 0
}
Expand Down Expand Up @@ -448,7 +489,21 @@ 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(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!"
echo "Reloading privilege tables.."
Expand Down
Loading