Repository navigation
Add Docker Hardened Images pilot for the min flavor (-hardened-test) - #268
berfinyuksel wants to merge 7 commits into
Conversation
Applies the decisions from pimcore/DevOps-Tasks#84: - Dockerfile.dhi: experimental `min` image built from dhi.io/php (Debian 13). Extensions missing in DHI (bcmath exif gd pcntl pdo_mysql sockets zip, amqp) are built with phpize in the -dev stage and copied, together with the shared libraries the distroless runtime lacks, into the -fpm runtime. The php CLI comes from the DHI CLI image. Runs as uid 65532, no shell, no package manager. - hardened-test.yml: manual-only workflow that builds the regular and the DHI min image (amd64), runs the same extension/composer smoke checks as test.yml, scans both with Trivy and writes a comparison to the step summary. Pushes php<ver>-min-v5-hardened-test only with publish=true. - README: state that -hardened publishing is paused since 2026-07-31 and document the experimental -hardened-test tag and its limits (min only: DHI has no ghostscript, ImageMagick, ffmpeg, poppler, LibreOffice or Chromium). release.yml is unchanged. Co-Authored-By: Claude <noreply@anthropic.com>
… pilot workflow_dispatch is only registered once the file is on the default branch, so the pilot could not be exercised from the PR. Run the build + smoke checks + Trivy comparison on pull_request (paths-filtered); publishing stays limited to a manual dispatch with publish=true. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
The Brotli pin requests an unavailable development package, Composer cannot execute because its shebang launcher is missing, and copied libraries lack package metadata, causing vulnerability scans to undercount CVEs.
3 open issues
What changed in this PR
Adds an experimental Docker Hardened Images pilot for Pimcore’s min flavor, separate from the existing release workflow, to compare vulnerability counts with the regular image.
Changes:
- Adds a nonroot, distroless PHP image with the required extensions, CLI, configuration and Composer.
- Adds a manual amd64 workflow for builds, smoke checks, Trivy comparisons and optional
-hardened-testpublishing. - Documents paused
-hardenedpublishing and the pilot’s unsupported status and limitations.
| File | Description |
|---|---|
| README.md | Documents hardened publishing status and experimental image usage. |
| Dockerfile.dhi | Defines the multi-stage DHI-based min image. |
| .github/workflows/hardened-test.yml | Builds, checks, compares and optionally publishes the pilot image. |
Review effort: Balanced
ext/zip failed with 'pcre2.h: No such file or directory': PHP on DHI is built with --with-external-pcre and the -dev image has no pcre2 headers. zlib1g-dev is added for the same reason ahead of the gd build. Co-Authored-By: Claude <noreply@anthropic.com>
The distroless runtime has no env binary; composer's '#!/usr/bin/env php' shebang therefore failed with 'no such file or directory'. The phar stub is signed, so the shebang cannot be rewritten; copy env (libc only) from the builder instead. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Incorrect PHP paths block the DHI build, Composer lacks its required interpreter, and missing package metadata causes the vulnerability comparison to undercount added libraries.
4 open issues
1 resolved since last review
Review effort: Balanced
composer create-project fails in the distroless runtime with 'Unable to launch a new process': composer shells out (rm -rf, git, unzip) and there is no /bin/sh. The image is a runtime image; the application has to be built elsewhere and copied in. Check composer starts and that the pimcore platform extension requirements are met instead. Co-Authored-By: Claude <noreply@anthropic.com>
| > pushed on 2026-07-31 and are not refreshed. Use the plain tags; they are the supported images and | ||
| > are rebuilt on every scheduled run with all Debian security updates available at build time. | ||
| > Hardened publishing resumes once there is a strategy that delivers it consistently | ||
| > (see [pimcore/DevOps-Tasks#84](https://github.com/pimcore/DevOps-Tasks/issues/84)). |
There was a problem hiding this comment.
It's great to include a link in the README file that cannot be accessed 🙄
There was a problem hiding this comment.
hello @jdreesen, fair point, thanks! the link is gone now :) that slipped in because i'm working on this with Claude and hadn't reviewed everything yet, which is also why the pr is still a draft. i'll go through it before marking it ready, happy to get your review!
…k PRs, README link - Pin brotli to the libbrotli-dev candidate (with --allow-downgrades for libbrotli1) instead of the runtime candidate: during the drift from catalog#677/#682 the newest runtime build has no matching -dev package, so the old pin requested a version that does not exist. - Write the dpkg status record of every package whose files are copied into the runtime to /var/lib/dpkg/status.d/, so Trivy sees libzip, librabbitmq, libpng, libjpeg, libwebp, freetype, bz2 and coreutils (env) in the DHI image like it does in the baseline. - Use PHP_PREFIX for the runtime COPY paths instead of the PHP_VERSION build arg (the build arg did expand correctly, this just removes the dependency on ARG/ENV precedence); drop the unused stage ARGs. - Skip the pilot job for fork pull requests: they get no secrets and the dhi.io login would fail. - README: drop the link to the private DevOps-Tasks issue. Co-Authored-By: Claude <noreply@anthropic.com>
…ow comment Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
The new distroless image has not been built, leaving native extension compilation, shared-library dependencies, and nonroot CLI/FPM startup unverified pending a successful pilot workflow run.
No open issues 🎉
Review effort: Balanced
There was a problem hiding this comment.
🔵 Needs a closer look
The DHI image has not been built or run, leaving registry authentication, extension and shared-library compatibility, and nonroot CLI/FPM behavior unverified pending a successful publish=false pilot.
No open issues 🎉
Review effort: Balanced


Applies the decisions from pimcore/DevOps-Tasks#84 (research on hardened base images as an alternative to Copa).
What this adds
Dockerfile.dhi– experimentalminimage built fromdhi.io/php:<ver>-debian13-{dev,fpm}(Docker Hardened Images, Apache-2.0, Debian 13 only). The extensions DHI does not compile in (bcmath exif gd pcntl pdo_mysql sockets zipand PECLamqp) are built with phpize in the-devstage and copied, together with only the shared libraries the distroless runtime lacks, into the-fpmruntime. ThephpCLI is taken from the DHI CLI image of the same version. The result runs as uid 65532 with no shell and no package manager; ourphp.ini, fpm env config and composer are included..github/workflows/hardened-test.yml–workflow_dispatchonly, amd64 only. Builds the regularminimage and the DHIminimage, runs the same extension/composer smoke checks astest.yml(plus CLI/fpm ABI, nonroot and env-substitution checks), scans both with Trivy and writes a side-by-side table to the step summary. Pushespimcore/pimcore:php<ver>-min-v5-hardened-testonly withpublish=true.-hardenedpublishing is paused since 2026-07-31 (which is already the case:publish_hardeneddefaults tofalseinrelease.yml) and documents the experimental-hardened-testtag and its limits.release.ymlis unchanged.Why
minonlyDHI's apt repo (
dhi.io/deb/debian/main trixie) provides hardened rebuilds of the PHP build dependencies (libpng, libjpeg, libwebp, libfreetype, libzip, librabbitmq) but not ghostscript, ImageMagick, ffmpeg, poppler, LibreOffice, Chromium, exiftool or supervisor. Those would come unpatched from plain Debian, and a distroless runtime cannot install them, so thedefault/max/supervisordflavors gain nothing from DHI. ImageMagick alone is the largest CVE contributor in the current known-CVEs report.Verification
hardened-test.ymldispatch from this branch (withpublish=false) is the real test; expect iterations on thedhi.iologin (uses the existing Docker Hub secrets), the gd configure flags and the shared-library copy.libfreetype-dev). The Dockerfile pinslibbrotli1/libbrotli-devto one version as a workaround.test.ymlis unaffected (it does not buildDockerfile.dhi); actionlint runs on this PR.🤖 Generated with Claude Code