Skip to content
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
### Minor changes

* **2026-10-09**
* Fix the container start when the mounted directory belongs to an ID used by a system account of the image (e.g. 998 for a `gitlab-runner` user): the commands were run with this account (no home directory, no sudo) instead of the `docker` user
* Fix `DOCKER_USER` set to an ID used by a system account of the image (`usermod: UID already exists`)
* Fix `php` run by a user that cannot use sudo (e.g. `sudo -u www-data php ...`): sudo password errors, and failure when a `PHP_*` variable had changed
* Fix PHP warnings (e.g. an extension that cannot be loaded) breaking the container start: they were written in `generated_conf.ini`, in the crontab and in the startup commands
* Fix the fpm variant stop: PHP-FPM is now stopped gracefully (`SIGQUIT`: running requests are completed) and port 9000 is exposed
* Fix stop requests sent while the apache or fpm container is starting: the stop signal (`SIGWINCH` / `SIGQUIT`) was ignored by the entrypoint and the container was killed after the stop timeout
* Fix Apache modules listed in the documentation but rejected by `APACHE_EXTENSION_*`: `brotli`, `cern_meta`, `imagemap`, `md`, `proxy_hcheck`, `proxy_uwsgi`, `socache_redis`
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,10 @@ working directory (`/var/www/html` for Apache/PHP-FPM, or `/usr/src/app` for CLI
you want to run commands as this user. So it will **dynamically change the ID of the docker user** to match the ID of
the current working directory user.

If this ID is already used by a system account of the image (e.g. `998`, used by `systemd-network` in the image and
often by the `gitlab-runner` user on CI hosts), this system account is moved to another ID: the commands are still run
by the `docker` user.

Furthermore, the image is changing the Apache default user/group to be `docker/docker` (instead if `www-data/www-data`).
So Apache will run with the same rights as the user on your host.

Expand Down
2 changes: 2 additions & 0 deletions tests-suite/assets/php-startup-warning.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
; Loading an extension that does not exist makes PHP display a startup warning
extension=does_not_exist
10 changes: 10 additions & 0 deletions tests-suite/tool-startup-command.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,13 @@ test_withFile() {
"${REPO}:${TAG_PREFIX}${PHP_VERSION}-${BRANCH}-slim-${BRANCH_VARIANT}${ARCH_SUFFIX}" php -m 2>/dev/null | grep -q "startup.sh executed"
assert_equals "0" "$?"
}
############################################################
## Tests that a PHP startup warning does not prevent the container from starting
############################################################
test_phpStartupWarning() {
RESULT="$(docker run ${RUN_OPTIONS} --rm -e STARTUP_COMMAND_1="echo startup-ok" \
-v "${SCRIPT_DIR}/assets/php-startup-warning.ini":"/etc/php/${PHP_VERSION}/cli/conf.d/99-startup-warning.ini" \
"${REPO}:${TAG_PREFIX}${PHP_VERSION}-${BRANCH}-slim-${BRANCH_VARIANT}${ARCH_SUFFIX}" sleep 1 2>/dev/null)"
assert_equals "0" "$?" "Docker run failed"
assert_equals "startup-ok" "$RESULT"
}
28 changes: 27 additions & 1 deletion tests-suite/users-rights.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ test_defaultUserCanWriteOnStdoutAndStderr() {
############################################################
## It's also works for users with existing IDs in the container
############################################################
test_defaultUserCanWriteOnStdoutAndStderr() {
test_defaultUserCanBeAnExistingUser() {
mkdir -p "${TMP_DIR}/user33"
cat << EOF > "${TMP_DIR}/user33/composer.json"
{
Expand All @@ -58,6 +58,32 @@ EOF
}


############################################################
## The system accounts of the image (systemd-network, polkitd...)
## do not take the place of the default user when they have the
## ID of the mounted directory (e.g. 998 for gitlab-runner)
############################################################
test_defaultUserTakesUidOfSystemAccount() {
mkdir -p "${TMP_DIR}/user998"
docker run ${RUN_OPTIONS} --rm -v /tmp:/tmp busybox chown 998:998 "${TMP_DIR}/user998" > /dev/null 2>&1
RESULT="$(docker run ${RUN_OPTIONS} --rm -v "${TMP_DIR}/user998":"${CONTAINER_CWD}" -e STARTUP_COMMAND_1='touch ~/.startup_done' \
"${REPO}:${TAG_PREFIX}${PHP_VERSION}-${BRANCH}-slim-${BRANCH_VARIANT}${ARCH_SUFFIX}" \
bash -c 'echo "$(id -un):$(id -ur)"')"
assert_equals "0" "$?" "Docker run failed"
assert_equals "docker:998" "${RESULT}" "Default user mismatch with a mounted directory owned by a system account ID"
}

############################################################
## Users that cannot use sudo can run PHP, even when a PHP_*
## environment variable has changed
############################################################
test_userWithoutSudoCanRunPhp() {
RESULT="$(docker run ${RUN_OPTIONS} --rm "${REPO}:${TAG_PREFIX}${PHP_VERSION}-${BRANCH}-slim-${BRANCH_VARIANT}${ARCH_SUFFIX}" \
sudo -E -u www-data PHP_INI_MEMORY_LIMIT=1G php -r 'echo "OK";' 2>&1)"
assert_equals "0" "$?" "Docker run failed"
assert_equals "OK" "${RESULT}" "PHP run as www-data failed or printed errors"
}

setup_suite() {
export TMP_DIR="$(mktemp -d)"
if [[ $VARIANT == cli* ]]; then export CONTAINER_CWD=/usr/src/app; else export CONTAINER_CWD=/var/www/html; fi
Expand Down
4 changes: 4 additions & 0 deletions utils/README.blueprint.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,10 @@ working directory (`/var/www/html` for Apache/PHP-FPM, or `/usr/src/app` for CLI
you want to run commands as this user. So it will **dynamically change the ID of the docker user** to match the ID of
the current working directory user.

If this ID is already used by a system account of the image (e.g. `998`, used by `systemd-network` in the image and
often by the `gitlab-runner` user on CI hosts), this system account is moved to another ID: the commands are still run
by the `docker` user.

Furthermore, the image is changing the Apache default user/group to be `docker/docker` (instead if `www-data/www-data`).
So Apache will run with the same rights as the user on your host.

Expand Down
30 changes: 24 additions & 6 deletions utils/docker-entrypoint-as-root.sh
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,24 @@ fi

# DOCKER_USER is a user name if the user exists in the container, otherwise, it is a user ID (from a user on the host).

# A system account of the image that has the ID of DOCKER_USER is moved to a free ID: the docker user takes this ID
SYS_UID_MIN=$(awk '$1 == "SYS_UID_MIN" { print $2 }' /etc/login.defs)
SYS_UID_MAX=$(awk '$1 == "SYS_UID_MAX" { print $2 }' /etc/login.defs)
SYS_UID_MIN=${SYS_UID_MIN:-100}
SYS_UID_MAX=${SYS_UID_MAX:-999}
SYSTEM_ACCOUNT=$(getent passwd "$DOCKER_USER" | cut -d: -f1,3)
SYSTEM_ACCOUNT_NAME=${SYSTEM_ACCOUNT%%:*}
SYSTEM_ACCOUNT_ID=${SYSTEM_ACCOUNT##*:}
if [[ -n "$SYSTEM_ACCOUNT" ]] && [[ "$SYSTEM_ACCOUNT_NAME" != "docker" ]] && (( SYSTEM_ACCOUNT_ID >= SYS_UID_MIN && SYSTEM_ACCOUNT_ID <= SYS_UID_MAX )); then
FREE_ID=$SYS_UID_MAX
while getent passwd "$FREE_ID" > /dev/null; do
FREE_ID=$((FREE_ID - 1))
done
sed -i "s/^${SYSTEM_ACCOUNT_NAME}:\([^:]*\):${SYSTEM_ACCOUNT_ID}:/${SYSTEM_ACCOUNT_NAME}:\1:${FREE_ID}:/" /etc/passwd
DOCKER_USER=$SYSTEM_ACCOUNT_ID
fi
unset SYS_UID_MIN SYS_UID_MAX SYSTEM_ACCOUNT SYSTEM_ACCOUNT_NAME SYSTEM_ACCOUNT_ID FREE_ID

# If DOCKER_USER is an ID, let's
if [[ "$DOCKER_USER" =~ ^[0-9]+$ ]] ; then
# MAIN_DIR_USER is a user ID.
Expand Down Expand Up @@ -123,14 +141,14 @@ unset DOCKER_FOR_MAC_REMOTE_HOST
unset REMOTE_HOST_FOUND

sudo chown docker:docker /opt/php_env_var_cache.php
/usr/bin/real_php /usr/local/bin/check_php_env_var_changes.php &> /dev/null
/usr/bin/real_php -d display_errors=stderr /usr/local/bin/check_php_env_var_changes.php &> /dev/null

/usr/bin/real_php /usr/local/bin/generate_conf.php > /etc/php/${PHP_VERSION}/mods-available/generated_conf.ini
PHP_VERSION="${PHP_VERSION}" /usr/bin/real_php /usr/local/bin/setup_extensions.php | sudo bash
/usr/bin/real_php -d display_errors=stderr /usr/local/bin/generate_conf.php > /etc/php/${PHP_VERSION}/mods-available/generated_conf.ini
PHP_VERSION="${PHP_VERSION}" /usr/bin/real_php -d display_errors=stderr /usr/local/bin/setup_extensions.php | sudo bash

# output on the logs can be done by writing on the "tini" PID. Useful for CRONTAB
TINI_PID=`ps -e | grep tini | awk '{print $1;}'`
/usr/bin/real_php /usr/local/bin/generate_cron.php $TINI_PID > /tmp/generated_crontab
/usr/bin/real_php -d display_errors=stderr /usr/local/bin/generate_cron.php $TINI_PID > /tmp/generated_crontab
chmod 0644 /tmp/generated_crontab

# If generated_crontab is not empty, start supercronic
Expand All @@ -139,13 +157,13 @@ if [[ -s /tmp/generated_crontab ]]; then
fi

if [[ "$IMAGE_VARIANT" == "apache" ]]; then
/usr/bin/real_php /usr/local/bin/enable_apache_mods.php | bash
/usr/bin/real_php -d display_errors=stderr /usr/local/bin/enable_apache_mods.php | bash
fi

if [ -e /etc/container/startup.sh ]; then
sudo -E -u "#$DOCKER_USER_ID" /etc/container/startup.sh
fi
sudo -E -u "#$DOCKER_USER_ID" sh -c "/usr/bin/real_php /usr/local/bin/startup_commands.php | bash"
sudo -E -u "#$DOCKER_USER_ID" sh -c "/usr/bin/real_php -d display_errors=stderr /usr/local/bin/startup_commands.php | bash"

if [[ "$APACHE_DOCUMENT_ROOT" == /* ]]; then
export ABSOLUTE_APACHE_DOCUMENT_ROOT="$APACHE_DOCUMENT_ROOT"
Expand Down
11 changes: 7 additions & 4 deletions utils/php_proxy.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#!/bin/bash

sudo chown docker:docker /opt/php_env_var_cache.php
# Users that cannot use sudo run PHP with the current configuration
if ! sudo -n chown docker:docker /opt/php_env_var_cache.php 2> /dev/null; then
exec /usr/bin/real_php "$@"
fi

REGENERATE=$(/usr/bin/real_php /usr/local/bin/check_php_env_var_changes.php)
REGENERATE=$(/usr/bin/real_php -d display_errors=stderr /usr/local/bin/check_php_env_var_changes.php)

if [[ "$REGENERATE" != "0" ]] && [[ "$REGENERATE" != "1" ]]; then
>&2 echo "Unexpected PHP proxy output:"
Expand All @@ -11,8 +14,8 @@ if [[ "$REGENERATE" != "0" ]] && [[ "$REGENERATE" != "1" ]]; then
fi

if [[ "$REGENERATE" == "1" ]]; then
/usr/bin/real_php /usr/local/bin/generate_conf.php | sudo tee "/etc/php/${PHP_VERSION}/mods-available/generated_conf.ini" > /dev/null
PHP_VERSION="${PHP_VERSION}" /usr/bin/real_php /usr/local/bin/setup_extensions.php | sudo bash
/usr/bin/real_php -d display_errors=stderr /usr/local/bin/generate_conf.php | sudo tee "/etc/php/${PHP_VERSION}/mods-available/generated_conf.ini" > /dev/null
PHP_VERSION="${PHP_VERSION}" /usr/bin/real_php -d display_errors=stderr /usr/local/bin/setup_extensions.php | sudo bash
fi

exec /usr/bin/real_php "$@"