From 70ad81d28998ee9a16378d1a368ec4b9619bdbae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Wed, 11 Mar 2026 17:16:55 +0100 Subject: [PATCH 1/4] docs: fix Mercure support with Docker hardened images --- docs/docker.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docker.md b/docs/docker.md index 99e2bb0f52..082548fe76 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -266,8 +266,8 @@ COPY --from=builder /usr/local/etc/php/php.ini-production /usr/local/etc/php/php # Caddy data dirs — must be writable for nonroot, even on a read-only root filesystem ENV XDG_CONFIG_HOME=/config \ XDG_DATA_HOME=/data -COPY --from=builder --chown=nonroot:nonroot /data/caddy /data/caddy -COPY --from=builder --chown=nonroot:nonroot /config/caddy /config/caddy +COPY --from=builder --chown=nonroot:nonroot /data /data +COPY --from=builder --chown=nonroot:nonroot /config /config USER nonroot From 6baeaad238ccdb22f7d3756054c8804bddb00659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Thu, 12 Mar 2026 14:26:12 +0100 Subject: [PATCH 2/4] harden --- docs/docker.md | 56 ++++++++++++++++++-------------------------------- 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/docs/docker.md b/docs/docker.md index 082548fe76..a2b537f297 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -223,58 +223,42 @@ RUN install-php-extensions pdo_mysql pdo_pgsql #... # Copy shared libs of frankenphp and all installed extensions to temporary location # You can also do this step manually by analyzing ldd output of frankenphp binary and each extension .so file -RUN apt-get update && apt-get install -y libtree && \ - EXT_DIR="$(php -r 'echo ini_get("extension_dir");')" && \ - FRANKENPHP_BIN="$(which frankenphp)"; \ - LIBS_TMP_DIR="/tmp/libs"; \ - mkdir -p "$LIBS_TMP_DIR"; \ - for target in "$FRANKENPHP_BIN" $(find "$EXT_DIR" -maxdepth 2 -type f -name "*.so"); do \ - libtree -pv "$target" | sed 's/.*── \(.*\) \[.*/\1/' | grep -v "^$target" | while IFS= read -r lib; do \ - [ -z "$lib" ] && continue; \ - base=$(basename "$lib"); \ - destfile="$LIBS_TMP_DIR/$base"; \ - if [ ! -f "$destfile" ]; then \ - cp "$lib" "$destfile"; \ - fi; \ - done; \ - done - - -# Distroless debian base image, make sure this is the same debian version as the base image +RUN apt-get update; \ + apt-get install -y --no-install-recommends libtree; \ + mkdir -p /tmp/libs; \ + for target in $(which frankenphp) \ + $(find "$(php -r 'echo ini_get("extension_dir");')" -maxdepth 2 -name "*.so"); do \ + libtree -pv "$target" 2>/dev/null | grep -oP '(?:── )\K/\S+(?= \[)' | while IFS= read -r lib; do \ + [ -f "$lib" ] && cp -n "$lib" /tmp/libs/; \ + done; \ + done; + + +# Distroless Debian base image, make sure this matches the Debian version of the builder FROM gcr.io/distroless/base-debian13 -# Docker hardened image alternative +# Docker hardened image alternative: # FROM dhi.io/debian:13 -# Location of your app and Caddyfile to be copied into the container -ARG PATH_TO_APP="." -ARG PATH_TO_CADDYFILE="./Caddyfile" - -# Copy your app into /app -# For further hardening make sure only writable paths are owned by the nonroot user -COPY --chown=nonroot:nonroot "$PATH_TO_APP" /app -COPY "$PATH_TO_CADDYFILE" /etc/caddy/Caddyfile - -# Copy frankenphp and necessary libs COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp COPY --from=builder /usr/local/lib/php/extensions /usr/local/lib/php/extensions COPY --from=builder /tmp/libs /usr/lib -# Copy php.ini configuration files COPY --from=builder /usr/local/etc/php/conf.d /usr/local/etc/php/conf.d COPY --from=builder /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini -# Caddy data dirs — must be writable for nonroot, even on a read-only root filesystem -ENV XDG_CONFIG_HOME=/config \ - XDG_DATA_HOME=/data +# Config and data dirs must be writable for nonroot, even on a read-only root filesystem +ENV XDG_CONFIG_HOME=/config XDG_DATA_HOME=/data COPY --from=builder --chown=nonroot:nonroot /data /data COPY --from=builder --chown=nonroot:nonroot /config /config -USER nonroot +# Copy your app and Caddyfile +COPY --chown=nonroot:nonroot . /app +COPY Caddyfile /etc/caddy/Caddyfile +USER nonroot WORKDIR /app -# entrypoint to run frankenphp with the provided Caddyfile -ENTRYPOINT ["/usr/local/bin/frankenphp", "run", "-c", "/etc/caddy/Caddyfile"] +ENTRYPOINT ["/usr/local/bin/frankenphp", "run", "--config", "/etc/caddy/Caddyfile"] ``` ## Development Versions From 23aaaadbba3229a6ee2ae72c9889e360999b2cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Tue, 24 Mar 2026 11:47:54 +0100 Subject: [PATCH 3/4] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Kévin Dunglas --- docs/docker.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/docker.md b/docs/docker.md index a2b537f297..7b428f2ce5 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -223,9 +223,9 @@ RUN install-php-extensions pdo_mysql pdo_pgsql #... # Copy shared libs of frankenphp and all installed extensions to temporary location # You can also do this step manually by analyzing ldd output of frankenphp binary and each extension .so file -RUN apt-get update; \ - apt-get install -y --no-install-recommends libtree; \ - mkdir -p /tmp/libs; \ +RUN apt-get update && \ + apt-get install -y --no-install-recommends libtree && \ + mkdir -p /tmp/libs && \ for target in $(which frankenphp) \ $(find "$(php -r 'echo ini_get("extension_dir");')" -maxdepth 2 -name "*.so"); do \ libtree -pv "$target" 2>/dev/null | grep -oP '(?:── )\K/\S+(?= \[)' | while IFS= read -r lib; do \ @@ -251,8 +251,8 @@ ENV XDG_CONFIG_HOME=/config XDG_DATA_HOME=/data COPY --from=builder --chown=nonroot:nonroot /data /data COPY --from=builder --chown=nonroot:nonroot /config /config -# Copy your app and Caddyfile -COPY --chown=nonroot:nonroot . /app +# Copy your app (kept root-owned) and Caddyfile +COPY . /app COPY Caddyfile /etc/caddy/Caddyfile USER nonroot From 15d2892d39a4f6cc93823f71928d7c90b3d7cac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Tue, 24 Mar 2026 12:10:35 +0100 Subject: [PATCH 4/4] use heredoc syntax --- docs/docker.md | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/docs/docker.md b/docs/docker.md index 7b428f2ce5..3dcf65beb7 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -158,13 +158,14 @@ FROM dunglas/frankenphp ARG USER=appuser -RUN \ +RUN <<-EOF # Use "adduser -D ${USER}" for alpine based distros - useradd ${USER}; \ + useradd ${USER} # Add additional capability to bind to port 80 and 443 - setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \ + setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp # Give write access to /config/caddy and /data/caddy chown -R ${USER}:${USER} /config/caddy /data/caddy +EOF USER ${USER} ``` @@ -182,13 +183,14 @@ FROM dunglas/frankenphp ARG USER=appuser -RUN \ +RUN <<-EOF # Use "adduser -D ${USER}" for alpine based distros - useradd ${USER}; \ + useradd ${USER} # Remove default capability - setcap -r /usr/local/bin/frankenphp; \ + setcap -r /usr/local/bin/frankenphp # Give write access to /config/caddy and /data/caddy chown -R ${USER}:${USER} /config/caddy /data/caddy +EOF USER ${USER} ``` @@ -223,20 +225,22 @@ RUN install-php-extensions pdo_mysql pdo_pgsql #... # Copy shared libs of frankenphp and all installed extensions to temporary location # You can also do this step manually by analyzing ldd output of frankenphp binary and each extension .so file -RUN apt-get update && \ - apt-get install -y --no-install-recommends libtree && \ - mkdir -p /tmp/libs && \ +RUN <<-EOF + apt-get update + apt-get install -y --no-install-recommends libtree + mkdir -p /tmp/libs for target in $(which frankenphp) \ - $(find "$(php -r 'echo ini_get("extension_dir");')" -maxdepth 2 -name "*.so"); do \ - libtree -pv "$target" 2>/dev/null | grep -oP '(?:── )\K/\S+(?= \[)' | while IFS= read -r lib; do \ - [ -f "$lib" ] && cp -n "$lib" /tmp/libs/; \ - done; \ - done; + $(find "$(php -r 'echo ini_get("extension_dir");')" -maxdepth 2 -name "*.so"); do + libtree -pv "$target" 2>/dev/null | grep -oP '(?:── )\K/\S+(?= \[)' | while IFS= read -r lib; do + [ -f "$lib" ] && cp -n "$lib" /tmp/libs/ + done + done +EOF # Distroless Debian base image, make sure this matches the Debian version of the builder FROM gcr.io/distroless/base-debian13 -# Docker hardened image alternative: +# Docker hardened image alternative # FROM dhi.io/debian:13 COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp