Skip to content

Fix handling of mysql passwords with weird characters in it #2660

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 27 additions & 30 deletions .github/jobs/baseinstall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export APP_ENV="${4:-prod}"
# In the test environment, we need to use a different database
[ "$APP_ENV" = "prod" ] && DATABASE_NAME=domjudge || DATABASE_NAME=domjudge_test

MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}
MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-mysql_root_password}

set -euxo pipefail

Expand All @@ -29,8 +29,8 @@ cd ..
section_end

section_start "Set simple admin password"
echo "password" > ./etc/initial_admin_password.secret
echo "default login admin password password" > ~/.netrc
echo "admin_password" > ./etc/initial_admin_password.secret
echo "default login admin password admin_password" > ~/.netrc
section_end

section_start "Install domserver"
Expand Down Expand Up @@ -67,31 +67,28 @@ password=${MYSQL_ROOT_PASSWORD}
EOF
cat ~/.my.cnf

mysql_root "CREATE DATABASE IF NOT EXISTS \`$DATABASE_NAME\` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mysql_root "CREATE USER IF NOT EXISTS \`domjudge\`@'%' IDENTIFIED BY 'domjudge';"
mysql_root "GRANT SELECT, INSERT, UPDATE, DELETE ON \`$DATABASE_NAME\`.* TO 'domjudge'@'%';"
mysql_root "FLUSH PRIVILEGES;"
echo 'unused:sqlserver:$DATABASE_NAME:domjudge:djdb-pw:3306' > /opt/domjudge/domserver/etc/dbpasswords.secret

/opt/domjudge/domserver/bin/dj_setup_database create-db-users

# Show some MySQL debugging
mysql_root "show databases"
mysql_root "SELECT CURRENT_USER();"
mysql_root "SELECT USER();"
mysql_root "SELECT user,host FROM mysql.user"
mysql_root "SET GLOBAL max_allowed_packet=1073741824"
mysql_root "SHOW GLOBAL STATUS LIKE 'Connection_errors_%'"
mysql_root "SHOW VARIABLES LIKE '%_timeout'"
echo "unused:sqlserver:$DATABASE_NAME:domjudge:domjudge:3306" > /opt/domjudge/domserver/etc/dbpasswords.secret
mysql_user "SELECT CURRENT_USER();"
mysql_user "SELECT USER();"
mysql_log "show databases"
mysql_log "SELECT CURRENT_USER();"
mysql_log "SELECT USER();"
mysql_log "SELECT user,host FROM mysql.user"
mysql_log "SET GLOBAL max_allowed_packet=1073741824"
mysql_log "SHOW GLOBAL STATUS LIKE 'Connection_errors_%'"
mysql_log "SHOW VARIABLES LIKE '%_timeout'"
section_end

if [ "${db}" = "install" ]; then
section_start "Install DOMjudge database"
/opt/domjudge/domserver/bin/dj_setup_database -uroot -p${MYSQL_ROOT_PASSWORD} bare-install
/opt/domjudge/domserver/bin/dj_setup_database bare-install
section_end
elif [ "${db}" = "upgrade" ]; then
section_start "Upgrade DOMjudge database"
/opt/domjudge/domserver/bin/dj_setup_database -uroot -p${MYSQL_ROOT_PASSWORD} upgrade
/opt/domjudge/domserver/bin/dj_setup_database update-password
/opt/domjudge/domserver/bin/dj_setup_database upgrade
section_end
fi

Expand Down Expand Up @@ -131,31 +128,31 @@ if [ "${db}" = "install" ]; then
section_start "Install the example data"
if [ "$version" = "unit" ]; then
# Make sure admin has no team associated so we will not insert submissions during unit tests.
mysql_root "UPDATE user SET teamid=null WHERE userid=1;" $DATABASE_NAME
mysql_log "UPDATE user SET teamid=null WHERE userid=1;" $DATABASE_NAME
fi
/opt/domjudge/domserver/bin/dj_setup_database -uroot -p${MYSQL_ROOT_PASSWORD} install-examples | tee -a "$ARTIFACTS/mysql.txt"
/opt/domjudge/domserver/bin/dj_setup_database install-examples | tee -a "$ARTIFACTS/mysql.txt"
section_end
fi

section_start "Setup user"
# We're using the admin user in all possible roles
mysql_root "DELETE FROM userrole WHERE userid=1;" $DATABASE_NAME
mysql_log "DELETE FROM userrole WHERE userid=1;" $DATABASE_NAME
if [ "$version" = "team" ]; then
# Add team to admin user
mysql_root "INSERT INTO userrole (userid, roleid) VALUES (1, 3);" $DATABASE_NAME
mysql_root "UPDATE user SET teamid = 1 WHERE userid = 1;" $DATABASE_NAME
mysql_log "INSERT INTO userrole (userid, roleid) VALUES (1, 3);" $DATABASE_NAME
mysql_log "UPDATE user SET teamid = 1 WHERE userid = 1;" $DATABASE_NAME
elif [ "$version" = "jury" ]; then
# Add jury to admin user
mysql_root "INSERT INTO userrole (userid, roleid) VALUES (1, 2);" $DATABASE_NAME
mysql_log "INSERT INTO userrole (userid, roleid) VALUES (1, 2);" $DATABASE_NAME
elif [ "$version" = "balloon" ]; then
# Add balloon to admin user
mysql_root "INSERT INTO userrole (userid, roleid) VALUES (1, 4);" $DATABASE_NAME
mysql_log "INSERT INTO userrole (userid, roleid) VALUES (1, 4);" $DATABASE_NAME
elif [ "$version" = "admin" ]; then
# Add admin to admin user
mysql_root "INSERT INTO userrole (userid, roleid) VALUES (1, 1);" $DATABASE_NAME
mysql_log "INSERT INTO userrole (userid, roleid) VALUES (1, 1);" $DATABASE_NAME
elif [ "$version" = "all" ] || [ "$version" = "unit" ]; then
mysql_root "INSERT INTO userrole (userid, roleid) VALUES (1, 1);" $DATABASE_NAME
mysql_root "INSERT INTO userrole (userid, roleid) VALUES (1, 3);" $DATABASE_NAME
mysql_root "UPDATE user SET teamid = 1 WHERE userid = 1;" $DATABASE_NAME
mysql_log "INSERT INTO userrole (userid, roleid) VALUES (1, 1);" $DATABASE_NAME
mysql_log "INSERT INTO userrole (userid, roleid) VALUES (1, 3);" $DATABASE_NAME
mysql_log "UPDATE user SET teamid = 1 WHERE userid = 1;" $DATABASE_NAME
fi
section_end
9 changes: 2 additions & 7 deletions .github/jobs/ci_settings.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,9 @@ section_end_internal () {
trace_on
}

mysql_root () {
mysql_log () {
# shellcheck disable=SC2086
echo "$1" | mysql -uroot -proot ${2:-} | tee -a "$ARTIFACTS"/mysql.txt
}

mysql_user () {
# shellcheck disable=SC2086
echo "$1" | mysql -udomjudge -pdomjudge ${2:-} | tee -a "$ARTIFACTS"/mysql.txt
echo "$1" | mysql ${2:-} | tee -a "$ARTIFACTS"/mysql.txt
}

show_phpinfo() {
Expand Down
8 changes: 3 additions & 5 deletions .github/workflows/database-upgrade.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,17 @@ jobs:
ports:
- 3306:3306
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_USER: domjudge
MYSQL_PASSWORD: domjudge
MYSQL_ROOT_PASSWORD: mysql_root_password
options: --health-cmd="healthcheck.sh --connect --innodb_initialized" --health-interval=10s --health-timeout=5s --health-retries=3
steps:
- uses: actions/checkout@v4
- name: Import Database
run: mysql -hsqlserver -uroot -proot < .github/jobs/data/dj733.sql
run: mysql -hsqlserver -uroot -pmysql_root_password < .github/jobs/data/dj733.sql
- name: Upgrade DOMjudge
run: .github/jobs/baseinstall.sh default upgrade
- name: Setting initial Admin Password
run: echo "pass" > /opt/domjudge/domserver/etc/initial_admin_password.secret
- name: Check for Errors in the Upgrade
run: mysql -hsqlserver -uroot -proot -e "SHOW TABLES FROM domjudge;"
run: mysql -hsqlserver -uroot -pmysql_root_password -e "SHOW TABLES FROM domjudge;"
- name: Check for Errors in DOMjudge Webinterface
run: .github/jobs/webstandard.sh none admin
6 changes: 2 additions & 4 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ jobs:
ports:
- 3306:3306
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_USER: domjudge
MYSQL_PASSWORD: domjudge
MYSQL_ROOT_PASSWORD: mysql_root_password
options: --health-cmd="healthcheck.sh --connect --innodb_initialized" --health-interval=10s --health-timeout=5s --health-retries=3
steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -81,7 +79,7 @@ jobs:
done
- name: dump the db
if: ${{ !cancelled() }}
run: mysqldump -uroot -proot --quick --max_allowed_packet=1024M domjudge > /tmp/db.sql
run: mysqldump -uroot -pmysql_root_password --quick --max_allowed_packet=1024M domjudge > /tmp/db.sql
- name: Upload database dump for debugging
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/webstandard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ jobs:
ports:
- 3306:3306
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_USER: domjudge
MYSQL_PASSWORD: domjudge
MYSQL_ROOT_PASSWORD: mysql_root_password
options: --health-cmd="healthcheck.sh --connect --innodb_initialized" --health-interval=10s --health-timeout=5s --health-retries=3
strategy:
matrix:
Expand Down
63 changes: 43 additions & 20 deletions sql/dj_setup_database.in
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#!/bin/sh
#!/bin/bash
# @configure_input@

# This script allows one to perform DOMjudge database setup actions.
# See usage below for possible commands and options.

set -e

set -x

BINDIR="@domserver_bindir@"
ETCDIR="@domserver_etcdir@"
WEBAPPDIR="@domserver_webappdir@"
Expand Down Expand Up @@ -52,30 +54,47 @@ not have to pass any of the options above.
EOF
}

urlencode()
{
php -r "echo rawurlencode('$1');"
}

# This is global variable to be able to return the output from
# mysql_options() below as an array, which is not possible otherwise.
declare -a _mysql_options

mysql_options()
{
local user pass
_mysql_options=()

# shellcheck disable=SC2153
if [ -n "$DBUSER" ]; then
_user="-u $DBUSER"
else
_user="${DBA_USER:+-u ${DBA_USER}}"
_mysql_options+=('-u' "$DBUSER")
elif [ -n "$DBA_USER" ]; then
_mysql_options+=('-u' "$DBA_USER")
fi
# shellcheck disable=SC2153
if [ -n "$PASSWD" ]; then
_pass="-p$PASSWD"
else
[ -n "$PROMPT_PASSWD" ] && _pass="-p"
[ -n "$DBA_PASSWD" ] && _pass="-p$DBA_PASSWD"
_mysql_options+=("-p$PASSWD")
elif [ -n "$DBA_PASSWD" ]; then
_mysql_options+=("-p$DBA_PASSWD")
elif [ -n "$PROMPT_PASSWD" ]; then
_mysql_options+=('-p')
fi

[ -z "$USE_SOCKET" ] && port="-P$DBPORT"
echo $_user ${_pass:+"$_pass"} -h "$DBHOST" ${port:+"$port"}
_mysql_options+=('-h' "$DBHOST")

if [ -z "$USE_SOCKET" ]; then
_mysql_options+=("-P$DBPORT")
fi
}

# Wrapper around mysql command to allow setting options, user, etc.
mysql()
{
command mysql $(mysql_options) --silent --skip-column-names "$@"
mysql_options
command mysql "${_mysql_options[@]}" --silent --skip-column-names "$@"
}

# Quick shell hack to get a key from an INI file.
Expand Down Expand Up @@ -126,12 +145,17 @@ symfony_console()
fi

if [ -n "$DBA_USER" ]; then
user=$(urlencode "${DBA_USER}")
host=$(urlencode "${domjudge_DBHOST}")
db=$(urlencode "${domjudge_DBNAME}")
if [ -n "$DBA_PASSWD" ]; then
DATABASE_URL=mysql://${DBA_USER}:${DBA_PASSWD}@${domjudge_DBHOST}:${domjudge_DBPORT}/${domjudge_DBNAME}
DATABASE_URL="mysql://$user:$(urlencode "${DBA_PASSWD}")@$host:${domjudge_DBPORT}/$db"
else
DATABASE_URL=mysql://${DBA_USER}@${domjudge_DBHOST}:${domjudge_DBPORT}/${domjudge_DBNAME}
DATABASE_URL="mysql://$user@$host:${domjudge_DBPORT}/$db"
fi
fi
else
DATABASE_URL="mysql://$DBUSER:$(urlencode "${DB_PASSWD}")@$host:${domjudge_DBPORT}/$db"
fi

if [ -n "$DATABASE_URL" ]; then
Expand Down Expand Up @@ -236,7 +260,6 @@ remove_db_users()

update_password()
{
read_dbpasswords
(
echo "ALTER USER '$domjudge_DBUSER'@'localhost' IDENTIFIED BY '$domjudge_PASSWD';"
echo "FLUSH PRIVILEGES;"
Expand All @@ -247,7 +270,7 @@ update_password()

install_examples()
{
DBUSER=$domjudge_DBUSER PASSWD=$domjudge_PASSWD symfony_console domjudge:load-example-data
symfony_console domjudge:load-example-data
"$EXAMPLEPROBDIR"/generate-contest-yaml
( cd "$EXAMPLEPROBDIR" && yes y | "$BINDIR"/import-contest )
}
Expand Down Expand Up @@ -321,8 +344,8 @@ install-loadtest)
read_dbpasswords
create_db_users
symfony_console doctrine:migrations:migrate -n
DBUSER=$domjudge_DBUSER PASSWD=$domjudge_PASSWD symfony_console domjudge:load-default-data
DBUSER=$domjudge_DBUSER PASSWD=$domjudge_PASSWD symfony_console domjudge:load-gatling-data
symfony_console domjudge:load-default-data
symfony_console domjudge:load-gatling-data
;;

create-db-users)
Expand All @@ -331,14 +354,15 @@ create-db-users)
;;

update-password)
read_dbpasswords
update_password
;;

bare-install|install)
read_dbpasswords
create_db_users
symfony_console doctrine:migrations:migrate -n
DBUSER=$domjudge_DBUSER PASSWD=$domjudge_PASSWD symfony_console domjudge:load-default-data
symfony_console domjudge:load-default-data
if [ "$1" = "install" ]; then
install_examples
verbose "SQL structure and default/example data installed."
Expand All @@ -362,9 +386,8 @@ upgrade)
fi

symfony_console doctrine:migrations:migrate -n
DBUSER=$domjudge_DBUSER PASSWD=$domjudge_PASSWD symfony_console domjudge:load-default-data

symfony_console domjudge:refresh-cache
symfony_console domjudge:load-default-data
verbose "DOMjudge database upgrade completed."
;;

Expand Down
6 changes: 5 additions & 1 deletion webapp/config/load_db_secrets.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php declare(strict_types=1);

Check warning on line 1 in webapp/config/load_db_secrets.php

View workflow job for this annotation

GitHub Actions / phpcs

A file should declare new symbols (classes, functions, constants, etc.) and cause no other side effects, or it should execute logic with side effects, but should not do both. The first symbol is defined on line 11 and the first side effect is on line 9.

// This file fetches credentials on the fly from files in etc.
// These settings can later be overridden by Symfony files
Expand Down Expand Up @@ -36,7 +36,11 @@
break;
}

return sprintf('mysql://%s:%s@%s:%d/%s?serverVersion=5.7.0', $user, $pass, $host, $port ?? 3306, $db);
return sprintf(
'mysql://%s:%s@%s:%d/%s?serverVersion=5.7.0',
rawurlencode($user), rawurlencode($pass), rawurlencode($host),
$port ?? 3306, rawurlencode($db)
);
}

function get_app_secret(): string
Expand Down
2 changes: 1 addition & 1 deletion webapp/config/packages/doctrine.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ doctrine:
charset: utf8mb4
collate: utf8mb4_unicode_ci

url: '%env(resolve:DATABASE_URL)%'
url: '%env(DATABASE_URL)%'
profiling_collect_backtrace: '%kernel.debug%'
types:
tinyint: App\Doctrine\DBAL\Types\TinyIntType
Expand Down
Loading