From aca8b1c0de23326098ebc141a7868bba43f73f05 Mon Sep 17 00:00:00 2001 From: Kai Schlachter Date: Fri, 3 May 2024 13:52:50 +0200 Subject: [PATCH 1/4] Added possibility to use an existing config file for recursor or authorative mysql-powerdns-server, in case the configuration file is handed in from a volume or bind mount (in which it should be mounted as read only!). --- pdns-mysql/docker-entrypoint.sh | 173 +++++++++++++++++++---------- pdns-recursor/docker-entrypoint.sh | 27 +++-- 2 files changed, 135 insertions(+), 65 deletions(-) diff --git a/pdns-mysql/docker-entrypoint.sh b/pdns-mysql/docker-entrypoint.sh index 0865131..c231969 100755 --- a/pdns-mysql/docker-entrypoint.sh +++ b/pdns-mysql/docker-entrypoint.sh @@ -2,65 +2,90 @@ set -eu -# Configure mysql env vars -: "${PDNS_gmysql_host:=${MYSQL_ENV_MYSQL_HOST:-mysql}}" -: "${PDNS_gmysql_port:=${MYSQL_ENV_MYSQL_PORT:-3306}}" -: "${PDNS_gmysql_user:=${MYSQL_ENV_MYSQL_USER:-root}}" -if [ "${PDNS_gmysql_user}" = 'root' ]; then - : "${PDNS_gmysql_password:=${MYSQL_ENV_MYSQL_ROOT_PASSWORD:-}}" -fi -: "${PDNS_gmysql_password:=${MYSQL_ENV_MYSQL_PASSWORD:-powerdns}}" -: "${PDNS_gmysql_dbname:=${MYSQL_ENV_MYSQL_DATABASE:-powerdns}}" - -# Use first part of node name as database name suffix -if [ "${NODE_NAME:-}" ]; then - NODE_NAME=$(echo "${NODE_NAME}" | sed -e 's/\..*//' -e 's/-//') - PDNS_gmysql_dbname="${PDNS_gmysql_dbname}${NODE_NAME}" -fi - -export PDNS_gmysql_host PDNS_gmysql_port PDNS_gmysql_user PDNS_gmysql_password PDNS_gmysql_dbname - - -EXTRA='' - -# Password Auth -if [ "${PDNS_gmysql_password}" ]; then - EXTRA="${EXTRA} -p${PDNS_gmysql_password}" -fi - -# Allow socket connections -if [ "${PDNS_gmysql_socket:-}" ]; then - export PDNS_gmysql_host='localhost' - EXTRA="${EXTRA} --socket=${PDNS_gmysql_socket}" -fi - -MYSQL_COMMAND="mysql -h ${PDNS_gmysql_host} -P ${PDNS_gmysql_port} -u ${PDNS_gmysql_user}${EXTRA}" - -# Wait for MySQL to respond -until $MYSQL_COMMAND -e ';' ; do - >&2 echo 'MySQL is unavailable - sleeping' - sleep 3 -done - -# Initialize DB if needed -if [ "${SKIP_DB_CREATE:-false}" != 'true' ]; then - $MYSQL_COMMAND -e "CREATE DATABASE IF NOT EXISTS ${PDNS_gmysql_dbname}" -fi - -MYSQL_CHECK_IF_HAS_TABLE="SELECT COUNT(DISTINCT table_name) FROM information_schema.columns WHERE table_schema = '${PDNS_gmysql_dbname}';" -MYSQL_NUM_TABLE=$($MYSQL_COMMAND --batch --skip-column-names -e "$MYSQL_CHECK_IF_HAS_TABLE") -if [ "$MYSQL_NUM_TABLE" -eq 0 ]; then - $MYSQL_COMMAND -D "$PDNS_gmysql_dbname" < /usr/share/doc/pdns/schema.mysql.sql -fi - -# SQL migration to version 4.7 -MYSQL_CHECK_IF_47="SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = '${PDNS_gmysql_dbname}' AND table_name = 'domains' AND column_name = 'options';" -MYSQL_NUM_TABLE=$($MYSQL_COMMAND --batch --skip-column-names -e "$MYSQL_CHECK_IF_47") -if [ "$MYSQL_NUM_TABLE" -eq 0 ]; then +##### Function definitions #### + + +function deriveMySQLSettingsFromExistingConfigFile { + if [ ! -f /etc/pdns/pdns.conf ]; then + echo "Use of existing file /etc/pdns/pdns.conf requested but file does not exist!" + exit 1 + fi + PDNS_gmysql_host=`sed -n 's/^gmysql-host=\(.*\)/\1/p' < /etc/pdns/pdns.conf` + PDNS_gmysql_port=`sed -n 's/^gmysql-port=\(.*\)/\1/p' < /etc/pdns/pdns.conf` + PDNS_gmysql_user=`sed -n 's/^gmysql-user=\(.*\)/\1/p' < /etc/pdns/pdns.conf` + PDNS_gmysql_password=`sed -n 's/^gmysql-password=\(.*\)/\1/p' < /etc/pdns/pdns.conf` + PDNS_gmysql_dbname=`sed -n 's/^gmysql-dbname=\(.*\)/\1/p' < /etc/pdns/pdns.conf` +} + +function deriveMySQLSettingsFromEnvironment { + # Configure mysql env vars + : "${PDNS_gmysql_host:=${MYSQL_ENV_MYSQL_HOST:-mysql}}" + : "${PDNS_gmysql_port:=${MYSQL_ENV_MYSQL_PORT:-3306}}" + : "${PDNS_gmysql_user:=${MYSQL_ENV_MYSQL_USER:-root}}" + if [ "${PDNS_gmysql_user}" = 'root' ]; then + : "${PDNS_gmysql_password:=${MYSQL_ENV_MYSQL_ROOT_PASSWORD:-}}" + fi + : "${PDNS_gmysql_password:=${MYSQL_ENV_MYSQL_PASSWORD:-powerdns}}" + : "${PDNS_gmysql_dbname:=${MYSQL_ENV_MYSQL_DATABASE:-powerdns}}" + + # Use first part of node name as database name suffix + if [ "${NODE_NAME:-}" ]; then + NODE_NAME=$(echo "${NODE_NAME}" | sed -e 's/\..*//' -e 's/-//') + PDNS_gmysql_dbname="${PDNS_gmysql_dbname}${NODE_NAME}" + fi + + export PDNS_gmysql_host PDNS_gmysql_port PDNS_gmysql_user PDNS_gmysql_password PDNS_gmysql_dbname +} + +function generateMySQLCommand { + EXTRA='' + + # Password Auth + if [ "${PDNS_gmysql_password}" ]; then + EXTRA="${EXTRA} -p${PDNS_gmysql_password}" + fi + + # Allow socket connections + if [ "${PDNS_gmysql_socket:-}" ]; then + export PDNS_gmysql_host='localhost' + EXTRA="${EXTRA} --socket=${PDNS_gmysql_socket}" + fi + + MYSQL_COMMAND="mysql -h ${PDNS_gmysql_host} -P ${PDNS_gmysql_port} -u ${PDNS_gmysql_user}${EXTRA}" +} + + +function createDatabaseIfRequested { + # Initialize DB if needed + if [ "${SKIP_DB_CREATE:-false}" != 'true' ]; then + $MYSQL_COMMAND -e "CREATE DATABASE IF NOT EXISTS ${PDNS_gmysql_dbname}" + fi +} + +function initDatabase { + if [ "${SKIP_DB_INIT:-false}" != 'true' ]; then + MYSQL_CHECK_IF_HAS_TABLE="SELECT COUNT(DISTINCT table_name) FROM information_schema.columns WHERE table_schema = '${PDNS_gmysql_dbname}';" + MYSQL_NUM_TABLE=$($MYSQL_COMMAND --batch --skip-column-names -e "$MYSQL_CHECK_IF_HAS_TABLE") + if [ "$MYSQL_NUM_TABLE" -eq 0 ]; then + echo "Database exists and has no tables yet, doing init"; + $MYSQL_COMMAND -D "$PDNS_gmysql_dbname" < /usr/share/doc/pdns/schema.mysql.sql + else + echo "Database exists but already has tables, will not try to init"; + fi + fi +} + +function migrateDatabaseTo47 { + # SQL migration to version 4.7 + MYSQL_CHECK_IF_47="SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = '${PDNS_gmysql_dbname}' AND table_name = 'domains' AND column_name = 'options';" + MYSQL_NUM_TABLE=$($MYSQL_COMMAND --batch --skip-column-names -e "$MYSQL_CHECK_IF_47") + if [ "$MYSQL_NUM_TABLE" -eq 0 ]; then echo 'Migrating MySQL schema to version 4.7...' $MYSQL_COMMAND -D "$PDNS_gmysql_dbname" < /usr/share/doc/pdns/4.3.0_to_4.7.0_schema.mysql.sql -fi + fi +} +function initSuperslave { if [ "${PDNS_superslave:-no}" = 'yes' ]; then # Configure supermasters if needed if [ "${SUPERMASTER_IPS:-}" ]; then @@ -83,8 +108,40 @@ if [ "${PDNS_superslave:-no}" = 'yes' ]; then $MYSQL_COMMAND -D "$PDNS_gmysql_dbname" -e "$MYSQL_INSERT_SUPERMASTERS" fi fi +} + +function generateAndInstallConfigFileFromEnvironment { + # Create config file from template + subvars --prefix 'PDNS_' < '/pdns.conf.tpl' > '/etc/pdns/pdns.conf' +} + + +#### End of function definitions, let's get to work ... + +if [ ${USE_EXISTING_CONFIG_FILE:-false} = 'true' ]; then + deriveMySQLSettingsFromExistingConfigFile +else + deriveMySQLSettingsFromEnvironment +fi + +generateMySQLCommand + +# Wait for MySQL to respond +until $MYSQL_COMMAND -e ';' ; do + >&2 echo 'MySQL is unavailable - sleeping' + sleep 3 +done -# Create config file from template -subvars --prefix 'PDNS_' < '/pdns.conf.tpl' > '/etc/pdns/pdns.conf' + +createDatabaseIfRequested +initDatabase +migrateDatabaseTo47 + +if [ ${USE_EXISTING_CONFIG_FILE:-false} = 'false' ]; then + echo "(re-)generating config file from environment variables" + generateAndInstallConfigFileFromEnvironment +fi exec "$@" + + diff --git a/pdns-recursor/docker-entrypoint.sh b/pdns-recursor/docker-entrypoint.sh index a7caca3..7d9f63e 100755 --- a/pdns-recursor/docker-entrypoint.sh +++ b/pdns-recursor/docker-entrypoint.sh @@ -2,12 +2,17 @@ set -eu -# Configure base vars -: "${PDNS_local_port:=53}" -: "${PDNS_local_address:=0.0.0.0}" -: "${PDNS_allow_from:=0.0.0.0/0}" +#### Function definitions +function deriveConfigValuesFromEnvrionement { + # Configure base vars + : "${PDNS_local_port:=53}" + : "${PDNS_local_address:=0.0.0.0}" + : "${PDNS_allow_from:=0.0.0.0/0}" + + export PDNS_local_port PDNS_local_address PDNS_allow_from +} -export PDNS_local_port PDNS_local_address PDNS_allow_from +### end of function definitions if [ -f /etc/fedora-release ]; then config_file=/etc/pdns-recursor/recursor.conf @@ -17,10 +22,18 @@ elif [ -f /etc/alpine-release ]; then pdns_user=recursor fi +if [ ${USE_EXISTING_CONFIG_FILE:-false} = 'false' ]; then + deriveConfigValuesFromEnvrionement + echo "generating config file from environment" + subvars --prefix 'PDNS_' < '/recursor.conf.tpl' > "${config_file}" + chown "${pdns_user}:" "${config_file}" + else + echo "using existing config file ${config_file}" +fi + # Create config file from template -subvars --prefix 'PDNS_' < '/recursor.conf.tpl' > "${config_file}" # Fix config file ownership -chown "${pdns_user}:" "${config_file}" + exec "$@" From d0c3929f6c5b1dd22fc7f0a320663e9fb70ffd9a Mon Sep 17 00:00:00 2001 From: Kai Schlachter Date: Sat, 4 May 2024 15:54:00 +0200 Subject: [PATCH 2/4] ammended changes to PostgreSQL init to provide the same features as the MySQL one. --- pdns-pgsql/docker-entrypoint.sh | 144 +++++++++++++++++++++----------- 1 file changed, 94 insertions(+), 50 deletions(-) diff --git a/pdns-pgsql/docker-entrypoint.sh b/pdns-pgsql/docker-entrypoint.sh index 68f7b95..9d160f8 100755 --- a/pdns-pgsql/docker-entrypoint.sh +++ b/pdns-pgsql/docker-entrypoint.sh @@ -2,25 +2,103 @@ set -eu -# Configure gpgsql env vars -: "${PDNS_gpgsql_host:=pgsql}" -: "${PDNS_gpgsql_port:=5432}" -: "${PDNS_gpgsql_user:=${PGSQL_ENV_POSTGRES_USER:-postgres}}" -: "${PDNS_gpgsql_password:=${PGSQL_ENV_POSTGRES_PASSWORD:-powerdns}}" -: "${PDNS_gpgsql_dbname:=${PGSQL_ENV_POSTGRES_DB:-powerdns}}" - -# Use first part of node name as database name suffix -if [ "${NODE_NAME:-}" ]; then - NODE_NAME=$(echo "${NODE_NAME}" | sed -e 's/\..*//' -e 's/-//') - PDNS_gpgsql_dbname="${PDNS_gpgsql_dbname}${NODE_NAME}" -fi +function derivePostgreSQLSettingsFromExistingConfigFile { + if [ ! -f /etc/pdns/pdns.conf ]; then + echo "Use of existing file /etc/pdns/pdns.conf requested but file does not exist!" + exit 1 + fi + PDNS_gpgsql_host=`sed -n 's/^gpgsql-host=\(.*\)/\1/p' < /etc/pdns/pdns.conf` + PDNS_gpgsql_port=`sed -n 's/^gpgsql-port=\(.*\)/\1/p' < /etc/pdns/pdns.conf` + PDNS_gpgsql_user=`sed -n 's/^gpgsql-user=\(.*\)/\1/p' < /etc/pdns/pdns.conf` + PDNS_gpgsql_password=`sed -n 's/^gpgsql-password=\(.*\)/\1/p' < /etc/pdns/pdns.conf` + PDNS_gpgsql_dbname=`sed -n 's/^gpgsql-dbname=\(.*\)/\1/p' < /etc/pdns/pdns.conf` +} + +function derivePostgreSQLSettingsFromEnvironment { + # Configure mysql env vars + # Configure gpgsql env vars + : "${PDNS_gpgsql_host:=pgsql}" + : "${PDNS_gpgsql_port:=5432}" + : "${PDNS_gpgsql_user:=${PGSQL_ENV_POSTGRES_USER:-postgres}}" + : "${PDNS_gpgsql_password:=${PGSQL_ENV_POSTGRES_PASSWORD:-powerdns}}" + : "${PDNS_gpgsql_dbname:=${PGSQL_ENV_POSTGRES_DB:-powerdns}}" + + # Use first part of node name as database name suffix + if [ "${NODE_NAME:-}" ]; then + NODE_NAME=$(echo "${NODE_NAME}" | sed -e 's/\..*//' -e 's/-//') + PDNS_gpgsql_dbname="${PDNS_gpgsql_dbname}${NODE_NAME}" + fi + + export PDNS_gpgsql_host PDNS_gpgsql_port PDNS_gpgsql_user PDNS_gpgsql_password PDNS_gpgsql_dbname +} + + + +function generatePostgreSQLCommand { + PGSQL_COMMAND="psql -h ${PDNS_gpgsql_host} -p ${PDNS_gpgsql_port} -U ${PDNS_gpgsql_user}" +} + +function createDatabaseIfRequested { + # Initialize DB if needed + if [ "${SKIP_DB_CREATE:-false}" != 'true' ]; then + echo "SELECT 'CREATE DATABASE ${PDNS_gpgsql_dbname}' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '${PDNS_gpgsql_dbname}')\gexec" | $PGSQL_COMMAND + fi +} + +function initDatabase { + if [ "${SKIP_DB_INIT:-false}" != 'true' ]; then + PGSQL_CHECK_IF_HAS_TABLE="SELECT COUNT(DISTINCT table_name) FROM information_schema.columns WHERE table_catalog = '${PDNS_gpgsql_dbname}' AND table_schema = 'public';" + PGSQL_NUM_TABLE=$($PGSQL_COMMAND -At -d "$PDNS_gpgsql_dbname" -c "$PGSQL_CHECK_IF_HAS_TABLE") + if [ "$PGSQL_NUM_TABLE" -eq 0 ]; then + echo "Database exists and has no tables yet, doing init"; + $PGSQL_COMMAND -d "$PDNS_gpgsql_dbname" < /usr/share/doc/pdns/schema.pgsql.sql + else + echo "Database exists but already has tables, will not try to init"; + fi + fi +} + +function initSuperslave { + if [ "${PDNS_superslave:-no}" = 'yes' ]; then + # Configure supermasters if needed + if [ "${SUPERMASTER_IPS:-}" ]; then + $PGSQL_COMMAND -d "$PDNS_gpgsql_dbname" -c 'TRUNCATE supermasters;' + PGSQL_INSERT_SUPERMASTERS='' + if [ "${SUPERMASTER_COUNT:-0}" -eq 0 ]; then + SUPERMASTER_COUNT=10 + fi + i=1; while [ $i -le "${SUPERMASTER_COUNT}" ]; do + SUPERMASTER_HOST=$(echo "${SUPERMASTER_HOSTS:-}" | awk -v col="$i" '{ print $col }') + SUPERMASTER_IP=$(echo "${SUPERMASTER_IPS}" | awk -v col="$i" '{ print $col }') + if [ -z "${SUPERMASTER_HOST:-}" ]; then + SUPERMASTER_HOST=$(hostname -f) + fi + if [ "${SUPERMASTER_IP:-}" ]; then + PGSQL_INSERT_SUPERMASTERS="${PGSQL_INSERT_SUPERMASTERS} INSERT INTO supermasters VALUES('${SUPERMASTER_IP}', '${SUPERMASTER_HOST}', 'admin');" + fi + i=$(( i + 1 )) + done + $PGSQL_COMMAND -d "$PDNS_gpgsql_dbname" -c "$PGSQL_INSERT_SUPERMASTERS" + fi + fi +} -export PDNS_gpgsql_host PDNS_gpgsql_port PDNS_gpgsql_user PDNS_gpgsql_password PDNS_gpgsql_dbname +function generateAndInstallConfigFileFromEnvironment { + # Create config file from template + subvars --prefix 'PDNS_' < '/pdns.conf.tpl' > '/etc/pdns/pdns.conf' +} +###End of function definitions +if [ ${USE_EXISTING_CONFIG_FILE:-false} = 'true' ]; then + derivePostgreSQLSettingsFromExistingConfigFile +else + derivePostgreSQLSettingsFromEnvironment +fi + +generatePostgreSQLCommand PGPASSWORD="${PDNS_gpgsql_password}" export PGPASSWORD -PGSQL_COMMAND="psql -h ${PDNS_gpgsql_host} -p ${PDNS_gpgsql_port} -U ${PDNS_gpgsql_user}" # Wait for pgsql to respond until $PGSQL_COMMAND -c ';' ; do @@ -28,43 +106,9 @@ until $PGSQL_COMMAND -c ';' ; do sleep 3 done -# Initialize DB if needed -if [ "${SKIP_DB_CREATE:-false}" != 'true' ]; then - echo "SELECT 'CREATE DATABASE ${PDNS_gpgsql_dbname}' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '${PDNS_gpgsql_dbname}')\gexec" | $PGSQL_COMMAND -fi - -PGSQL_CHECK_IF_HAS_TABLE="SELECT COUNT(DISTINCT table_name) FROM information_schema.columns WHERE table_catalog = '${PDNS_gpgsql_dbname}' AND table_schema = 'public';" -PGSQL_NUM_TABLE=$($PGSQL_COMMAND -At -d "$PDNS_gpgsql_dbname" -c "$PGSQL_CHECK_IF_HAS_TABLE") -if [ "$PGSQL_NUM_TABLE" -eq 0 ]; then - $PGSQL_COMMAND -d "$PDNS_gpgsql_dbname" < /usr/share/doc/pdns/schema.pgsql.sql -fi - -if [ "${PDNS_superslave:-no}" = 'yes' ]; then - # Configure supermasters if needed - if [ "${SUPERMASTER_IPS:-}" ]; then - $PGSQL_COMMAND -d "$PDNS_gpgsql_dbname" -c 'TRUNCATE supermasters;' - PGSQL_INSERT_SUPERMASTERS='' - if [ "${SUPERMASTER_COUNT:-0}" -eq 0 ]; then - SUPERMASTER_COUNT=10 - fi - i=1; while [ $i -le "${SUPERMASTER_COUNT}" ]; do - SUPERMASTER_HOST=$(echo "${SUPERMASTER_HOSTS:-}" | awk -v col="$i" '{ print $col }') - SUPERMASTER_IP=$(echo "${SUPERMASTER_IPS}" | awk -v col="$i" '{ print $col }') - if [ -z "${SUPERMASTER_HOST:-}" ]; then - SUPERMASTER_HOST=$(hostname -f) - fi - if [ "${SUPERMASTER_IP:-}" ]; then - PGSQL_INSERT_SUPERMASTERS="${PGSQL_INSERT_SUPERMASTERS} INSERT INTO supermasters VALUES('${SUPERMASTER_IP}', '${SUPERMASTER_HOST}', 'admin');" - fi - i=$(( i + 1 )) - done - $PGSQL_COMMAND -d "$PDNS_gpgsql_dbname" -c "$PGSQL_INSERT_SUPERMASTERS" - fi -fi +createDatabaseIfRequested +initDatabase unset PGPASSWORD -# Create config file from template -subvars --prefix 'PDNS_' < '/pdns.conf.tpl' > '/etc/pdns/pdns.conf' - exec "$@" From 013ddb8ff6c670aabdb864276f430b6853f37d07 Mon Sep 17 00:00:00 2001 From: Kai Schlachter Date: Mon, 6 May 2024 08:46:30 +0200 Subject: [PATCH 3/4] do not forget to init superslaves if needed. --- pdns-mysql/docker-entrypoint.sh | 1 + pdns-pgsql/docker-entrypoint.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/pdns-mysql/docker-entrypoint.sh b/pdns-mysql/docker-entrypoint.sh index c231969..41014bb 100755 --- a/pdns-mysql/docker-entrypoint.sh +++ b/pdns-mysql/docker-entrypoint.sh @@ -136,6 +136,7 @@ done createDatabaseIfRequested initDatabase migrateDatabaseTo47 +initSuperslave if [ ${USE_EXISTING_CONFIG_FILE:-false} = 'false' ]; then echo "(re-)generating config file from environment variables" diff --git a/pdns-pgsql/docker-entrypoint.sh b/pdns-pgsql/docker-entrypoint.sh index 9d160f8..1af437f 100755 --- a/pdns-pgsql/docker-entrypoint.sh +++ b/pdns-pgsql/docker-entrypoint.sh @@ -108,6 +108,7 @@ done createDatabaseIfRequested initDatabase +initSuperslave unset PGPASSWORD From 218b2a50cef0f60a3753ff249b67f2f9fb0fda09 Mon Sep 17 00:00:00 2001 From: Kai Schlachter Date: Mon, 6 May 2024 08:51:25 +0200 Subject: [PATCH 4/4] typos and really do install config file by environment. --- pdns-pgsql/docker-entrypoint.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pdns-pgsql/docker-entrypoint.sh b/pdns-pgsql/docker-entrypoint.sh index 1af437f..7db548a 100755 --- a/pdns-pgsql/docker-entrypoint.sh +++ b/pdns-pgsql/docker-entrypoint.sh @@ -15,7 +15,6 @@ function derivePostgreSQLSettingsFromExistingConfigFile { } function derivePostgreSQLSettingsFromEnvironment { - # Configure mysql env vars # Configure gpgsql env vars : "${PDNS_gpgsql_host:=pgsql}" : "${PDNS_gpgsql_port:=5432}" @@ -110,6 +109,12 @@ createDatabaseIfRequested initDatabase initSuperslave +if [ ${USE_EXISTING_CONFIG_FILE:-false} = 'false' ]; then + echo "(re-)generating config file from environment variables" + generateAndInstallConfigFileFromEnvironment +fi + + unset PGPASSWORD exec "$@"