summaryrefslogtreecommitdiffstats
path: root/scripts/mysql_secure_installation.sh
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:07:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:07:14 +0000
commita175314c3e5827eb193872241446f2f8f5c9d33c (patch)
treecd3d60ca99ae00829c52a6ca79150a5b6e62528b /scripts/mysql_secure_installation.sh
parentInitial commit. (diff)
downloadmariadb-10.5-a175314c3e5827eb193872241446f2f8f5c9d33c.tar.xz
mariadb-10.5-a175314c3e5827eb193872241446f2f8f5c9d33c.zip
Adding upstream version 1:10.5.12.upstream/1%10.5.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'scripts/mysql_secure_installation.sh')
-rw-r--r--scripts/mysql_secure_installation.sh577
1 files changed, 577 insertions, 0 deletions
diff --git a/scripts/mysql_secure_installation.sh b/scripts/mysql_secure_installation.sh
new file mode 100644
index 00000000..b2a9edf4
--- /dev/null
+++ b/scripts/mysql_secure_installation.sh
@@ -0,0 +1,577 @@
+#!/bin/sh
+
+# Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA
+
+config=".my.cnf.$$"
+command=".mysql.$$"
+output=".my.output.$$"
+
+trap "interrupt" 1 2 3 6 15
+
+rootpass=""
+echo_n=
+echo_c=
+basedir=
+defaults_file=
+defaults_extra_file=
+no_defaults=
+
+parse_arg()
+{
+ echo "$1" | sed -e 's/^[^=]*=//'
+}
+
+parse_arguments()
+{
+ # We only need to pass arguments through to the server if we don't
+ # handle them here. So, we collect unrecognized options (passed on
+ # the command line) into the args variable.
+ pick_args=
+ if test "$1" = PICK-ARGS-FROM-ARGV
+ then
+ pick_args=1
+ shift
+ fi
+
+ for arg
+ do
+ case "$arg" in
+ --basedir=*) basedir=`parse_arg "$arg"` ;;
+ --defaults-file=*) defaults_file="$arg" ;;
+ --defaults-extra-file=*) defaults_extra_file="$arg" ;;
+ --no-defaults) no_defaults="$arg" ;;
+ *)
+ if test -n "$pick_args"
+ then
+ # This sed command makes sure that any special chars are quoted,
+ # so the arg gets passed exactly to the server.
+ # XXX: This is broken; true fix requires using eval and proper
+ # quoting of every single arg ($basedir, $ldata, etc.)
+ #args="$args "`echo "$arg" | sed -e 's,\([^a-zA-Z0-9_.-]\),\\\\\1,g'`
+ args="$args $arg"
+ fi
+ ;;
+ esac
+ done
+}
+
+# Try to find a specific file within --basedir which can either be a binary
+# release or installed source directory and return the path.
+find_in_basedir()
+{
+ return_dir=0
+ found=0
+ case "$1" in
+ --dir)
+ return_dir=1; shift
+ ;;
+ esac
+
+ file=$1; shift
+
+ for dir in "$@"
+ do
+ if test -f "$basedir/$dir/$file"
+ then
+ found=1
+ if test $return_dir -eq 1
+ then
+ echo "$basedir/$dir"
+ else
+ echo "$basedir/$dir/$file"
+ fi
+ break
+ fi
+ done
+
+ if test $found -eq 0
+ then
+ # Test if command is in PATH
+ $file --no-defaults --version > /dev/null 2>&1
+ status=$?
+ if test $status -eq 0
+ then
+ echo $file
+ fi
+ fi
+}
+
+cannot_find_file()
+{
+ echo
+ echo "FATAL ERROR: Could not find $1"
+
+ shift
+ if test $# -ne 0
+ then
+ echo
+ echo "The following directories were searched:"
+ echo
+ for dir in "$@"
+ do
+ echo " $dir"
+ done
+ fi
+
+ echo
+ echo "If you compiled from source, you need to run 'make install' to"
+ echo "copy the software into the correct location ready for operation."
+ echo
+ echo "If you are using a binary release, you must either be at the top"
+ echo "level of the extracted archive, or pass the --basedir option"
+ echo "pointing to that location."
+ echo
+}
+
+# Ok, let's go. We first need to parse arguments which are required by
+# my_print_defaults so that we can execute it first, then later re-parse
+# the command line to add any extra bits that we need.
+parse_arguments PICK-ARGS-FROM-ARGV "$@"
+
+#
+# We can now find my_print_defaults. This script supports:
+#
+# --srcdir=path pointing to compiled source tree
+# --basedir=path pointing to installed binary location
+#
+# or default to compiled-in locations.
+#
+
+if test -n "$basedir"
+then
+ print_defaults=`find_in_basedir my_print_defaults bin extra`
+ echo "print: $print_defaults"
+ if test -z "$print_defaults"
+ then
+ cannot_find_file my_print_defaults $basedir/bin $basedir/extra
+ exit 1
+ fi
+ mysql_command=`find_in_basedir mysql bin`
+ if test -z "$mysql_command"
+ then
+ cannot_find_file mysql $basedir/bin
+ exit 1
+ fi
+else
+ print_defaults="@bindir@/my_print_defaults"
+ mysql_command="@bindir@/mysql"
+fi
+
+if test ! -x "$print_defaults"
+then
+ cannot_find_file "$print_defaults"
+ exit 1
+fi
+
+if test ! -x "$mysql_command"
+then
+ cannot_find_file "$mysql_command"
+ exit 1
+fi
+
+# Now we can get arguments from the group [client] and [client-server]
+# in the my.cfg file, then re-run to merge with command line arguments.
+parse_arguments `$print_defaults $defaults_file $defaults_extra_file $no_defaults client client-server client-mariadb`
+parse_arguments PICK-ARGS-FROM-ARGV "$@"
+
+set_echo_compat() {
+ case `echo "testing\c"`,`echo -n testing` in
+ *c*,-n*) echo_n= echo_c= ;;
+ *c*,*) echo_n=-n echo_c= ;;
+ *) echo_n= echo_c='\c' ;;
+ esac
+}
+
+validate_reply () {
+ ret=0
+ if [ -z "$1" ]; then
+ reply=y
+ return $ret
+ fi
+ case $1 in
+ y|Y|yes|Yes|YES) reply=y ;;
+ n|N|no|No|NO) reply=n ;;
+ *) ret=1 ;;
+ esac
+ return $ret
+}
+
+prepare() {
+ touch $config $command
+ chmod 600 $config $command
+}
+
+do_query() {
+ echo "$1" >$command
+ #sed 's,^,> ,' < $command # Debugging
+ $mysql_command --defaults-file=$config $defaults_extra_file $no_defaults $args <$command >$output
+ return $?
+}
+
+# Simple escape mechanism (\-escape any ' and \), suitable for two contexts:
+# - single-quoted SQL strings
+# - single-quoted option values on the right hand side of = in my.cnf
+#
+# These two contexts don't handle escapes identically. SQL strings allow
+# quoting any character (\C => C, for any C), but my.cnf parsing allows
+# quoting only \, ' or ". For example, password='a\b' quotes a 3-character
+# string in my.cnf, but a 2-character string in SQL.
+#
+# This simple escape works correctly in both places.
+basic_single_escape () {
+ # The quoting on this sed command is a bit complex. Single-quoted strings
+ # don't allow *any* escape mechanism, so they cannot contain a single
+ # quote. The string sed gets (as argv[1]) is: s/\(['\]\)/\\\1/g
+ #
+ # Inside a character class, \ and ' are not special, so the ['\] character
+ # class is balanced and contains two characters.
+ echo "$1" | sed 's/\(['"'"'\]\)/\\\1/g'
+}
+
+#
+# create a simple my.cnf file to be able to pass the root password to the mysql
+# client without putting it on the command line
+#
+make_config() {
+ echo "# mysql_secure_installation config file" >$config
+ echo "[mysql]" >>$config
+ echo "user=root" >>$config
+ esc_pass=`basic_single_escape "$rootpass"`
+ echo "password='$esc_pass'" >>$config
+ #sed 's,^,> ,' < $config # Debugging
+
+ if test -n "$defaults_file"
+ then
+ dfile=`parse_arg "$defaults_file"`
+ cat "$dfile" >>$config
+ fi
+}
+
+get_root_password() {
+ status=1
+ while [ $status -eq 1 ]; do
+ stty -echo
+ echo $echo_n "Enter current password for root (enter for none): $echo_c"
+ read password
+ echo
+ stty echo
+ if [ "x$password" = "x" ]; then
+ emptypass=1
+ else
+ emptypass=0
+ fi
+ rootpass=$password
+ make_config
+ do_query "show create user root@localhost"
+ status=$?
+ done
+ if grep -q unix_socket $output; then
+ emptypass=0
+ fi
+ echo "OK, successfully used password, moving on..."
+ echo
+}
+
+set_root_password() {
+ stty -echo
+ echo $echo_n "New password: $echo_c"
+ read password1
+ echo
+ echo $echo_n "Re-enter new password: $echo_c"
+ read password2
+ echo
+ stty echo
+
+ if [ "$password1" != "$password2" ]; then
+ echo "Sorry, passwords do not match."
+ echo
+ return 1
+ fi
+
+ if [ "$password1" = "" ]; then
+ echo "Sorry, you can't use an empty password here."
+ echo
+ return 1
+ 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';"
+ if [ $? -eq 0 ]; then
+ echo "Password updated successfully!"
+ echo "Reloading privilege tables.."
+ reload_privilege_tables
+ if [ $? -eq 1 ]; then
+ clean_and_exit
+ fi
+ echo
+ rootpass=$password1
+ make_config
+ else
+ echo "Password update failed!"
+ clean_and_exit
+ fi
+
+ return 0
+}
+
+remove_anonymous_users() {
+ do_query "DELETE FROM mysql.global_priv WHERE User='';"
+ if [ $? -eq 0 ]; then
+ echo " ... Success!"
+ else
+ echo " ... Failed!"
+ clean_and_exit
+ fi
+
+ return 0
+}
+
+remove_remote_root() {
+ do_query "DELETE FROM mysql.global_priv WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');"
+ if [ $? -eq 0 ]; then
+ echo " ... Success!"
+ else
+ echo " ... Failed!"
+ fi
+}
+
+remove_test_database() {
+ echo " - Dropping test database..."
+ do_query "DROP DATABASE IF EXISTS test;"
+ if [ $? -eq 0 ]; then
+ echo " ... Success!"
+ else
+ echo " ... Failed! Not critical, keep moving..."
+ 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
+
+ return 0
+}
+
+reload_privilege_tables() {
+ do_query "FLUSH PRIVILEGES;"
+ if [ $? -eq 0 ]; then
+ echo " ... Success!"
+ return 0
+ else
+ echo " ... Failed!"
+ return 1
+ fi
+}
+
+interrupt() {
+ echo
+ echo "Aborting!"
+ echo
+ cleanup
+ stty echo
+ exit 1
+}
+
+cleanup() {
+ echo "Cleaning up..."
+ rm -f $config $command $output
+}
+
+# Remove the files before exiting.
+clean_and_exit() {
+ cleanup
+ exit 1
+}
+
+# The actual script starts here
+
+prepare
+set_echo_compat
+
+echo
+echo "NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB"
+echo " SERVERS IN PRODUCTION USE! PLEASE READ EACH STEP CAREFULLY!"
+echo
+echo "In order to log into MariaDB to secure it, we'll need the current"
+echo "password for the root user. If you've just installed MariaDB, and"
+echo "haven't set the root password yet, you should just press enter here."
+echo
+
+get_root_password
+
+
+#
+# Set the root password
+#
+
+echo "Setting the root password or using the unix_socket ensures that nobody"
+echo "can log into the MariaDB root user without the proper authorisation."
+echo
+
+while true ; do
+ if [ $emptypass -eq 1 ]; then
+ echo $echo_n "Enable unix_socket authentication? [Y/n] $echo_c"
+ else
+ echo "You already have your root account protected, so you can safely answer 'n'."
+ echo
+ echo $echo_n "Switch to unix_socket authentication [Y/n] $echo_c"
+ fi
+ read reply
+ validate_reply $reply && break
+done
+
+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';"
+ if [ $? -eq 0 ]; then
+ echo "Enabled successfully!"
+ echo "Reloading privilege tables.."
+ reload_privilege_tables
+ if [ $? -eq 1 ]; then
+ clean_and_exit
+ fi
+ echo
+ else
+ echo "Failed!"
+ clean_and_exit
+ fi
+fi
+echo
+
+while true ; do
+ if [ $emptypass -eq 1 ]; then
+ echo $echo_n "Set root password? [Y/n] $echo_c"
+ else
+ echo "You already have your root account protected, so you can safely answer 'n'."
+ echo
+ echo $echo_n "Change the root password? [Y/n] $echo_c"
+ fi
+ read reply
+ validate_reply $reply && break
+done
+
+if [ "$reply" = "n" ]; then
+ echo " ... skipping."
+else
+ status=1
+ while [ $status -eq 1 ]; do
+ set_root_password
+ status=$?
+ done
+fi
+echo
+
+
+#
+# Remove anonymous users
+#
+
+echo "By default, a MariaDB installation has an anonymous user, allowing anyone"
+echo "to log into MariaDB without having to have a user account created for"
+echo "them. This is intended only for testing, and to make the installation"
+echo "go a bit smoother. You should remove them before moving into a"
+echo "production environment."
+echo
+
+while true ; do
+ echo $echo_n "Remove anonymous users? [Y/n] $echo_c"
+ read reply
+ validate_reply $reply && break
+done
+if [ "$reply" = "n" ]; then
+ echo " ... skipping."
+else
+ remove_anonymous_users
+fi
+echo
+
+
+#
+# Disallow remote root login
+#
+
+echo "Normally, root should only be allowed to connect from 'localhost'. This"
+echo "ensures that someone cannot guess at the root password from the network."
+echo
+while true ; do
+ echo $echo_n "Disallow root login remotely? [Y/n] $echo_c"
+ read reply
+ validate_reply $reply && break
+done
+if [ "$reply" = "n" ]; then
+ echo " ... skipping."
+else
+ remove_remote_root
+fi
+echo
+
+
+#
+# Remove test database
+#
+
+echo "By default, MariaDB comes with a database named 'test' that anyone can"
+echo "access. This is also intended only for testing, and should be removed"
+echo "before moving into a production environment."
+echo
+
+while true ; do
+ echo $echo_n "Remove test database and access to it? [Y/n] $echo_c"
+ read reply
+ validate_reply $reply && break
+done
+
+if [ "$reply" = "n" ]; then
+ echo " ... skipping."
+else
+ remove_test_database
+fi
+echo
+
+
+#
+# Reload privilege tables
+#
+
+echo "Reloading the privilege tables will ensure that all changes made so far"
+echo "will take effect immediately."
+echo
+
+while true ; do
+ echo $echo_n "Reload privilege tables now? [Y/n] $echo_c"
+ read reply
+ validate_reply $reply && break
+done
+
+if [ "$reply" = "n" ]; then
+ echo " ... skipping."
+else
+ reload_privilege_tables
+fi
+echo
+
+cleanup
+
+echo
+echo "All done! If you've completed all of the above steps, your MariaDB"
+echo "installation should now be secure."
+echo
+echo "Thanks for using MariaDB!"