From e2dd738280ad4be1b1b50378f49f7e958813666e Mon Sep 17 00:00:00 2001 From: kon-foo <25391223+kon-foo@users.noreply.github.com> Date: Tue, 10 Feb 2026 20:18:26 +0100 Subject: [PATCH 1/7] add su-exec and remove hardcoded user --- Dockerfile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8277253b6f3..fd517c43546 100644 --- a/Dockerfile +++ b/Dockerfile @@ -165,7 +165,8 @@ RUN apk update && apk upgrade && \ tzdata \ libedit \ libldap \ - libcap && \ + libcap \ + su-exec && \ rm -rf /var/cache/apk/* # Copy in the Python packages @@ -206,8 +207,6 @@ RUN /venv/bin/python3 -m pip install --no-cache-dir gunicorn==23.0.0 && \ echo "pgadmin ALL = NOPASSWD: /usr/sbin/postfix start" > /etc/sudoers.d/postfix && \ echo "pgadminr ALL = NOPASSWD: /usr/sbin/postfix start" >> /etc/sudoers.d/postfix -USER 5050 - # Finish up VOLUME /var/lib/pgadmin EXPOSE 80 443 From e5403364b08751b244e87f5d472411ddb3c3e3a6 Mon Sep 17 00:00:00 2001 From: kon-foo <25391223+kon-foo@users.noreply.github.com> Date: Tue, 10 Feb 2026 20:35:14 +0100 Subject: [PATCH 2/7] rassign pgadmin user to pass PUID and PGID, chwon required files, use su-exec to drop privileges --- pkg/docker/entrypoint.sh | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/pkg/docker/entrypoint.sh b/pkg/docker/entrypoint.sh index 38b4e478538..7bd02627182 100755 --- a/pkg/docker/entrypoint.sh +++ b/pkg/docker/entrypoint.sh @@ -1,4 +1,18 @@ #!/usr/bin/env bash +PUID=${PUID:-5050} +PGID=${PGID:-0} + +if [ "$(id -u)" = "0" ]; then + # Ensure a group with the target GID exists + if ! getent group "$PGID" > /dev/null 2>&1; then + addgroup -g "$PGID" pggroup + fi + + # Reassign the pgadmin user to the desired UID/GID + usermod -o -u "$PUID" -g "$PGID" pgadmin 2>/dev/null || true + + echo "pgAdmin will run as UID=$PUID, GID=$PGID" +fi # Fixup the passwd file, in case we're on OpenShift if ! whoami > /dev/null 2>&1; then @@ -178,6 +192,10 @@ fi # to define the Gunicorn worker timeout TIMEOUT=$(cd /pgadmin4 && /venv/bin/python3 -c 'import config; print(config.SESSION_EXPIRATION_TIME * 60 * 60 * 24)') +if [ "$(id -u)" = "0" ]; then + chown -R "$PUID:$PGID" /run/pgadmin /var/lib/pgadmin /pgadmin4/config_distro.py +fi + # NOTE: currently pgadmin can run only with 1 worker due to sessions implementation # Using --threads to have multi-threaded single-process worker @@ -192,7 +210,7 @@ else fi if [ -n "${PGADMIN_ENABLE_TLS}" ]; then - exec /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --timeout "${TIMEOUT}" --bind "${BIND_ADDRESS}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" --keyfile /certs/server.key --certfile /certs/server.cert -c gunicorn_config.py run_pgadmin:app + exec su-exec "$PUID:$PGID" /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --timeout "${TIMEOUT}" --bind "${BIND_ADDRESS}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" --keyfile /certs/server.key --certfile /certs/server.cert -c gunicorn_config.py run_pgadmin:app else - exec /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --limit-request-fields "${GUNICORN_LIMIT_REQUEST_FIELDS:-100}" --limit-request-field_size "${GUNICORN_LIMIT_REQUEST_FIELD_SIZE:-8190}" --timeout "${TIMEOUT}" --bind "${BIND_ADDRESS}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" -c gunicorn_config.py run_pgadmin:app + exec su-exec "$PUID:$PGID" /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --limit-request-fields "${GUNICORN_LIMIT_REQUEST_FIELDS:-100}" --limit-request-field_size "${GUNICORN_LIMIT_REQUEST_FIELD_SIZE:-8190}" --timeout "${TIMEOUT}" --bind "${BIND_ADDRESS}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" -c gunicorn_config.py run_pgadmin:app fi From 6e8fe13a8280226f0e8e90d9cee892cea2b5f360 Mon Sep 17 00:00:00 2001 From: kon-foo <25391223+kon-foo@users.noreply.github.com> Date: Tue, 10 Feb 2026 21:10:38 +0100 Subject: [PATCH 3/7] set SU_EXEC var to prevent running as 5050:0 when someone uses the Docker --user flag --- pkg/docker/entrypoint.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/docker/entrypoint.sh b/pkg/docker/entrypoint.sh index 7bd02627182..75c7b8da0f1 100755 --- a/pkg/docker/entrypoint.sh +++ b/pkg/docker/entrypoint.sh @@ -11,7 +11,11 @@ if [ "$(id -u)" = "0" ]; then # Reassign the pgadmin user to the desired UID/GID usermod -o -u "$PUID" -g "$PGID" pgadmin 2>/dev/null || true + # Compose su-exec command + SU_EXEC="su-exec $PUID:$PGID" echo "pgAdmin will run as UID=$PUID, GID=$PGID" +else + SU_EXEC="" fi # Fixup the passwd file, in case we're on OpenShift @@ -210,7 +214,7 @@ else fi if [ -n "${PGADMIN_ENABLE_TLS}" ]; then - exec su-exec "$PUID:$PGID" /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --timeout "${TIMEOUT}" --bind "${BIND_ADDRESS}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" --keyfile /certs/server.key --certfile /certs/server.cert -c gunicorn_config.py run_pgadmin:app + exec $SU_EXEC /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --timeout "${TIMEOUT}" --bind "${BIND_ADDRESS}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" --keyfile /certs/server.key --certfile /certs/server.cert -c gunicorn_config.py run_pgadmin:app else - exec su-exec "$PUID:$PGID" /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --limit-request-fields "${GUNICORN_LIMIT_REQUEST_FIELDS:-100}" --limit-request-field_size "${GUNICORN_LIMIT_REQUEST_FIELD_SIZE:-8190}" --timeout "${TIMEOUT}" --bind "${BIND_ADDRESS}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" -c gunicorn_config.py run_pgadmin:app + exec $SU_EXEC /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --limit-request-fields "${GUNICORN_LIMIT_REQUEST_FIELDS:-100}" --limit-request-field_size "${GUNICORN_LIMIT_REQUEST_FIELD_SIZE:-8190}" --timeout "${TIMEOUT}" --bind "${BIND_ADDRESS}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" -c gunicorn_config.py run_pgadmin:app fi From d518f88502da7f74dd664aa4c599b28ae6389f57 Mon Sep 17 00:00:00 2001 From: kon-foo <25391223+kon-foo@users.noreply.github.com> Date: Fri, 13 Feb 2026 21:21:37 +0100 Subject: [PATCH 4/7] chown /certs when using PUID/GUID --- pkg/docker/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/docker/entrypoint.sh b/pkg/docker/entrypoint.sh index 75c7b8da0f1..a2d53ae45fb 100755 --- a/pkg/docker/entrypoint.sh +++ b/pkg/docker/entrypoint.sh @@ -197,7 +197,7 @@ fi TIMEOUT=$(cd /pgadmin4 && /venv/bin/python3 -c 'import config; print(config.SESSION_EXPIRATION_TIME * 60 * 60 * 24)') if [ "$(id -u)" = "0" ]; then - chown -R "$PUID:$PGID" /run/pgadmin /var/lib/pgadmin /pgadmin4/config_distro.py + chown -R "$PUID:$PGID" /run/pgadmin /var/lib/pgadmin /pgadmin4/config_distro.py /certs fi # NOTE: currently pgadmin can run only with 1 worker due to sessions implementation From 7a2d48d74defbc484fc22befbeb116afb50e4707 Mon Sep 17 00:00:00 2001 From: kon-foo <25391223+kon-foo@users.noreply.github.com> Date: Fri, 20 Feb 2026 17:28:25 +0100 Subject: [PATCH 5/7] add safe_chown function to check if dir/file exists and if it already has the correct ownership before chowning it --- pkg/docker/entrypoint.sh | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/pkg/docker/entrypoint.sh b/pkg/docker/entrypoint.sh index a2d53ae45fb..f920d2437ca 100755 --- a/pkg/docker/entrypoint.sh +++ b/pkg/docker/entrypoint.sh @@ -27,6 +27,27 @@ if ! whoami > /dev/null 2>&1; then fi fi +# Helper: chown a path only if it exists and isn't already owned correctly +safe_chown() { + local target="$1" + local owner="$2:$3" # UID:GID + + # Skip if path doesn't exist + [ -e "$target" ] || return 0 + + # Get current ownership + local current_uid current_gid + current_uid=$(stat -c '%u' "$target") + current_gid=$(stat -c '%g' "$target") + + # Skip if already owned correctly + if [ "$current_uid" = "$1" ] && [ "$current_gid" = "$2" ]; then + return 0 + fi + + chown -R "$owner" "$target" +} + # usage: file_env VAR [DEFAULT] ie: file_env 'XYZ_DB_PASSWORD' 'example' # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of # "$XYZ_DB_PASSWORD" from a file, for Docker's secrets feature) @@ -197,7 +218,9 @@ fi TIMEOUT=$(cd /pgadmin4 && /venv/bin/python3 -c 'import config; print(config.SESSION_EXPIRATION_TIME * 60 * 60 * 24)') if [ "$(id -u)" = "0" ]; then - chown -R "$PUID:$PGID" /run/pgadmin /var/lib/pgadmin /pgadmin4/config_distro.py /certs + for path in /run/pgadmin /var/lib/pgadmin "$CONFIG_DISTRO_FILE_PATH" /certs; do + safe_chown "$path" "$PUID" "$PGID" + done fi # NOTE: currently pgadmin can run only with 1 worker due to sessions implementation From d3f2eb5d48859e2fcec490600c846ec4a41de093 Mon Sep 17 00:00:00 2001 From: kon-foo <25391223+kon-foo@users.noreply.github.com> Date: Fri, 20 Feb 2026 18:16:50 +0100 Subject: [PATCH 6/7] fix: check against correct parameters --- pkg/docker/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/docker/entrypoint.sh b/pkg/docker/entrypoint.sh index f920d2437ca..5dba41bdc6e 100755 --- a/pkg/docker/entrypoint.sh +++ b/pkg/docker/entrypoint.sh @@ -41,7 +41,7 @@ safe_chown() { current_gid=$(stat -c '%g' "$target") # Skip if already owned correctly - if [ "$current_uid" = "$1" ] && [ "$current_gid" = "$2" ]; then + if [ "$current_uid" = "$2" ] && [ "$current_gid" = "$3" ]; then return 0 fi From 7ee0af1535c5ab7683b7576d3741ef247b25c2c5 Mon Sep 17 00:00:00 2001 From: kon-foo <25391223+kon-foo@users.noreply.github.com> Date: Fri, 20 Feb 2026 20:38:09 +0100 Subject: [PATCH 7/7] issue warning when usermod fails --- pkg/docker/entrypoint.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/docker/entrypoint.sh b/pkg/docker/entrypoint.sh index 5dba41bdc6e..37d2e4c498d 100755 --- a/pkg/docker/entrypoint.sh +++ b/pkg/docker/entrypoint.sh @@ -9,7 +9,8 @@ if [ "$(id -u)" = "0" ]; then fi # Reassign the pgadmin user to the desired UID/GID - usermod -o -u "$PUID" -g "$PGID" pgadmin 2>/dev/null || true + usermod -o -u "$PUID" -g "$PGID" pgadmin 2>&1 || \ + echo "WARNING: usermod failed for UID=$PUID GID=$PGID" # Compose su-exec command SU_EXEC="su-exec $PUID:$PGID"