github.com/diadata-org/diadata@v1.4.593/deployments/config/postgres-docker-entrypoint.sh (about) 1 #!/usr/bin/env bash 2 echo "Starting entrypoint script (${BASH_SOURCE[0]}) ..." 3 echo "Current user: $(whoami)" 4 echo "Current working directory: $(pwd)" 5 echo; echo "Injected scripts /docker-entrypoint-initdb.d/:" 6 ls -la /docker-entrypoint-initdb.d/ 7 echo; echo "Injected volumes (/mnt/):" 8 ls -la /mnt/ 9 set -Eeo pipefail 10 # TODO swap to -Eeuo pipefail above (after handling all potentially-unset variables) 11 12 # usage: file_env VAR [DEFAULT] 13 # ie: file_env 'XYZ_DB_PASSWORD' 'example' 14 # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of 15 # "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) 16 file_env() { 17 local var="$1" 18 local fileVar="${var}_FILE" 19 local def="${2:-}" 20 if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then 21 printf >&2 'error: both %s and %s are set (but are exclusive)\n' "$var" "$fileVar" 22 exit 1 23 fi 24 local val="$def" 25 if [ "${!var:-}" ]; then 26 val="${!var}" 27 elif [ "${!fileVar:-}" ]; then 28 val="$(< "${!fileVar}")" 29 fi 30 export "$var"="$val" 31 unset "$fileVar" 32 } 33 34 # check to see if this file is being run or sourced from another script 35 _is_sourced() { 36 # https://unix.stackexchange.com/a/215279 37 [ "${#FUNCNAME[@]}" -ge 2 ] \ 38 && [ "${FUNCNAME[0]}" = '_is_sourced' ] \ 39 && [ "${FUNCNAME[1]}" = 'source' ] 40 } 41 42 # used to create initial postgres directories and if run as root, ensure ownership to the "postgres" user 43 docker_create_db_directories() { 44 local user; user="$(id -u)" 45 46 mkdir -p "$PGDATA" 47 # ignore failure since there are cases where we can't chmod (and PostgreSQL might fail later anyhow - it's picky about permissions of this directory) 48 chmod 00700 "$PGDATA" || : 49 50 # ignore failure since it will be fine when using the image provided directory; see also https://github.com/docker-library/postgres/pull/289 51 mkdir -p /var/run/postgresql || : 52 chmod 03775 /var/run/postgresql || : 53 54 # Create the transaction log directory before initdb is run so the directory is owned by the correct user 55 if [ -n "${POSTGRES_INITDB_WALDIR:-}" ]; then 56 mkdir -p "$POSTGRES_INITDB_WALDIR" 57 if [ "$user" = '0' ]; then 58 find "$POSTGRES_INITDB_WALDIR" \! -user postgres -exec chown postgres '{}' + 59 fi 60 chmod 700 "$POSTGRES_INITDB_WALDIR" 61 fi 62 63 # allow the container to be started with `--user` 64 if [ "$user" = '0' ]; then 65 find "$PGDATA" \! -user postgres -exec chown postgres '{}' + 66 find /var/run/postgresql \! -user postgres -exec chown postgres '{}' + 67 fi 68 } 69 70 # initialize empty PGDATA directory with new database via 'initdb' 71 # arguments to `initdb` can be passed via POSTGRES_INITDB_ARGS or as arguments to this function 72 # `initdb` automatically creates the "postgres", "template0", and "template1" dbnames 73 # this is also where the database user is created, specified by `POSTGRES_USER` env 74 docker_init_database_dir() { 75 # "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary 76 # see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html 77 local uid; uid="$(id -u)" 78 if ! getent passwd "$uid" &> /dev/null; then 79 # see if we can find a suitable "libnss_wrapper.so" (https://salsa.debian.org/sssd-team/nss-wrapper/-/commit/b9925a653a54e24d09d9b498a2d913729f7abb15) 80 local wrapper 81 for wrapper in {/usr,}/lib{/*,}/libnss_wrapper.so; do 82 if [ -s "$wrapper" ]; then 83 NSS_WRAPPER_PASSWD="$(mktemp)" 84 NSS_WRAPPER_GROUP="$(mktemp)" 85 export LD_PRELOAD="$wrapper" NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP 86 local gid; gid="$(id -g)" 87 printf 'postgres:x:%s:%s:PostgreSQL:%s:/bin/false\n' "$uid" "$gid" "$PGDATA" > "$NSS_WRAPPER_PASSWD" 88 printf 'postgres:x:%s:\n' "$gid" > "$NSS_WRAPPER_GROUP" 89 break 90 fi 91 done 92 fi 93 94 if [ -n "${POSTGRES_INITDB_WALDIR:-}" ]; then 95 set -- --waldir "$POSTGRES_INITDB_WALDIR" "$@" 96 fi 97 98 # --pwfile refuses to handle a properly-empty file (hence the "\n"): https://github.com/docker-library/postgres/issues/1025 99 eval 'initdb --username="$POSTGRES_USER" --pwfile=<(printf "%s\n" "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"' "$@"' 100 101 # unset/cleanup "nss_wrapper" bits 102 if [[ "${LD_PRELOAD:-}" == */libnss_wrapper.so ]]; then 103 rm -f "$NSS_WRAPPER_PASSWD" "$NSS_WRAPPER_GROUP" 104 unset LD_PRELOAD NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP 105 fi 106 } 107 108 # print large warning if POSTGRES_PASSWORD is long 109 # error if both POSTGRES_PASSWORD is empty and POSTGRES_HOST_AUTH_METHOD is not 'trust' 110 # print large warning if POSTGRES_HOST_AUTH_METHOD is set to 'trust' 111 # assumes database is not set up, ie: [ -z "$DATABASE_ALREADY_EXISTS" ] 112 docker_verify_minimum_env() { 113 # check password first so we can output the warning before postgres 114 # messes it up 115 if [ "${#POSTGRES_PASSWORD}" -ge 100 ]; then 116 cat >&2 <<-'EOWARN' 117 118 WARNING: The supplied POSTGRES_PASSWORD is 100+ characters. 119 120 This will not work if used via PGPASSWORD with "psql". 121 122 https://www.postgresql.org/message-id/flat/E1Rqxp2-0004Qt-PL%40wrigleys.postgresql.org (BUG #6412) 123 https://github.com/docker-library/postgres/issues/507 124 125 EOWARN 126 fi 127 if [ -z "$POSTGRES_PASSWORD" ] && [ 'trust' != "$POSTGRES_HOST_AUTH_METHOD" ]; then 128 # The - option suppresses leading tabs but *not* spaces. :) 129 cat >&2 <<-'EOE' 130 Error: Database is uninitialized and superuser password is not specified. 131 You must specify POSTGRES_PASSWORD to a non-empty value for the 132 superuser. For example, "-e POSTGRES_PASSWORD=password" on "docker run". 133 134 You may also use "POSTGRES_HOST_AUTH_METHOD=trust" to allow all 135 connections without a password. This is *not* recommended. 136 137 See PostgreSQL documentation about "trust": 138 https://www.postgresql.org/docs/current/auth-trust.html 139 EOE 140 exit 1 141 fi 142 if [ 'trust' = "$POSTGRES_HOST_AUTH_METHOD" ]; then 143 cat >&2 <<-'EOWARN' 144 ******************************************************************************** 145 WARNING: POSTGRES_HOST_AUTH_METHOD has been set to "trust". This will allow 146 anyone with access to the Postgres port to access your database without 147 a password, even if POSTGRES_PASSWORD is set. See PostgreSQL 148 documentation about "trust": 149 https://www.postgresql.org/docs/current/auth-trust.html 150 In Docker's default configuration, this is effectively any other 151 container on the same system. 152 153 It is not recommended to use POSTGRES_HOST_AUTH_METHOD=trust. Replace 154 it with "-e POSTGRES_PASSWORD=password" instead to set a password in 155 "docker run". 156 ******************************************************************************** 157 EOWARN 158 fi 159 } 160 161 # usage: docker_process_init_files [file [file [...]]] 162 # ie: docker_process_init_files /always-initdb.d/* 163 # process initializer files, based on file extensions and permissions 164 docker_process_init_files() { 165 # psql here for backwards compatibility "${psql[@]}" 166 psql=( docker_process_sql ) 167 168 printf '\n' 169 local f 170 for f; do 171 case "$f" in 172 *.sh) 173 # https://github.com/docker-library/postgres/issues/450#issuecomment-393167936 174 # https://github.com/docker-library/postgres/pull/452 175 if [ -x "$f" ]; then 176 printf '%s: running %s\n' "$0" "$f" 177 "$f" 178 else 179 printf '%s: sourcing %s\n' "$0" "$f" 180 . "$f" 181 fi 182 ;; 183 *.sql) printf '%s: running %s\n' "$0" "$f"; docker_process_sql -f "$f"; printf '\n' ;; 184 *.sql.gz) printf '%s: running %s\n' "$0" "$f"; gunzip -c "$f" | docker_process_sql; printf '\n' ;; 185 *.sql.xz) printf '%s: running %s\n' "$0" "$f"; xzcat "$f" | docker_process_sql; printf '\n' ;; 186 *.sql.zst) printf '%s: running %s\n' "$0" "$f"; zstd -dc "$f" | docker_process_sql; printf '\n' ;; 187 *) printf '%s: ignoring %s\n' "$0" "$f" ;; 188 esac 189 printf '\n' 190 done 191 } 192 193 # Execute sql script, passed via stdin (or -f flag of pqsl) 194 # usage: docker_process_sql [psql-cli-args] 195 # ie: docker_process_sql --dbname=mydb <<<'INSERT ...' 196 # ie: docker_process_sql -f my-file.sql 197 # ie: docker_process_sql <my-file.sql 198 docker_process_sql() { 199 local query_runner=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password --no-psqlrc ) 200 if [ -n "$POSTGRES_DB" ]; then 201 query_runner+=( --dbname "$POSTGRES_DB" ) 202 fi 203 204 PGHOST= PGHOSTADDR= "${query_runner[@]}" "$@" 205 } 206 207 # create initial database 208 # uses environment variables for input: POSTGRES_DB 209 docker_setup_db() { 210 local dbAlreadyExists 211 dbAlreadyExists="$( 212 POSTGRES_DB= docker_process_sql --dbname postgres --set db="$POSTGRES_DB" --tuples-only <<-'EOSQL' 213 SELECT 1 FROM pg_database WHERE datname = :'db' ; 214 EOSQL 215 )" 216 if [ -z "$dbAlreadyExists" ]; then 217 POSTGRES_DB= docker_process_sql --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL' 218 CREATE DATABASE :"db" ; 219 EOSQL 220 printf '\n' 221 fi 222 } 223 224 # Loads various settings that are used elsewhere in the script 225 # This should be called before any other functions 226 docker_setup_env() { 227 file_env 'POSTGRES_PASSWORD' 228 229 file_env 'POSTGRES_USER' 'postgres' 230 file_env 'POSTGRES_DB' "$POSTGRES_USER" 231 file_env 'POSTGRES_INITDB_ARGS' 232 : "${POSTGRES_HOST_AUTH_METHOD:=}" 233 234 declare -g DATABASE_ALREADY_EXISTS 235 # look specifically for PG_VERSION, as it is expected in the DB dir 236 if [ -s "$PGDATA/PG_VERSION" ]; then 237 DATABASE_ALREADY_EXISTS='true' 238 fi 239 } 240 241 # append POSTGRES_HOST_AUTH_METHOD to pg_hba.conf for "host" connections 242 # all arguments will be passed along as arguments to `postgres` for getting the value of 'password_encryption' 243 pg_setup_hba_conf() { 244 # default authentication method is md5 on versions before 14 245 # https://www.postgresql.org/about/news/postgresql-14-released-2318/ 246 if [ "$1" = 'postgres' ]; then 247 shift 248 fi 249 local auth 250 # check the default/configured encryption and use that as the auth method 251 auth="$(postgres -C password_encryption "$@")" 252 : "${POSTGRES_HOST_AUTH_METHOD:=$auth}" 253 { 254 printf '\n' 255 if [ 'trust' = "$POSTGRES_HOST_AUTH_METHOD" ]; then 256 printf '# warning trust is enabled for all connections\n' 257 printf '# see https://www.postgresql.org/docs/12/auth-trust.html\n' 258 fi 259 printf 'host all all all %s\n' "$POSTGRES_HOST_AUTH_METHOD" 260 } >> "$PGDATA/pg_hba.conf" 261 } 262 263 # start socket-only postgresql server for setting up or running scripts 264 # all arguments will be passed along as arguments to `postgres` (via pg_ctl) 265 docker_temp_server_start() { 266 if [ "$1" = 'postgres' ]; then 267 shift 268 fi 269 270 # internal start of server in order to allow setup using psql client 271 # does not listen on external TCP/IP and waits until start finishes 272 set -- "$@" -c listen_addresses='' -p "${PGPORT:-5432}" 273 274 PGUSER="${PGUSER:-$POSTGRES_USER}" \ 275 pg_ctl -D "$PGDATA" \ 276 -o "$(printf '%q ' "$@")" \ 277 -w start 278 } 279 280 # stop postgresql server after done setting up user and running scripts 281 docker_temp_server_stop() { 282 PGUSER="${PGUSER:-postgres}" \ 283 pg_ctl -D "$PGDATA" -m fast -w stop 284 } 285 286 # check arguments for an option that would cause postgres to stop 287 # return true if there is one 288 _pg_want_help() { 289 local arg 290 for arg; do 291 case "$arg" in 292 # postgres --help | grep 'then exit' 293 # leaving out -C on purpose since it always fails and is unhelpful: 294 # postgres: could not access the server configuration file "/var/lib/postgresql/data/postgresql.conf": No such file or directory 295 -'?'|--help|--describe-config|-V|--version) 296 return 0 297 ;; 298 esac 299 done 300 return 1 301 } 302 303 _main() { 304 # if first arg looks like a flag, assume we want to run postgres server 305 if [ "${1:0:1}" = '-' ]; then 306 set -- postgres "$@" 307 fi 308 309 if [ "$1" = 'postgres' ] && ! _pg_want_help "$@"; then 310 docker_setup_env 311 # setup data directories and permissions (when run as root) 312 docker_create_db_directories 313 if [ "$(id -u)" = '0' ]; then 314 # then restart script as postgres user 315 exec su-exec postgres "$BASH_SOURCE" "$@" 316 fi 317 318 # only run initialization on an empty data directory 319 if [ -z "$DATABASE_ALREADY_EXISTS" ]; then 320 docker_verify_minimum_env 321 322 # check dir permissions to reduce likelihood of half-initialized database 323 ls /docker-entrypoint-initdb.d/ > /dev/null 324 325 docker_init_database_dir 326 pg_setup_hba_conf "$@" 327 328 # PGPASSWORD is required for psql when authentication is required for 'local' connections via pg_hba.conf and is otherwise harmless 329 # e.g. when '--auth=md5' or '--auth-local=md5' is used in POSTGRES_INITDB_ARGS 330 export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}" 331 docker_temp_server_start "$@" 332 333 docker_setup_db 334 docker_process_init_files /docker-entrypoint-initdb.d/* 335 336 docker_temp_server_stop 337 unset PGPASSWORD 338 339 cat <<-'EOM' 340 341 PostgreSQL init process complete; ready for start up. 342 343 EOM 344 else 345 cat <<-'EOM' 346 347 PostgreSQL Database directory appears to contain a database; Skipping initialization 348 349 EOM 350 fi 351 fi 352 } 353 354 if ! _is_sourced; then 355 _main "$@" 356 fi