diff --git a/.test/initdb.d/a_first.sh b/.test/initdb.d/a_first.sh index 0987955..d8724cc 100755 --- a/.test/initdb.d/a_first.sh +++ b/.test/initdb.d/a_first.sh @@ -1,4 +1,5 @@ #!/bin/sh -mysql -u "${MARIADB_USER:-$MYSQL_USER}" -p"${MARIADB_PASSWORD:-$MYSQL_PASSWORD}" \ +PASS="${MARIADB_PASSWORD:-$MYSQL_PASSWORD}" +mysql -u "${MARIADB_USER:-$MYSQL_USER}" -p"${PASS}" \ -e 'create table t1 (i int unsigned primary key not null)' \ "${MARIADB_DATABASE:-$MYSQL_DATABASE}" diff --git a/.test/run.sh b/.test/run.sh index b1f4449..736c3ac 100755 --- a/.test/run.sh +++ b/.test/run.sh @@ -199,6 +199,16 @@ echo -e "Test: MYSQL_ROOT_HOST\n" runandwait -e MYSQL_ALLOW_EMPTY_PASSWORD=1 -e MYSQL_ROOT_HOST=apple "${image}" ru=$(mariadbclient_unix --skip-column-names -B -u root -e 'select user,host from mysql.user where host="apple"') [ "${ru}" = '' ] && die 'root@apple not created' +killoff + + ;& + mysql_root_host_localhost) + +echo -e "Test: MYSQL_ROOT_HOST=localhost\n" + +runandwait -e MARIADB_ROOT_PASSWORD=bob -e MYSQL_ROOT_HOST=localhost "${image}" +ru=$(mariadbclient_unix --skip-column-names -B -u root -pbob -e 'select user,host from mysql.user where user="root" and host="localhost"') +[ "${ru}" = '' ] && die 'root@localhost not created' killoff ;& @@ -591,10 +601,28 @@ binlog) echo -e "Test: create user passwords using password hash\n" - runandwait -e MARIADB_ROOT_PASSWORD_HASH='*61584B76F6ECE8FB9A328E7CF198094B2FAC55C7' -e MARIADB_PASSWORD_HASH='*0FD9A3F0F816D076CF239580A68A1147C250EB7B' -e MARIADB_DATABASE=neptune -e MARIADB_USER=henry "${image}" +initdb=$(mktemp -d) +chmod go+rx "${initdb}" +cp -a "$dir"/initdb.d/* "${initdb}" +sed -i -e 's/^PASS=.*/PASS=jane/' "${initdb}"/a_first.sh +gzip "${initdb}"/*gz* +xz "${initdb}"/*xz* +zstd "${initdb}"/*zst* + + runandwait -e MARIADB_ROOT_PASSWORD_HASH='*61584B76F6ECE8FB9A328E7CF198094B2FAC55C7' \ + -e MARIADB_PASSWORD_HASH='*0FD9A3F0F816D076CF239580A68A1147C250EB7B' \ + -e MARIADB_DATABASE=neptune \ + -e MARIADB_USER=henry \ + -v "${initdb}":/docker-entrypoint-initdb.d:Z \ + "${image}" mariadbclient -u root -pbob -e 'select current_user()' + mariadbclient_unix -u root -pbob -e 'select current_user()' mariadbclient -u henry -pjane neptune -e 'select current_user()' + + init_sum=$(mariadbclient --skip-column-names -B -u henry -pjane -P 3306 -h 127.0.0.1 --protocol tcp neptune -e "select sum(i) from t1;") + [ "${init_sum}" = '1833' ] || (podman logs m_init; die 'initialization order error') killoff + rm -rf "${initdb}" # Insert new tests above by copying the comments below # ;& @@ -605,3 +633,5 @@ binlog) echo "Test $2 not found" >&2 exit 1 esac + +echo "Tests finished" diff --git a/10.10/docker-entrypoint.sh b/10.10/docker-entrypoint.sh index 48cb22c..bd674b3 100755 --- a/10.10/docker-entrypoint.sh +++ b/10.10/docker-entrypoint.sh @@ -147,9 +147,8 @@ docker_temp_server_start() { # Stop the server. When using a local socket file mariadb-admin will block until # the shutdown is complete. docker_temp_server_stop() { - if ! MYSQL_PWD=$MARIADB_ROOT_PASSWORD mariadb-admin shutdown -uroot --socket="${SOCKET}"; then - mysql_error "Unable to shut down server." - fi + kill "$MARIADB_PID" + wait "$MARIADB_PID" } # Verify that the minimally required password settings are set for new databases. @@ -291,22 +290,24 @@ docker_setup_db() { # Creates root users for non-localhost hosts local rootCreate= + local rootPasswordEscaped= + if [ -n "$MARIADB_ROOT_PASSWORD" ]; then + # Sets root password and creates root users for non-localhost hosts + rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) + fi # default root to listen for connections from anywhere if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then - if [ -n "$MARIADB_ROOT_PASSWORD" ]; then - # Sets root password and creates root users for non-localhost hosts - local rootPasswordEscaped - rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) - # no, we don't care if read finds a terminating character in this heredoc - # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + # ref "read -d ''", no, we don't care if read finds a terminating character in this heredoc + # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + if [ -n "$MARIADB_ROOT_PASSWORD_HASH" ]; then read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL else read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL fi @@ -335,12 +336,44 @@ docker_setup_db() { fi fi + local rootLocalhostPass= + if [ -z "$MARIADB_ROOT_PASSWORD_HASH" ]; then + # handle MARIADB_ROOT_PASSWORD_HASH for root@localhost after /docker-entrypoint-initdb.d + rootLocalhostPass="SET PASSWORD FOR 'root'@'localhost'= PASSWORD('${rootPasswordEscaped}');" + fi + + local createDatabase= + # Creates a custom database and user if specified + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Creating database ${MARIADB_DATABASE}" + createDatabase="CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\`;" + fi + + local createUser= + local userGrants= + if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then + mysql_note "Creating user ${MARIADB_USER}" + if [ -n "$MARIADB_PASSWORD_HASH" ]; then + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH';" + else + # SQL escape the user password, \ followed by ' + local userPasswordEscaped + userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped';" + fi + + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" + userGrants="GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%';" + fi + fi + mysql_note "Securing system users (equivalent to running mysql_secure_installation)" # tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set # --binary-mode to save us from the semi-mad users go out of their way to confuse the encoding. docker_process_sql --dont-use-mysql-root-password --database=mysql --binary-mode <<-EOSQL - -- What's done in this file shouldn't be replicated - -- or products like mysql-fabric won't work + -- Securing system users shouldn't be replicated + SET @orig_sql_log_bin= @@SESSION.SQL_LOG_BIN; SET @@SESSION.SQL_LOG_BIN=0; -- we need the SQL_MODE NO_BACKSLASH_ESCAPES mode to be clear for the password to be set SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); @@ -348,42 +381,19 @@ docker_setup_db() { DROP USER IF EXISTS root@'127.0.0.1', root@'::1'; EXECUTE IMMEDIATE CONCAT('DROP USER IF EXISTS root@\'', @@hostname,'\''); - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ; + ${rootLocalhostPass} ${rootCreate} ${mysqlAtLocalhost} ${mysqlAtLocalhostGrants} - -- pre-10.3 + -- pre-10.3 only DROP DATABASE IF EXISTS test ; + -- end of securing system users, rest of init now... + SET @@SESSION.SQL_LOG_BIN=@orig_sql_log_bin; + -- create users/databases + ${createDatabase} + ${createUser} + ${userGrants} EOSQL - - # Creates a custom database and user if specified - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Creating database ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\` ;" - fi - - if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then - mysql_note "Creating user ${MARIADB_USER}" - if [ -n "$MARIADB_PASSWORD" ]; then - # SQL escape the user password, \ followed by ' - local userPasswordEscaped - userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped'; - EOSQL_USER - else - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH'; - EOSQL_USER - fi - - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%' ;" - fi - fi } # backup the mysql database @@ -430,11 +440,8 @@ docker_mariadb_upgrade() { mariadb-upgrade --upgrade-system-tables mysql_note "Finished mariadb-upgrade" - # docker_temp_server_stop needs authentication since - # upgrade ended in FLUSH PRIVILEGES mysql_note "Stopping temporary server" - kill "$MARIADB_PID" - wait "$MARIADB_PID" + docker_temp_server_stop mysql_note "Temporary server stopped" } @@ -509,6 +516,15 @@ _main() { docker_setup_db docker_process_init_files /docker-entrypoint-initdb.d/* + # Wait until after /docker-entrypoint-initdb.d is performed before setting + # root@localhost password to a hash we don't know the password for. + if [ -n "${MARIADB_ROOT_PASSWORD_HASH}" ]; then + mysql_note "Setting root@localhost password hash" + docker_process_sql --dont-use-mysql-root-password --binary-mode <<-EOSQL + SET @@SESSION.SQL_LOG_BIN=0; + SET PASSWORD FOR 'root'@'localhost'= '${MARIADB_ROOT_PASSWORD_HASH}'; + EOSQL + fi mysql_note "Stopping temporary server" docker_temp_server_stop diff --git a/10.11/docker-entrypoint.sh b/10.11/docker-entrypoint.sh index 48cb22c..bd674b3 100755 --- a/10.11/docker-entrypoint.sh +++ b/10.11/docker-entrypoint.sh @@ -147,9 +147,8 @@ docker_temp_server_start() { # Stop the server. When using a local socket file mariadb-admin will block until # the shutdown is complete. docker_temp_server_stop() { - if ! MYSQL_PWD=$MARIADB_ROOT_PASSWORD mariadb-admin shutdown -uroot --socket="${SOCKET}"; then - mysql_error "Unable to shut down server." - fi + kill "$MARIADB_PID" + wait "$MARIADB_PID" } # Verify that the minimally required password settings are set for new databases. @@ -291,22 +290,24 @@ docker_setup_db() { # Creates root users for non-localhost hosts local rootCreate= + local rootPasswordEscaped= + if [ -n "$MARIADB_ROOT_PASSWORD" ]; then + # Sets root password and creates root users for non-localhost hosts + rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) + fi # default root to listen for connections from anywhere if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then - if [ -n "$MARIADB_ROOT_PASSWORD" ]; then - # Sets root password and creates root users for non-localhost hosts - local rootPasswordEscaped - rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) - # no, we don't care if read finds a terminating character in this heredoc - # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + # ref "read -d ''", no, we don't care if read finds a terminating character in this heredoc + # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + if [ -n "$MARIADB_ROOT_PASSWORD_HASH" ]; then read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL else read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL fi @@ -335,12 +336,44 @@ docker_setup_db() { fi fi + local rootLocalhostPass= + if [ -z "$MARIADB_ROOT_PASSWORD_HASH" ]; then + # handle MARIADB_ROOT_PASSWORD_HASH for root@localhost after /docker-entrypoint-initdb.d + rootLocalhostPass="SET PASSWORD FOR 'root'@'localhost'= PASSWORD('${rootPasswordEscaped}');" + fi + + local createDatabase= + # Creates a custom database and user if specified + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Creating database ${MARIADB_DATABASE}" + createDatabase="CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\`;" + fi + + local createUser= + local userGrants= + if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then + mysql_note "Creating user ${MARIADB_USER}" + if [ -n "$MARIADB_PASSWORD_HASH" ]; then + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH';" + else + # SQL escape the user password, \ followed by ' + local userPasswordEscaped + userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped';" + fi + + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" + userGrants="GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%';" + fi + fi + mysql_note "Securing system users (equivalent to running mysql_secure_installation)" # tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set # --binary-mode to save us from the semi-mad users go out of their way to confuse the encoding. docker_process_sql --dont-use-mysql-root-password --database=mysql --binary-mode <<-EOSQL - -- What's done in this file shouldn't be replicated - -- or products like mysql-fabric won't work + -- Securing system users shouldn't be replicated + SET @orig_sql_log_bin= @@SESSION.SQL_LOG_BIN; SET @@SESSION.SQL_LOG_BIN=0; -- we need the SQL_MODE NO_BACKSLASH_ESCAPES mode to be clear for the password to be set SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); @@ -348,42 +381,19 @@ docker_setup_db() { DROP USER IF EXISTS root@'127.0.0.1', root@'::1'; EXECUTE IMMEDIATE CONCAT('DROP USER IF EXISTS root@\'', @@hostname,'\''); - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ; + ${rootLocalhostPass} ${rootCreate} ${mysqlAtLocalhost} ${mysqlAtLocalhostGrants} - -- pre-10.3 + -- pre-10.3 only DROP DATABASE IF EXISTS test ; + -- end of securing system users, rest of init now... + SET @@SESSION.SQL_LOG_BIN=@orig_sql_log_bin; + -- create users/databases + ${createDatabase} + ${createUser} + ${userGrants} EOSQL - - # Creates a custom database and user if specified - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Creating database ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\` ;" - fi - - if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then - mysql_note "Creating user ${MARIADB_USER}" - if [ -n "$MARIADB_PASSWORD" ]; then - # SQL escape the user password, \ followed by ' - local userPasswordEscaped - userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped'; - EOSQL_USER - else - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH'; - EOSQL_USER - fi - - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%' ;" - fi - fi } # backup the mysql database @@ -430,11 +440,8 @@ docker_mariadb_upgrade() { mariadb-upgrade --upgrade-system-tables mysql_note "Finished mariadb-upgrade" - # docker_temp_server_stop needs authentication since - # upgrade ended in FLUSH PRIVILEGES mysql_note "Stopping temporary server" - kill "$MARIADB_PID" - wait "$MARIADB_PID" + docker_temp_server_stop mysql_note "Temporary server stopped" } @@ -509,6 +516,15 @@ _main() { docker_setup_db docker_process_init_files /docker-entrypoint-initdb.d/* + # Wait until after /docker-entrypoint-initdb.d is performed before setting + # root@localhost password to a hash we don't know the password for. + if [ -n "${MARIADB_ROOT_PASSWORD_HASH}" ]; then + mysql_note "Setting root@localhost password hash" + docker_process_sql --dont-use-mysql-root-password --binary-mode <<-EOSQL + SET @@SESSION.SQL_LOG_BIN=0; + SET PASSWORD FOR 'root'@'localhost'= '${MARIADB_ROOT_PASSWORD_HASH}'; + EOSQL + fi mysql_note "Stopping temporary server" docker_temp_server_stop diff --git a/10.3/docker-entrypoint.sh b/10.3/docker-entrypoint.sh index a303a74..bacd0ef 100755 --- a/10.3/docker-entrypoint.sh +++ b/10.3/docker-entrypoint.sh @@ -147,9 +147,8 @@ docker_temp_server_start() { # Stop the server. When using a local socket file mysqladmin will block until # the shutdown is complete. docker_temp_server_stop() { - if ! MYSQL_PWD=$MARIADB_ROOT_PASSWORD mysqladmin shutdown -uroot --socket="${SOCKET}"; then - mysql_error "Unable to shut down server." - fi + kill "$MARIADB_PID" + wait "$MARIADB_PID" } # Verify that the minimally required password settings are set for new databases. @@ -291,22 +290,24 @@ docker_setup_db() { # Creates root users for non-localhost hosts local rootCreate= + local rootPasswordEscaped= + if [ -n "$MARIADB_ROOT_PASSWORD" ]; then + # Sets root password and creates root users for non-localhost hosts + rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) + fi # default root to listen for connections from anywhere if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then - if [ -n "$MARIADB_ROOT_PASSWORD" ]; then - # Sets root password and creates root users for non-localhost hosts - local rootPasswordEscaped - rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) - # no, we don't care if read finds a terminating character in this heredoc - # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + # ref "read -d ''", no, we don't care if read finds a terminating character in this heredoc + # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + if [ -n "$MARIADB_ROOT_PASSWORD_HASH" ]; then read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL else read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL fi @@ -335,12 +336,44 @@ docker_setup_db() { fi fi + local rootLocalhostPass= + if [ -z "$MARIADB_ROOT_PASSWORD_HASH" ]; then + # handle MARIADB_ROOT_PASSWORD_HASH for root@localhost after /docker-entrypoint-initdb.d + rootLocalhostPass="SET PASSWORD FOR 'root'@'localhost'= PASSWORD('${rootPasswordEscaped}');" + fi + + local createDatabase= + # Creates a custom database and user if specified + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Creating database ${MARIADB_DATABASE}" + createDatabase="CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\`;" + fi + + local createUser= + local userGrants= + if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then + mysql_note "Creating user ${MARIADB_USER}" + if [ -n "$MARIADB_PASSWORD_HASH" ]; then + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH';" + else + # SQL escape the user password, \ followed by ' + local userPasswordEscaped + userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped';" + fi + + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" + userGrants="GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%';" + fi + fi + mysql_note "Securing system users (equivalent to running mysql_secure_installation)" # tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set # --binary-mode to save us from the semi-mad users go out of their way to confuse the encoding. docker_process_sql --dont-use-mysql-root-password --database=mysql --binary-mode <<-EOSQL - -- What's done in this file shouldn't be replicated - -- or products like mysql-fabric won't work + -- Securing system users shouldn't be replicated + SET @orig_sql_log_bin= @@SESSION.SQL_LOG_BIN; SET @@SESSION.SQL_LOG_BIN=0; -- we need the SQL_MODE NO_BACKSLASH_ESCAPES mode to be clear for the password to be set SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); @@ -348,42 +381,19 @@ docker_setup_db() { DROP USER IF EXISTS root@'127.0.0.1', root@'::1'; EXECUTE IMMEDIATE CONCAT('DROP USER IF EXISTS root@\'', @@hostname,'\''); - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ; + ${rootLocalhostPass} ${rootCreate} ${mysqlAtLocalhost} ${mysqlAtLocalhostGrants} - -- pre-10.3 + -- pre-10.3 only DROP DATABASE IF EXISTS test ; + -- end of securing system users, rest of init now... + SET @@SESSION.SQL_LOG_BIN=@orig_sql_log_bin; + -- create users/databases + ${createDatabase} + ${createUser} + ${userGrants} EOSQL - - # Creates a custom database and user if specified - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Creating database ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\` ;" - fi - - if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then - mysql_note "Creating user ${MARIADB_USER}" - if [ -n "$MARIADB_PASSWORD" ]; then - # SQL escape the user password, \ followed by ' - local userPasswordEscaped - userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped'; - EOSQL_USER - else - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH'; - EOSQL_USER - fi - - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%' ;" - fi - fi } # backup the mysql database @@ -430,11 +440,8 @@ docker_mariadb_upgrade() { mysql_upgrade --upgrade-system-tables mysql_note "Finished mariadb-upgrade" - # docker_temp_server_stop needs authentication since - # upgrade ended in FLUSH PRIVILEGES mysql_note "Stopping temporary server" - kill "$MARIADB_PID" - wait "$MARIADB_PID" + docker_temp_server_stop mysql_note "Temporary server stopped" } @@ -509,6 +516,15 @@ _main() { docker_setup_db docker_process_init_files /docker-entrypoint-initdb.d/* + # Wait until after /docker-entrypoint-initdb.d is performed before setting + # root@localhost password to a hash we don't know the password for. + if [ -n "${MARIADB_ROOT_PASSWORD_HASH}" ]; then + mysql_note "Setting root@localhost password hash" + docker_process_sql --dont-use-mysql-root-password --binary-mode <<-EOSQL + SET @@SESSION.SQL_LOG_BIN=0; + SET PASSWORD FOR 'root'@'localhost'= '${MARIADB_ROOT_PASSWORD_HASH}'; + EOSQL + fi mysql_note "Stopping temporary server" docker_temp_server_stop diff --git a/10.4/docker-entrypoint.sh b/10.4/docker-entrypoint.sh index a303a74..bacd0ef 100755 --- a/10.4/docker-entrypoint.sh +++ b/10.4/docker-entrypoint.sh @@ -147,9 +147,8 @@ docker_temp_server_start() { # Stop the server. When using a local socket file mysqladmin will block until # the shutdown is complete. docker_temp_server_stop() { - if ! MYSQL_PWD=$MARIADB_ROOT_PASSWORD mysqladmin shutdown -uroot --socket="${SOCKET}"; then - mysql_error "Unable to shut down server." - fi + kill "$MARIADB_PID" + wait "$MARIADB_PID" } # Verify that the minimally required password settings are set for new databases. @@ -291,22 +290,24 @@ docker_setup_db() { # Creates root users for non-localhost hosts local rootCreate= + local rootPasswordEscaped= + if [ -n "$MARIADB_ROOT_PASSWORD" ]; then + # Sets root password and creates root users for non-localhost hosts + rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) + fi # default root to listen for connections from anywhere if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then - if [ -n "$MARIADB_ROOT_PASSWORD" ]; then - # Sets root password and creates root users for non-localhost hosts - local rootPasswordEscaped - rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) - # no, we don't care if read finds a terminating character in this heredoc - # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + # ref "read -d ''", no, we don't care if read finds a terminating character in this heredoc + # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + if [ -n "$MARIADB_ROOT_PASSWORD_HASH" ]; then read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL else read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL fi @@ -335,12 +336,44 @@ docker_setup_db() { fi fi + local rootLocalhostPass= + if [ -z "$MARIADB_ROOT_PASSWORD_HASH" ]; then + # handle MARIADB_ROOT_PASSWORD_HASH for root@localhost after /docker-entrypoint-initdb.d + rootLocalhostPass="SET PASSWORD FOR 'root'@'localhost'= PASSWORD('${rootPasswordEscaped}');" + fi + + local createDatabase= + # Creates a custom database and user if specified + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Creating database ${MARIADB_DATABASE}" + createDatabase="CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\`;" + fi + + local createUser= + local userGrants= + if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then + mysql_note "Creating user ${MARIADB_USER}" + if [ -n "$MARIADB_PASSWORD_HASH" ]; then + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH';" + else + # SQL escape the user password, \ followed by ' + local userPasswordEscaped + userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped';" + fi + + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" + userGrants="GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%';" + fi + fi + mysql_note "Securing system users (equivalent to running mysql_secure_installation)" # tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set # --binary-mode to save us from the semi-mad users go out of their way to confuse the encoding. docker_process_sql --dont-use-mysql-root-password --database=mysql --binary-mode <<-EOSQL - -- What's done in this file shouldn't be replicated - -- or products like mysql-fabric won't work + -- Securing system users shouldn't be replicated + SET @orig_sql_log_bin= @@SESSION.SQL_LOG_BIN; SET @@SESSION.SQL_LOG_BIN=0; -- we need the SQL_MODE NO_BACKSLASH_ESCAPES mode to be clear for the password to be set SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); @@ -348,42 +381,19 @@ docker_setup_db() { DROP USER IF EXISTS root@'127.0.0.1', root@'::1'; EXECUTE IMMEDIATE CONCAT('DROP USER IF EXISTS root@\'', @@hostname,'\''); - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ; + ${rootLocalhostPass} ${rootCreate} ${mysqlAtLocalhost} ${mysqlAtLocalhostGrants} - -- pre-10.3 + -- pre-10.3 only DROP DATABASE IF EXISTS test ; + -- end of securing system users, rest of init now... + SET @@SESSION.SQL_LOG_BIN=@orig_sql_log_bin; + -- create users/databases + ${createDatabase} + ${createUser} + ${userGrants} EOSQL - - # Creates a custom database and user if specified - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Creating database ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\` ;" - fi - - if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then - mysql_note "Creating user ${MARIADB_USER}" - if [ -n "$MARIADB_PASSWORD" ]; then - # SQL escape the user password, \ followed by ' - local userPasswordEscaped - userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped'; - EOSQL_USER - else - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH'; - EOSQL_USER - fi - - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%' ;" - fi - fi } # backup the mysql database @@ -430,11 +440,8 @@ docker_mariadb_upgrade() { mysql_upgrade --upgrade-system-tables mysql_note "Finished mariadb-upgrade" - # docker_temp_server_stop needs authentication since - # upgrade ended in FLUSH PRIVILEGES mysql_note "Stopping temporary server" - kill "$MARIADB_PID" - wait "$MARIADB_PID" + docker_temp_server_stop mysql_note "Temporary server stopped" } @@ -509,6 +516,15 @@ _main() { docker_setup_db docker_process_init_files /docker-entrypoint-initdb.d/* + # Wait until after /docker-entrypoint-initdb.d is performed before setting + # root@localhost password to a hash we don't know the password for. + if [ -n "${MARIADB_ROOT_PASSWORD_HASH}" ]; then + mysql_note "Setting root@localhost password hash" + docker_process_sql --dont-use-mysql-root-password --binary-mode <<-EOSQL + SET @@SESSION.SQL_LOG_BIN=0; + SET PASSWORD FOR 'root'@'localhost'= '${MARIADB_ROOT_PASSWORD_HASH}'; + EOSQL + fi mysql_note "Stopping temporary server" docker_temp_server_stop diff --git a/10.5/docker-entrypoint.sh b/10.5/docker-entrypoint.sh index a303a74..bacd0ef 100755 --- a/10.5/docker-entrypoint.sh +++ b/10.5/docker-entrypoint.sh @@ -147,9 +147,8 @@ docker_temp_server_start() { # Stop the server. When using a local socket file mysqladmin will block until # the shutdown is complete. docker_temp_server_stop() { - if ! MYSQL_PWD=$MARIADB_ROOT_PASSWORD mysqladmin shutdown -uroot --socket="${SOCKET}"; then - mysql_error "Unable to shut down server." - fi + kill "$MARIADB_PID" + wait "$MARIADB_PID" } # Verify that the minimally required password settings are set for new databases. @@ -291,22 +290,24 @@ docker_setup_db() { # Creates root users for non-localhost hosts local rootCreate= + local rootPasswordEscaped= + if [ -n "$MARIADB_ROOT_PASSWORD" ]; then + # Sets root password and creates root users for non-localhost hosts + rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) + fi # default root to listen for connections from anywhere if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then - if [ -n "$MARIADB_ROOT_PASSWORD" ]; then - # Sets root password and creates root users for non-localhost hosts - local rootPasswordEscaped - rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) - # no, we don't care if read finds a terminating character in this heredoc - # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + # ref "read -d ''", no, we don't care if read finds a terminating character in this heredoc + # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + if [ -n "$MARIADB_ROOT_PASSWORD_HASH" ]; then read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL else read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL fi @@ -335,12 +336,44 @@ docker_setup_db() { fi fi + local rootLocalhostPass= + if [ -z "$MARIADB_ROOT_PASSWORD_HASH" ]; then + # handle MARIADB_ROOT_PASSWORD_HASH for root@localhost after /docker-entrypoint-initdb.d + rootLocalhostPass="SET PASSWORD FOR 'root'@'localhost'= PASSWORD('${rootPasswordEscaped}');" + fi + + local createDatabase= + # Creates a custom database and user if specified + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Creating database ${MARIADB_DATABASE}" + createDatabase="CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\`;" + fi + + local createUser= + local userGrants= + if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then + mysql_note "Creating user ${MARIADB_USER}" + if [ -n "$MARIADB_PASSWORD_HASH" ]; then + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH';" + else + # SQL escape the user password, \ followed by ' + local userPasswordEscaped + userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped';" + fi + + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" + userGrants="GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%';" + fi + fi + mysql_note "Securing system users (equivalent to running mysql_secure_installation)" # tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set # --binary-mode to save us from the semi-mad users go out of their way to confuse the encoding. docker_process_sql --dont-use-mysql-root-password --database=mysql --binary-mode <<-EOSQL - -- What's done in this file shouldn't be replicated - -- or products like mysql-fabric won't work + -- Securing system users shouldn't be replicated + SET @orig_sql_log_bin= @@SESSION.SQL_LOG_BIN; SET @@SESSION.SQL_LOG_BIN=0; -- we need the SQL_MODE NO_BACKSLASH_ESCAPES mode to be clear for the password to be set SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); @@ -348,42 +381,19 @@ docker_setup_db() { DROP USER IF EXISTS root@'127.0.0.1', root@'::1'; EXECUTE IMMEDIATE CONCAT('DROP USER IF EXISTS root@\'', @@hostname,'\''); - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ; + ${rootLocalhostPass} ${rootCreate} ${mysqlAtLocalhost} ${mysqlAtLocalhostGrants} - -- pre-10.3 + -- pre-10.3 only DROP DATABASE IF EXISTS test ; + -- end of securing system users, rest of init now... + SET @@SESSION.SQL_LOG_BIN=@orig_sql_log_bin; + -- create users/databases + ${createDatabase} + ${createUser} + ${userGrants} EOSQL - - # Creates a custom database and user if specified - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Creating database ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\` ;" - fi - - if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then - mysql_note "Creating user ${MARIADB_USER}" - if [ -n "$MARIADB_PASSWORD" ]; then - # SQL escape the user password, \ followed by ' - local userPasswordEscaped - userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped'; - EOSQL_USER - else - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH'; - EOSQL_USER - fi - - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%' ;" - fi - fi } # backup the mysql database @@ -430,11 +440,8 @@ docker_mariadb_upgrade() { mysql_upgrade --upgrade-system-tables mysql_note "Finished mariadb-upgrade" - # docker_temp_server_stop needs authentication since - # upgrade ended in FLUSH PRIVILEGES mysql_note "Stopping temporary server" - kill "$MARIADB_PID" - wait "$MARIADB_PID" + docker_temp_server_stop mysql_note "Temporary server stopped" } @@ -509,6 +516,15 @@ _main() { docker_setup_db docker_process_init_files /docker-entrypoint-initdb.d/* + # Wait until after /docker-entrypoint-initdb.d is performed before setting + # root@localhost password to a hash we don't know the password for. + if [ -n "${MARIADB_ROOT_PASSWORD_HASH}" ]; then + mysql_note "Setting root@localhost password hash" + docker_process_sql --dont-use-mysql-root-password --binary-mode <<-EOSQL + SET @@SESSION.SQL_LOG_BIN=0; + SET PASSWORD FOR 'root'@'localhost'= '${MARIADB_ROOT_PASSWORD_HASH}'; + EOSQL + fi mysql_note "Stopping temporary server" docker_temp_server_stop diff --git a/10.6/docker-entrypoint.sh b/10.6/docker-entrypoint.sh index 48cb22c..bd674b3 100755 --- a/10.6/docker-entrypoint.sh +++ b/10.6/docker-entrypoint.sh @@ -147,9 +147,8 @@ docker_temp_server_start() { # Stop the server. When using a local socket file mariadb-admin will block until # the shutdown is complete. docker_temp_server_stop() { - if ! MYSQL_PWD=$MARIADB_ROOT_PASSWORD mariadb-admin shutdown -uroot --socket="${SOCKET}"; then - mysql_error "Unable to shut down server." - fi + kill "$MARIADB_PID" + wait "$MARIADB_PID" } # Verify that the minimally required password settings are set for new databases. @@ -291,22 +290,24 @@ docker_setup_db() { # Creates root users for non-localhost hosts local rootCreate= + local rootPasswordEscaped= + if [ -n "$MARIADB_ROOT_PASSWORD" ]; then + # Sets root password and creates root users for non-localhost hosts + rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) + fi # default root to listen for connections from anywhere if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then - if [ -n "$MARIADB_ROOT_PASSWORD" ]; then - # Sets root password and creates root users for non-localhost hosts - local rootPasswordEscaped - rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) - # no, we don't care if read finds a terminating character in this heredoc - # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + # ref "read -d ''", no, we don't care if read finds a terminating character in this heredoc + # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + if [ -n "$MARIADB_ROOT_PASSWORD_HASH" ]; then read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL else read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL fi @@ -335,12 +336,44 @@ docker_setup_db() { fi fi + local rootLocalhostPass= + if [ -z "$MARIADB_ROOT_PASSWORD_HASH" ]; then + # handle MARIADB_ROOT_PASSWORD_HASH for root@localhost after /docker-entrypoint-initdb.d + rootLocalhostPass="SET PASSWORD FOR 'root'@'localhost'= PASSWORD('${rootPasswordEscaped}');" + fi + + local createDatabase= + # Creates a custom database and user if specified + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Creating database ${MARIADB_DATABASE}" + createDatabase="CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\`;" + fi + + local createUser= + local userGrants= + if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then + mysql_note "Creating user ${MARIADB_USER}" + if [ -n "$MARIADB_PASSWORD_HASH" ]; then + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH';" + else + # SQL escape the user password, \ followed by ' + local userPasswordEscaped + userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped';" + fi + + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" + userGrants="GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%';" + fi + fi + mysql_note "Securing system users (equivalent to running mysql_secure_installation)" # tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set # --binary-mode to save us from the semi-mad users go out of their way to confuse the encoding. docker_process_sql --dont-use-mysql-root-password --database=mysql --binary-mode <<-EOSQL - -- What's done in this file shouldn't be replicated - -- or products like mysql-fabric won't work + -- Securing system users shouldn't be replicated + SET @orig_sql_log_bin= @@SESSION.SQL_LOG_BIN; SET @@SESSION.SQL_LOG_BIN=0; -- we need the SQL_MODE NO_BACKSLASH_ESCAPES mode to be clear for the password to be set SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); @@ -348,42 +381,19 @@ docker_setup_db() { DROP USER IF EXISTS root@'127.0.0.1', root@'::1'; EXECUTE IMMEDIATE CONCAT('DROP USER IF EXISTS root@\'', @@hostname,'\''); - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ; + ${rootLocalhostPass} ${rootCreate} ${mysqlAtLocalhost} ${mysqlAtLocalhostGrants} - -- pre-10.3 + -- pre-10.3 only DROP DATABASE IF EXISTS test ; + -- end of securing system users, rest of init now... + SET @@SESSION.SQL_LOG_BIN=@orig_sql_log_bin; + -- create users/databases + ${createDatabase} + ${createUser} + ${userGrants} EOSQL - - # Creates a custom database and user if specified - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Creating database ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\` ;" - fi - - if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then - mysql_note "Creating user ${MARIADB_USER}" - if [ -n "$MARIADB_PASSWORD" ]; then - # SQL escape the user password, \ followed by ' - local userPasswordEscaped - userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped'; - EOSQL_USER - else - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH'; - EOSQL_USER - fi - - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%' ;" - fi - fi } # backup the mysql database @@ -430,11 +440,8 @@ docker_mariadb_upgrade() { mariadb-upgrade --upgrade-system-tables mysql_note "Finished mariadb-upgrade" - # docker_temp_server_stop needs authentication since - # upgrade ended in FLUSH PRIVILEGES mysql_note "Stopping temporary server" - kill "$MARIADB_PID" - wait "$MARIADB_PID" + docker_temp_server_stop mysql_note "Temporary server stopped" } @@ -509,6 +516,15 @@ _main() { docker_setup_db docker_process_init_files /docker-entrypoint-initdb.d/* + # Wait until after /docker-entrypoint-initdb.d is performed before setting + # root@localhost password to a hash we don't know the password for. + if [ -n "${MARIADB_ROOT_PASSWORD_HASH}" ]; then + mysql_note "Setting root@localhost password hash" + docker_process_sql --dont-use-mysql-root-password --binary-mode <<-EOSQL + SET @@SESSION.SQL_LOG_BIN=0; + SET PASSWORD FOR 'root'@'localhost'= '${MARIADB_ROOT_PASSWORD_HASH}'; + EOSQL + fi mysql_note "Stopping temporary server" docker_temp_server_stop diff --git a/10.7/docker-entrypoint.sh b/10.7/docker-entrypoint.sh index 48cb22c..bd674b3 100755 --- a/10.7/docker-entrypoint.sh +++ b/10.7/docker-entrypoint.sh @@ -147,9 +147,8 @@ docker_temp_server_start() { # Stop the server. When using a local socket file mariadb-admin will block until # the shutdown is complete. docker_temp_server_stop() { - if ! MYSQL_PWD=$MARIADB_ROOT_PASSWORD mariadb-admin shutdown -uroot --socket="${SOCKET}"; then - mysql_error "Unable to shut down server." - fi + kill "$MARIADB_PID" + wait "$MARIADB_PID" } # Verify that the minimally required password settings are set for new databases. @@ -291,22 +290,24 @@ docker_setup_db() { # Creates root users for non-localhost hosts local rootCreate= + local rootPasswordEscaped= + if [ -n "$MARIADB_ROOT_PASSWORD" ]; then + # Sets root password and creates root users for non-localhost hosts + rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) + fi # default root to listen for connections from anywhere if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then - if [ -n "$MARIADB_ROOT_PASSWORD" ]; then - # Sets root password and creates root users for non-localhost hosts - local rootPasswordEscaped - rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) - # no, we don't care if read finds a terminating character in this heredoc - # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + # ref "read -d ''", no, we don't care if read finds a terminating character in this heredoc + # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + if [ -n "$MARIADB_ROOT_PASSWORD_HASH" ]; then read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL else read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL fi @@ -335,12 +336,44 @@ docker_setup_db() { fi fi + local rootLocalhostPass= + if [ -z "$MARIADB_ROOT_PASSWORD_HASH" ]; then + # handle MARIADB_ROOT_PASSWORD_HASH for root@localhost after /docker-entrypoint-initdb.d + rootLocalhostPass="SET PASSWORD FOR 'root'@'localhost'= PASSWORD('${rootPasswordEscaped}');" + fi + + local createDatabase= + # Creates a custom database and user if specified + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Creating database ${MARIADB_DATABASE}" + createDatabase="CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\`;" + fi + + local createUser= + local userGrants= + if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then + mysql_note "Creating user ${MARIADB_USER}" + if [ -n "$MARIADB_PASSWORD_HASH" ]; then + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH';" + else + # SQL escape the user password, \ followed by ' + local userPasswordEscaped + userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped';" + fi + + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" + userGrants="GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%';" + fi + fi + mysql_note "Securing system users (equivalent to running mysql_secure_installation)" # tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set # --binary-mode to save us from the semi-mad users go out of their way to confuse the encoding. docker_process_sql --dont-use-mysql-root-password --database=mysql --binary-mode <<-EOSQL - -- What's done in this file shouldn't be replicated - -- or products like mysql-fabric won't work + -- Securing system users shouldn't be replicated + SET @orig_sql_log_bin= @@SESSION.SQL_LOG_BIN; SET @@SESSION.SQL_LOG_BIN=0; -- we need the SQL_MODE NO_BACKSLASH_ESCAPES mode to be clear for the password to be set SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); @@ -348,42 +381,19 @@ docker_setup_db() { DROP USER IF EXISTS root@'127.0.0.1', root@'::1'; EXECUTE IMMEDIATE CONCAT('DROP USER IF EXISTS root@\'', @@hostname,'\''); - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ; + ${rootLocalhostPass} ${rootCreate} ${mysqlAtLocalhost} ${mysqlAtLocalhostGrants} - -- pre-10.3 + -- pre-10.3 only DROP DATABASE IF EXISTS test ; + -- end of securing system users, rest of init now... + SET @@SESSION.SQL_LOG_BIN=@orig_sql_log_bin; + -- create users/databases + ${createDatabase} + ${createUser} + ${userGrants} EOSQL - - # Creates a custom database and user if specified - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Creating database ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\` ;" - fi - - if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then - mysql_note "Creating user ${MARIADB_USER}" - if [ -n "$MARIADB_PASSWORD" ]; then - # SQL escape the user password, \ followed by ' - local userPasswordEscaped - userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped'; - EOSQL_USER - else - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH'; - EOSQL_USER - fi - - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%' ;" - fi - fi } # backup the mysql database @@ -430,11 +440,8 @@ docker_mariadb_upgrade() { mariadb-upgrade --upgrade-system-tables mysql_note "Finished mariadb-upgrade" - # docker_temp_server_stop needs authentication since - # upgrade ended in FLUSH PRIVILEGES mysql_note "Stopping temporary server" - kill "$MARIADB_PID" - wait "$MARIADB_PID" + docker_temp_server_stop mysql_note "Temporary server stopped" } @@ -509,6 +516,15 @@ _main() { docker_setup_db docker_process_init_files /docker-entrypoint-initdb.d/* + # Wait until after /docker-entrypoint-initdb.d is performed before setting + # root@localhost password to a hash we don't know the password for. + if [ -n "${MARIADB_ROOT_PASSWORD_HASH}" ]; then + mysql_note "Setting root@localhost password hash" + docker_process_sql --dont-use-mysql-root-password --binary-mode <<-EOSQL + SET @@SESSION.SQL_LOG_BIN=0; + SET PASSWORD FOR 'root'@'localhost'= '${MARIADB_ROOT_PASSWORD_HASH}'; + EOSQL + fi mysql_note "Stopping temporary server" docker_temp_server_stop diff --git a/10.8/docker-entrypoint.sh b/10.8/docker-entrypoint.sh index 48cb22c..bd674b3 100755 --- a/10.8/docker-entrypoint.sh +++ b/10.8/docker-entrypoint.sh @@ -147,9 +147,8 @@ docker_temp_server_start() { # Stop the server. When using a local socket file mariadb-admin will block until # the shutdown is complete. docker_temp_server_stop() { - if ! MYSQL_PWD=$MARIADB_ROOT_PASSWORD mariadb-admin shutdown -uroot --socket="${SOCKET}"; then - mysql_error "Unable to shut down server." - fi + kill "$MARIADB_PID" + wait "$MARIADB_PID" } # Verify that the minimally required password settings are set for new databases. @@ -291,22 +290,24 @@ docker_setup_db() { # Creates root users for non-localhost hosts local rootCreate= + local rootPasswordEscaped= + if [ -n "$MARIADB_ROOT_PASSWORD" ]; then + # Sets root password and creates root users for non-localhost hosts + rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) + fi # default root to listen for connections from anywhere if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then - if [ -n "$MARIADB_ROOT_PASSWORD" ]; then - # Sets root password and creates root users for non-localhost hosts - local rootPasswordEscaped - rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) - # no, we don't care if read finds a terminating character in this heredoc - # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + # ref "read -d ''", no, we don't care if read finds a terminating character in this heredoc + # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + if [ -n "$MARIADB_ROOT_PASSWORD_HASH" ]; then read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL else read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL fi @@ -335,12 +336,44 @@ docker_setup_db() { fi fi + local rootLocalhostPass= + if [ -z "$MARIADB_ROOT_PASSWORD_HASH" ]; then + # handle MARIADB_ROOT_PASSWORD_HASH for root@localhost after /docker-entrypoint-initdb.d + rootLocalhostPass="SET PASSWORD FOR 'root'@'localhost'= PASSWORD('${rootPasswordEscaped}');" + fi + + local createDatabase= + # Creates a custom database and user if specified + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Creating database ${MARIADB_DATABASE}" + createDatabase="CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\`;" + fi + + local createUser= + local userGrants= + if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then + mysql_note "Creating user ${MARIADB_USER}" + if [ -n "$MARIADB_PASSWORD_HASH" ]; then + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH';" + else + # SQL escape the user password, \ followed by ' + local userPasswordEscaped + userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped';" + fi + + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" + userGrants="GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%';" + fi + fi + mysql_note "Securing system users (equivalent to running mysql_secure_installation)" # tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set # --binary-mode to save us from the semi-mad users go out of their way to confuse the encoding. docker_process_sql --dont-use-mysql-root-password --database=mysql --binary-mode <<-EOSQL - -- What's done in this file shouldn't be replicated - -- or products like mysql-fabric won't work + -- Securing system users shouldn't be replicated + SET @orig_sql_log_bin= @@SESSION.SQL_LOG_BIN; SET @@SESSION.SQL_LOG_BIN=0; -- we need the SQL_MODE NO_BACKSLASH_ESCAPES mode to be clear for the password to be set SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); @@ -348,42 +381,19 @@ docker_setup_db() { DROP USER IF EXISTS root@'127.0.0.1', root@'::1'; EXECUTE IMMEDIATE CONCAT('DROP USER IF EXISTS root@\'', @@hostname,'\''); - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ; + ${rootLocalhostPass} ${rootCreate} ${mysqlAtLocalhost} ${mysqlAtLocalhostGrants} - -- pre-10.3 + -- pre-10.3 only DROP DATABASE IF EXISTS test ; + -- end of securing system users, rest of init now... + SET @@SESSION.SQL_LOG_BIN=@orig_sql_log_bin; + -- create users/databases + ${createDatabase} + ${createUser} + ${userGrants} EOSQL - - # Creates a custom database and user if specified - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Creating database ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\` ;" - fi - - if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then - mysql_note "Creating user ${MARIADB_USER}" - if [ -n "$MARIADB_PASSWORD" ]; then - # SQL escape the user password, \ followed by ' - local userPasswordEscaped - userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped'; - EOSQL_USER - else - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH'; - EOSQL_USER - fi - - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%' ;" - fi - fi } # backup the mysql database @@ -430,11 +440,8 @@ docker_mariadb_upgrade() { mariadb-upgrade --upgrade-system-tables mysql_note "Finished mariadb-upgrade" - # docker_temp_server_stop needs authentication since - # upgrade ended in FLUSH PRIVILEGES mysql_note "Stopping temporary server" - kill "$MARIADB_PID" - wait "$MARIADB_PID" + docker_temp_server_stop mysql_note "Temporary server stopped" } @@ -509,6 +516,15 @@ _main() { docker_setup_db docker_process_init_files /docker-entrypoint-initdb.d/* + # Wait until after /docker-entrypoint-initdb.d is performed before setting + # root@localhost password to a hash we don't know the password for. + if [ -n "${MARIADB_ROOT_PASSWORD_HASH}" ]; then + mysql_note "Setting root@localhost password hash" + docker_process_sql --dont-use-mysql-root-password --binary-mode <<-EOSQL + SET @@SESSION.SQL_LOG_BIN=0; + SET PASSWORD FOR 'root'@'localhost'= '${MARIADB_ROOT_PASSWORD_HASH}'; + EOSQL + fi mysql_note "Stopping temporary server" docker_temp_server_stop diff --git a/10.9/docker-entrypoint.sh b/10.9/docker-entrypoint.sh index 48cb22c..bd674b3 100755 --- a/10.9/docker-entrypoint.sh +++ b/10.9/docker-entrypoint.sh @@ -147,9 +147,8 @@ docker_temp_server_start() { # Stop the server. When using a local socket file mariadb-admin will block until # the shutdown is complete. docker_temp_server_stop() { - if ! MYSQL_PWD=$MARIADB_ROOT_PASSWORD mariadb-admin shutdown -uroot --socket="${SOCKET}"; then - mysql_error "Unable to shut down server." - fi + kill "$MARIADB_PID" + wait "$MARIADB_PID" } # Verify that the minimally required password settings are set for new databases. @@ -291,22 +290,24 @@ docker_setup_db() { # Creates root users for non-localhost hosts local rootCreate= + local rootPasswordEscaped= + if [ -n "$MARIADB_ROOT_PASSWORD" ]; then + # Sets root password and creates root users for non-localhost hosts + rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) + fi # default root to listen for connections from anywhere if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then - if [ -n "$MARIADB_ROOT_PASSWORD" ]; then - # Sets root password and creates root users for non-localhost hosts - local rootPasswordEscaped - rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) - # no, we don't care if read finds a terminating character in this heredoc - # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + # ref "read -d ''", no, we don't care if read finds a terminating character in this heredoc + # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + if [ -n "$MARIADB_ROOT_PASSWORD_HASH" ]; then read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL else read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL fi @@ -335,12 +336,44 @@ docker_setup_db() { fi fi + local rootLocalhostPass= + if [ -z "$MARIADB_ROOT_PASSWORD_HASH" ]; then + # handle MARIADB_ROOT_PASSWORD_HASH for root@localhost after /docker-entrypoint-initdb.d + rootLocalhostPass="SET PASSWORD FOR 'root'@'localhost'= PASSWORD('${rootPasswordEscaped}');" + fi + + local createDatabase= + # Creates a custom database and user if specified + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Creating database ${MARIADB_DATABASE}" + createDatabase="CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\`;" + fi + + local createUser= + local userGrants= + if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then + mysql_note "Creating user ${MARIADB_USER}" + if [ -n "$MARIADB_PASSWORD_HASH" ]; then + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH';" + else + # SQL escape the user password, \ followed by ' + local userPasswordEscaped + userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped';" + fi + + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" + userGrants="GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%';" + fi + fi + mysql_note "Securing system users (equivalent to running mysql_secure_installation)" # tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set # --binary-mode to save us from the semi-mad users go out of their way to confuse the encoding. docker_process_sql --dont-use-mysql-root-password --database=mysql --binary-mode <<-EOSQL - -- What's done in this file shouldn't be replicated - -- or products like mysql-fabric won't work + -- Securing system users shouldn't be replicated + SET @orig_sql_log_bin= @@SESSION.SQL_LOG_BIN; SET @@SESSION.SQL_LOG_BIN=0; -- we need the SQL_MODE NO_BACKSLASH_ESCAPES mode to be clear for the password to be set SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); @@ -348,42 +381,19 @@ docker_setup_db() { DROP USER IF EXISTS root@'127.0.0.1', root@'::1'; EXECUTE IMMEDIATE CONCAT('DROP USER IF EXISTS root@\'', @@hostname,'\''); - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ; + ${rootLocalhostPass} ${rootCreate} ${mysqlAtLocalhost} ${mysqlAtLocalhostGrants} - -- pre-10.3 + -- pre-10.3 only DROP DATABASE IF EXISTS test ; + -- end of securing system users, rest of init now... + SET @@SESSION.SQL_LOG_BIN=@orig_sql_log_bin; + -- create users/databases + ${createDatabase} + ${createUser} + ${userGrants} EOSQL - - # Creates a custom database and user if specified - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Creating database ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\` ;" - fi - - if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then - mysql_note "Creating user ${MARIADB_USER}" - if [ -n "$MARIADB_PASSWORD" ]; then - # SQL escape the user password, \ followed by ' - local userPasswordEscaped - userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped'; - EOSQL_USER - else - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH'; - EOSQL_USER - fi - - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%' ;" - fi - fi } # backup the mysql database @@ -430,11 +440,8 @@ docker_mariadb_upgrade() { mariadb-upgrade --upgrade-system-tables mysql_note "Finished mariadb-upgrade" - # docker_temp_server_stop needs authentication since - # upgrade ended in FLUSH PRIVILEGES mysql_note "Stopping temporary server" - kill "$MARIADB_PID" - wait "$MARIADB_PID" + docker_temp_server_stop mysql_note "Temporary server stopped" } @@ -509,6 +516,15 @@ _main() { docker_setup_db docker_process_init_files /docker-entrypoint-initdb.d/* + # Wait until after /docker-entrypoint-initdb.d is performed before setting + # root@localhost password to a hash we don't know the password for. + if [ -n "${MARIADB_ROOT_PASSWORD_HASH}" ]; then + mysql_note "Setting root@localhost password hash" + docker_process_sql --dont-use-mysql-root-password --binary-mode <<-EOSQL + SET @@SESSION.SQL_LOG_BIN=0; + SET PASSWORD FOR 'root'@'localhost'= '${MARIADB_ROOT_PASSWORD_HASH}'; + EOSQL + fi mysql_note "Stopping temporary server" docker_temp_server_stop diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index a303a74..bacd0ef 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -147,9 +147,8 @@ docker_temp_server_start() { # Stop the server. When using a local socket file mysqladmin will block until # the shutdown is complete. docker_temp_server_stop() { - if ! MYSQL_PWD=$MARIADB_ROOT_PASSWORD mysqladmin shutdown -uroot --socket="${SOCKET}"; then - mysql_error "Unable to shut down server." - fi + kill "$MARIADB_PID" + wait "$MARIADB_PID" } # Verify that the minimally required password settings are set for new databases. @@ -291,22 +290,24 @@ docker_setup_db() { # Creates root users for non-localhost hosts local rootCreate= + local rootPasswordEscaped= + if [ -n "$MARIADB_ROOT_PASSWORD" ]; then + # Sets root password and creates root users for non-localhost hosts + rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) + fi # default root to listen for connections from anywhere if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then - if [ -n "$MARIADB_ROOT_PASSWORD" ]; then - # Sets root password and creates root users for non-localhost hosts - local rootPasswordEscaped - rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) - # no, we don't care if read finds a terminating character in this heredoc - # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + # ref "read -d ''", no, we don't care if read finds a terminating character in this heredoc + # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 + if [ -n "$MARIADB_ROOT_PASSWORD_HASH" ]; then read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL else read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY PASSWORD '${MARIADB_ROOT_PASSWORD_HASH}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL fi @@ -335,12 +336,44 @@ docker_setup_db() { fi fi + local rootLocalhostPass= + if [ -z "$MARIADB_ROOT_PASSWORD_HASH" ]; then + # handle MARIADB_ROOT_PASSWORD_HASH for root@localhost after /docker-entrypoint-initdb.d + rootLocalhostPass="SET PASSWORD FOR 'root'@'localhost'= PASSWORD('${rootPasswordEscaped}');" + fi + + local createDatabase= + # Creates a custom database and user if specified + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Creating database ${MARIADB_DATABASE}" + createDatabase="CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\`;" + fi + + local createUser= + local userGrants= + if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then + mysql_note "Creating user ${MARIADB_USER}" + if [ -n "$MARIADB_PASSWORD_HASH" ]; then + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH';" + else + # SQL escape the user password, \ followed by ' + local userPasswordEscaped + userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) + createUser="CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped';" + fi + + if [ -n "$MARIADB_DATABASE" ]; then + mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" + userGrants="GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%';" + fi + fi + mysql_note "Securing system users (equivalent to running mysql_secure_installation)" # tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set # --binary-mode to save us from the semi-mad users go out of their way to confuse the encoding. docker_process_sql --dont-use-mysql-root-password --database=mysql --binary-mode <<-EOSQL - -- What's done in this file shouldn't be replicated - -- or products like mysql-fabric won't work + -- Securing system users shouldn't be replicated + SET @orig_sql_log_bin= @@SESSION.SQL_LOG_BIN; SET @@SESSION.SQL_LOG_BIN=0; -- we need the SQL_MODE NO_BACKSLASH_ESCAPES mode to be clear for the password to be set SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); @@ -348,42 +381,19 @@ docker_setup_db() { DROP USER IF EXISTS root@'127.0.0.1', root@'::1'; EXECUTE IMMEDIATE CONCAT('DROP USER IF EXISTS root@\'', @@hostname,'\''); - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ; + ${rootLocalhostPass} ${rootCreate} ${mysqlAtLocalhost} ${mysqlAtLocalhostGrants} - -- pre-10.3 + -- pre-10.3 only DROP DATABASE IF EXISTS test ; + -- end of securing system users, rest of init now... + SET @@SESSION.SQL_LOG_BIN=@orig_sql_log_bin; + -- create users/databases + ${createDatabase} + ${createUser} + ${userGrants} EOSQL - - # Creates a custom database and user if specified - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Creating database ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MARIADB_DATABASE\` ;" - fi - - if [ -n "$MARIADB_PASSWORD" ] || [ -n "$MARIADB_PASSWORD_HASH" ] && [ -n "$MARIADB_USER" ]; then - mysql_note "Creating user ${MARIADB_USER}" - if [ -n "$MARIADB_PASSWORD" ]; then - # SQL escape the user password, \ followed by ' - local userPasswordEscaped - userPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_PASSWORD}" ) - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped'; - EOSQL_USER - else - docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER - SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', ''); - CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY PASSWORD '$MARIADB_PASSWORD_HASH'; - EOSQL_USER - fi - - if [ -n "$MARIADB_DATABASE" ]; then - mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" - docker_process_sql --database=mysql <<<"GRANT ALL ON \`${MARIADB_DATABASE//_/\\_}\`.* TO '$MARIADB_USER'@'%' ;" - fi - fi } # backup the mysql database @@ -430,11 +440,8 @@ docker_mariadb_upgrade() { mysql_upgrade --upgrade-system-tables mysql_note "Finished mariadb-upgrade" - # docker_temp_server_stop needs authentication since - # upgrade ended in FLUSH PRIVILEGES mysql_note "Stopping temporary server" - kill "$MARIADB_PID" - wait "$MARIADB_PID" + docker_temp_server_stop mysql_note "Temporary server stopped" } @@ -509,6 +516,15 @@ _main() { docker_setup_db docker_process_init_files /docker-entrypoint-initdb.d/* + # Wait until after /docker-entrypoint-initdb.d is performed before setting + # root@localhost password to a hash we don't know the password for. + if [ -n "${MARIADB_ROOT_PASSWORD_HASH}" ]; then + mysql_note "Setting root@localhost password hash" + docker_process_sql --dont-use-mysql-root-password --binary-mode <<-EOSQL + SET @@SESSION.SQL_LOG_BIN=0; + SET PASSWORD FOR 'root'@'localhost'= '${MARIADB_ROOT_PASSWORD_HASH}'; + EOSQL + fi mysql_note "Stopping temporary server" docker_temp_server_stop