diff --git a/10.2/docker-entrypoint.sh b/10.2/docker-entrypoint.sh index 2375375d..d420d79f 100755 --- a/10.2/docker-entrypoint.sh +++ b/10.2/docker-entrypoint.sh @@ -139,9 +139,11 @@ 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 ! mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}"; then + export MYSQL_PWD=$MARIADB_ROOT_PASSWORD + if ! mysqladmin shutdown -uroot --socket="${SOCKET}"; then mysql_error "Unable to shut down server." fi + unset MYSQL_PWD } # Verify that the minimally required password settings are set for new databases. @@ -216,15 +218,32 @@ docker_setup_env() { docker_process_sql() { passfileArgs=() if [ '--dont-use-mysql-root-password' = "$1" ]; then - passfileArgs+=( "$1" ) shift + unset MYSQL_PWD + else + export MYSQL_PWD=$MARIADB_ROOT_PASSWORD fi + local count=5 + while [ $count -gt 0 ] && [ ! -S "${SOCKET}" ] + do + count=$(( $count - 1 )) + mysql_note "Waiting for MariaDB to start, $count more seconds" + sleep 1 + done # args sent in can override this db, since they will be later in the command if [ -n "$MYSQL_DATABASE" ]; then set -- --database="$MYSQL_DATABASE" "$@" fi - mysql --defaults-extra-file=<( _mysql_passfile "${passfileArgs[@]}") --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" + mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" + unset MYSQL_PWD +} + +# SQL escape the string $1 to be placed in a string literal. +# escape, \ followed by ' +docker_sql_escape_string_literal() { + local escaped=${1//\\/\\\\} + echo "${escaped//\'/\\\'}" } # Initializes database with timezone info and root password, plus optional extra db/user @@ -258,24 +277,29 @@ docker_setup_db() { fi # Sets root password and creates root users for non-localhost hosts local rootCreate= + local rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) + # default root to listen for connections from anywhere if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then # 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 read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${MARIADB_ROOT_PASSWORD}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL fi # tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set - docker_process_sql --dont-use-mysql-root-password --database=mysql <<-EOSQL + # --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 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', ''); DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mariadb.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ; - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MARIADB_ROOT_PASSWORD}') ; + SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ; -- 10.1: https://github.com/MariaDB/server/blob/d925aec1c10cebf6c34825a7de50afe4e630aff4/scripts/mysql_secure_installation.sh#L347-L365 -- 10.5: https://github.com/MariaDB/server/blob/00c3a28820c67c37ebbca72691f4897b57f2eed5/scripts/mysql_secure_installation.sh#L351-L369 DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%' ; @@ -294,7 +318,12 @@ docker_setup_db() { if [ -n "$MARIADB_USER" ] && [ -n "$MARIADB_PASSWORD" ]; then mysql_note "Creating user ${MARIADB_USER}" - docker_process_sql --database=mysql <<<"CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$MARIADB_PASSWORD' ;" + # SQL escape the user password, \ followed by ' + local 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 if [ -n "$MARIADB_DATABASE" ]; then mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" @@ -303,18 +332,6 @@ docker_setup_db() { fi } -_mysql_passfile() { - # echo the password to the "file" the client uses - # the client command will use process substitution to create a file on the fly - # ie: --defaults-extra-file=<( _mysql_passfile ) - if [ '--dont-use-mysql-root-password' != "$1" ] && [ -n "$MARIADB_ROOT_PASSWORD" ]; then - cat <<-EOF - [client] - password="${MARIADB_ROOT_PASSWORD}" - EOF - fi -} - # check arguments for an option that would cause mysqld to stop # return true if there is one _mysql_want_help() { diff --git a/10.3/docker-entrypoint.sh b/10.3/docker-entrypoint.sh index 2375375d..d420d79f 100755 --- a/10.3/docker-entrypoint.sh +++ b/10.3/docker-entrypoint.sh @@ -139,9 +139,11 @@ 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 ! mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}"; then + export MYSQL_PWD=$MARIADB_ROOT_PASSWORD + if ! mysqladmin shutdown -uroot --socket="${SOCKET}"; then mysql_error "Unable to shut down server." fi + unset MYSQL_PWD } # Verify that the minimally required password settings are set for new databases. @@ -216,15 +218,32 @@ docker_setup_env() { docker_process_sql() { passfileArgs=() if [ '--dont-use-mysql-root-password' = "$1" ]; then - passfileArgs+=( "$1" ) shift + unset MYSQL_PWD + else + export MYSQL_PWD=$MARIADB_ROOT_PASSWORD fi + local count=5 + while [ $count -gt 0 ] && [ ! -S "${SOCKET}" ] + do + count=$(( $count - 1 )) + mysql_note "Waiting for MariaDB to start, $count more seconds" + sleep 1 + done # args sent in can override this db, since they will be later in the command if [ -n "$MYSQL_DATABASE" ]; then set -- --database="$MYSQL_DATABASE" "$@" fi - mysql --defaults-extra-file=<( _mysql_passfile "${passfileArgs[@]}") --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" + mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" + unset MYSQL_PWD +} + +# SQL escape the string $1 to be placed in a string literal. +# escape, \ followed by ' +docker_sql_escape_string_literal() { + local escaped=${1//\\/\\\\} + echo "${escaped//\'/\\\'}" } # Initializes database with timezone info and root password, plus optional extra db/user @@ -258,24 +277,29 @@ docker_setup_db() { fi # Sets root password and creates root users for non-localhost hosts local rootCreate= + local rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) + # default root to listen for connections from anywhere if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then # 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 read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${MARIADB_ROOT_PASSWORD}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL fi # tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set - docker_process_sql --dont-use-mysql-root-password --database=mysql <<-EOSQL + # --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 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', ''); DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mariadb.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ; - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MARIADB_ROOT_PASSWORD}') ; + SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ; -- 10.1: https://github.com/MariaDB/server/blob/d925aec1c10cebf6c34825a7de50afe4e630aff4/scripts/mysql_secure_installation.sh#L347-L365 -- 10.5: https://github.com/MariaDB/server/blob/00c3a28820c67c37ebbca72691f4897b57f2eed5/scripts/mysql_secure_installation.sh#L351-L369 DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%' ; @@ -294,7 +318,12 @@ docker_setup_db() { if [ -n "$MARIADB_USER" ] && [ -n "$MARIADB_PASSWORD" ]; then mysql_note "Creating user ${MARIADB_USER}" - docker_process_sql --database=mysql <<<"CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$MARIADB_PASSWORD' ;" + # SQL escape the user password, \ followed by ' + local 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 if [ -n "$MARIADB_DATABASE" ]; then mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" @@ -303,18 +332,6 @@ docker_setup_db() { fi } -_mysql_passfile() { - # echo the password to the "file" the client uses - # the client command will use process substitution to create a file on the fly - # ie: --defaults-extra-file=<( _mysql_passfile ) - if [ '--dont-use-mysql-root-password' != "$1" ] && [ -n "$MARIADB_ROOT_PASSWORD" ]; then - cat <<-EOF - [client] - password="${MARIADB_ROOT_PASSWORD}" - EOF - fi -} - # check arguments for an option that would cause mysqld to stop # return true if there is one _mysql_want_help() { diff --git a/10.4/docker-entrypoint.sh b/10.4/docker-entrypoint.sh index 2375375d..d420d79f 100755 --- a/10.4/docker-entrypoint.sh +++ b/10.4/docker-entrypoint.sh @@ -139,9 +139,11 @@ 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 ! mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}"; then + export MYSQL_PWD=$MARIADB_ROOT_PASSWORD + if ! mysqladmin shutdown -uroot --socket="${SOCKET}"; then mysql_error "Unable to shut down server." fi + unset MYSQL_PWD } # Verify that the minimally required password settings are set for new databases. @@ -216,15 +218,32 @@ docker_setup_env() { docker_process_sql() { passfileArgs=() if [ '--dont-use-mysql-root-password' = "$1" ]; then - passfileArgs+=( "$1" ) shift + unset MYSQL_PWD + else + export MYSQL_PWD=$MARIADB_ROOT_PASSWORD fi + local count=5 + while [ $count -gt 0 ] && [ ! -S "${SOCKET}" ] + do + count=$(( $count - 1 )) + mysql_note "Waiting for MariaDB to start, $count more seconds" + sleep 1 + done # args sent in can override this db, since they will be later in the command if [ -n "$MYSQL_DATABASE" ]; then set -- --database="$MYSQL_DATABASE" "$@" fi - mysql --defaults-extra-file=<( _mysql_passfile "${passfileArgs[@]}") --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" + mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" + unset MYSQL_PWD +} + +# SQL escape the string $1 to be placed in a string literal. +# escape, \ followed by ' +docker_sql_escape_string_literal() { + local escaped=${1//\\/\\\\} + echo "${escaped//\'/\\\'}" } # Initializes database with timezone info and root password, plus optional extra db/user @@ -258,24 +277,29 @@ docker_setup_db() { fi # Sets root password and creates root users for non-localhost hosts local rootCreate= + local rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) + # default root to listen for connections from anywhere if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then # 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 read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${MARIADB_ROOT_PASSWORD}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL fi # tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set - docker_process_sql --dont-use-mysql-root-password --database=mysql <<-EOSQL + # --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 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', ''); DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mariadb.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ; - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MARIADB_ROOT_PASSWORD}') ; + SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ; -- 10.1: https://github.com/MariaDB/server/blob/d925aec1c10cebf6c34825a7de50afe4e630aff4/scripts/mysql_secure_installation.sh#L347-L365 -- 10.5: https://github.com/MariaDB/server/blob/00c3a28820c67c37ebbca72691f4897b57f2eed5/scripts/mysql_secure_installation.sh#L351-L369 DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%' ; @@ -294,7 +318,12 @@ docker_setup_db() { if [ -n "$MARIADB_USER" ] && [ -n "$MARIADB_PASSWORD" ]; then mysql_note "Creating user ${MARIADB_USER}" - docker_process_sql --database=mysql <<<"CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$MARIADB_PASSWORD' ;" + # SQL escape the user password, \ followed by ' + local 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 if [ -n "$MARIADB_DATABASE" ]; then mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" @@ -303,18 +332,6 @@ docker_setup_db() { fi } -_mysql_passfile() { - # echo the password to the "file" the client uses - # the client command will use process substitution to create a file on the fly - # ie: --defaults-extra-file=<( _mysql_passfile ) - if [ '--dont-use-mysql-root-password' != "$1" ] && [ -n "$MARIADB_ROOT_PASSWORD" ]; then - cat <<-EOF - [client] - password="${MARIADB_ROOT_PASSWORD}" - EOF - fi -} - # check arguments for an option that would cause mysqld to stop # return true if there is one _mysql_want_help() { diff --git a/10.5/docker-entrypoint.sh b/10.5/docker-entrypoint.sh index 2375375d..d420d79f 100755 --- a/10.5/docker-entrypoint.sh +++ b/10.5/docker-entrypoint.sh @@ -139,9 +139,11 @@ 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 ! mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}"; then + export MYSQL_PWD=$MARIADB_ROOT_PASSWORD + if ! mysqladmin shutdown -uroot --socket="${SOCKET}"; then mysql_error "Unable to shut down server." fi + unset MYSQL_PWD } # Verify that the minimally required password settings are set for new databases. @@ -216,15 +218,32 @@ docker_setup_env() { docker_process_sql() { passfileArgs=() if [ '--dont-use-mysql-root-password' = "$1" ]; then - passfileArgs+=( "$1" ) shift + unset MYSQL_PWD + else + export MYSQL_PWD=$MARIADB_ROOT_PASSWORD fi + local count=5 + while [ $count -gt 0 ] && [ ! -S "${SOCKET}" ] + do + count=$(( $count - 1 )) + mysql_note "Waiting for MariaDB to start, $count more seconds" + sleep 1 + done # args sent in can override this db, since they will be later in the command if [ -n "$MYSQL_DATABASE" ]; then set -- --database="$MYSQL_DATABASE" "$@" fi - mysql --defaults-extra-file=<( _mysql_passfile "${passfileArgs[@]}") --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" + mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" + unset MYSQL_PWD +} + +# SQL escape the string $1 to be placed in a string literal. +# escape, \ followed by ' +docker_sql_escape_string_literal() { + local escaped=${1//\\/\\\\} + echo "${escaped//\'/\\\'}" } # Initializes database with timezone info and root password, plus optional extra db/user @@ -258,24 +277,29 @@ docker_setup_db() { fi # Sets root password and creates root users for non-localhost hosts local rootCreate= + local rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) + # default root to listen for connections from anywhere if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then # 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 read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${MARIADB_ROOT_PASSWORD}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL fi # tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set - docker_process_sql --dont-use-mysql-root-password --database=mysql <<-EOSQL + # --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 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', ''); DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mariadb.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ; - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MARIADB_ROOT_PASSWORD}') ; + SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ; -- 10.1: https://github.com/MariaDB/server/blob/d925aec1c10cebf6c34825a7de50afe4e630aff4/scripts/mysql_secure_installation.sh#L347-L365 -- 10.5: https://github.com/MariaDB/server/blob/00c3a28820c67c37ebbca72691f4897b57f2eed5/scripts/mysql_secure_installation.sh#L351-L369 DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%' ; @@ -294,7 +318,12 @@ docker_setup_db() { if [ -n "$MARIADB_USER" ] && [ -n "$MARIADB_PASSWORD" ]; then mysql_note "Creating user ${MARIADB_USER}" - docker_process_sql --database=mysql <<<"CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$MARIADB_PASSWORD' ;" + # SQL escape the user password, \ followed by ' + local 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 if [ -n "$MARIADB_DATABASE" ]; then mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" @@ -303,18 +332,6 @@ docker_setup_db() { fi } -_mysql_passfile() { - # echo the password to the "file" the client uses - # the client command will use process substitution to create a file on the fly - # ie: --defaults-extra-file=<( _mysql_passfile ) - if [ '--dont-use-mysql-root-password' != "$1" ] && [ -n "$MARIADB_ROOT_PASSWORD" ]; then - cat <<-EOF - [client] - password="${MARIADB_ROOT_PASSWORD}" - EOF - fi -} - # check arguments for an option that would cause mysqld to stop # return true if there is one _mysql_want_help() { diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 2375375d..d420d79f 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -139,9 +139,11 @@ 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 ! mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}"; then + export MYSQL_PWD=$MARIADB_ROOT_PASSWORD + if ! mysqladmin shutdown -uroot --socket="${SOCKET}"; then mysql_error "Unable to shut down server." fi + unset MYSQL_PWD } # Verify that the minimally required password settings are set for new databases. @@ -216,15 +218,32 @@ docker_setup_env() { docker_process_sql() { passfileArgs=() if [ '--dont-use-mysql-root-password' = "$1" ]; then - passfileArgs+=( "$1" ) shift + unset MYSQL_PWD + else + export MYSQL_PWD=$MARIADB_ROOT_PASSWORD fi + local count=5 + while [ $count -gt 0 ] && [ ! -S "${SOCKET}" ] + do + count=$(( $count - 1 )) + mysql_note "Waiting for MariaDB to start, $count more seconds" + sleep 1 + done # args sent in can override this db, since they will be later in the command if [ -n "$MYSQL_DATABASE" ]; then set -- --database="$MYSQL_DATABASE" "$@" fi - mysql --defaults-extra-file=<( _mysql_passfile "${passfileArgs[@]}") --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" + mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@" + unset MYSQL_PWD +} + +# SQL escape the string $1 to be placed in a string literal. +# escape, \ followed by ' +docker_sql_escape_string_literal() { + local escaped=${1//\\/\\\\} + echo "${escaped//\'/\\\'}" } # Initializes database with timezone info and root password, plus optional extra db/user @@ -258,24 +277,29 @@ docker_setup_db() { fi # Sets root password and creates root users for non-localhost hosts local rootCreate= + local rootPasswordEscaped=$( docker_sql_escape_string_literal "${MARIADB_ROOT_PASSWORD}" ) + # default root to listen for connections from anywhere if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then # 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 read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${MARIADB_ROOT_PASSWORD}' ; + CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ; GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ; EOSQL fi # tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set - docker_process_sql --dont-use-mysql-root-password --database=mysql <<-EOSQL + # --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 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', ''); DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mariadb.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ; - SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MARIADB_ROOT_PASSWORD}') ; + SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ; -- 10.1: https://github.com/MariaDB/server/blob/d925aec1c10cebf6c34825a7de50afe4e630aff4/scripts/mysql_secure_installation.sh#L347-L365 -- 10.5: https://github.com/MariaDB/server/blob/00c3a28820c67c37ebbca72691f4897b57f2eed5/scripts/mysql_secure_installation.sh#L351-L369 DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%' ; @@ -294,7 +318,12 @@ docker_setup_db() { if [ -n "$MARIADB_USER" ] && [ -n "$MARIADB_PASSWORD" ]; then mysql_note "Creating user ${MARIADB_USER}" - docker_process_sql --database=mysql <<<"CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$MARIADB_PASSWORD' ;" + # SQL escape the user password, \ followed by ' + local 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 if [ -n "$MARIADB_DATABASE" ]; then mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}" @@ -303,18 +332,6 @@ docker_setup_db() { fi } -_mysql_passfile() { - # echo the password to the "file" the client uses - # the client command will use process substitution to create a file on the fly - # ie: --defaults-extra-file=<( _mysql_passfile ) - if [ '--dont-use-mysql-root-password' != "$1" ] && [ -n "$MARIADB_ROOT_PASSWORD" ]; then - cat <<-EOF - [client] - password="${MARIADB_ROOT_PASSWORD}" - EOF - fi -} - # check arguments for an option that would cause mysqld to stop # return true if there is one _mysql_want_help() {