From ae18dd73fd8a208d2ee3bdb2c11e1da63161267b Mon Sep 17 00:00:00 2001 From: Kevin Hahn Date: Wed, 21 Oct 2026 21:43:04 +0700 Subject: [PATCH] Rebuild base-php from the PHP 7.4 backport for CVE-2026-109618/109619 Both CVEs are unauthenticated RCE in PHP, highest risk on Apache mod_php, which is what Language Forge runs. PHP 7.4 is EOL so there is no fixed release and the official php:7.4-apache image will not be rebuilt. The PHP security team published a minimal source backport branch (PHP-7.4-CVE-2026-109618-109619); this recompiles PHP from that branch, pinned to commit 712e60f9, in place on top of the official image using the same configure flags, so the apache module, ini layout and extension tooling are unchanged. Also needed to make the image buildable at all today: - Debian 11 moved to archive.debian.org, so apt sources are rewritten. - The mongodb extension is pinned to 1.19.0 (the version in the published image); unpinned it resolves to 1.20, whose ReadPreference deprecation floods the test log and OOMs PHPUnit's JUnit writer. Defence in depth: session.upload_progress.enabled=Off, a confirmed CVE-2026-109619 path that Language Forge does not use. docs/RELEASE.md explains that the base PHP image must be published via the manual workflow before merging changes to docker/base-php. Co-Authored-By: Claude Fable 5.1 --- docker/base-php/Dockerfile | 105 ++++++++++++++++++++++++- docker/base-php/customizations.php.ini | 6 ++ docs/RELEASE.md | 6 ++ 3 files changed, 116 insertions(+), 1 deletion(-) diff --git a/docker/base-php/Dockerfile b/docker/base-php/Dockerfile index 4703f2b514..0f2f429725 100644 --- a/docker/base-php/Dockerfile +++ b/docker/base-php/Dockerfile @@ -2,6 +2,106 @@ # https://hub.docker.com/layers/library/php/7.4-apache/images/sha256-c44681664d111addef2a2b6598901609990d45047b874c87d7d40fc7ce269195 FROM php:7.4-apache +# Debian 11 (bullseye) reached end of life in August 2026 and its packages, +# including bullseye-security, now live only on archive.debian.org. Without +# this every apt-get below fails with 404s. +RUN set -eux; \ + sed -i \ + -e 's|http://deb.debian.org/debian-security|http://archive.debian.org/debian-security|g' \ + -e 's|http://deb.debian.org/debian|http://archive.debian.org/debian|g' \ + /etc/apt/sources.list; \ + echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/99-archive-debian + +# --------------------------------------------------------------------------- +# Rebuild PHP from the PHP security team's minimal backport branch for +# CVE-2026-109618 (RCE via OOM corruption) and CVE-2026-109619 (RCE via stale +# global state). See https://www.php.net/archive/2026.php#2026-10-21-5 +# +# PHP 7.4 is end-of-life, so there is no 7.4 release containing the fix and the +# official php:7.4-apache image will never be rebuilt. The backport branch is +# https://github.com/php/php-src/tree/PHP-7.4-CVE-2026-109618-109619 +# +# We recompile PHP in place, on top of the official image, using the exact +# configure flags the official image was built with (php-config +# --configure-options) so the layout, apache module, ini dirs and extension +# tooling (docker-php-ext-*, install-php-extensions) keep working unchanged. +# Extensions are installed further down and so build against the new headers. +# +# To move to a newer commit on the branch, update PHP_SRC_REF to the new SHA. +# --------------------------------------------------------------------------- +ARG PHP_SRC_REPO=https://github.com/php/php-src.git +ARG PHP_SRC_BRANCH=PHP-7.4-CVE-2026-109618-109619 +ARG PHP_SRC_REF=712e60f9edfd03f0960fc4fc3c2c375f42b90aa2 +RUN set -eux; \ + savedAptMark="$(apt-mark showmanual)"; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + git \ + bison \ + apache2-dev \ + libargon2-dev \ + libcurl4-openssl-dev \ + libonig-dev \ + libreadline-dev \ + libsodium-dev \ + libsqlite3-dev \ + libssl-dev \ + libxml2-dev \ + zlib1g-dev \ + ; \ + \ + git clone --depth 1 --branch "$PHP_SRC_BRANCH" "$PHP_SRC_REPO" /usr/src/php-src; \ + cd /usr/src/php-src; \ + # fail the build if the branch has moved away from the reviewed commit + test "$(git rev-parse HEAD)" = "$PHP_SRC_REF"; \ + \ + export \ + CFLAGS="$PHP_CFLAGS" \ + CPPFLAGS="$PHP_CPPFLAGS" \ + LDFLAGS="$PHP_LDFLAGS" \ + ; \ + debMultiarch="$(dpkg-architecture --query DEB_BUILD_MULTIARCH)"; \ + # https://bugs.php.net/bug.php?id=74125 + if [ ! -d /usr/include/curl ] && [ ! -e /usr/local/include/curl ]; then \ + ln -sT "/usr/include/$debMultiarch/curl" /usr/local/include/curl; \ + fi; \ + \ + ./buildconf --force; \ + eval ./configure "$(php-config --configure-options)"; \ + make -j "$(nproc)"; \ + find -type f -name '*.a' -delete; \ + make install; \ + find \ + /usr/local \ + -type f \ + -perm '/0111' \ + -exec sh -euxc ' \ + strip --strip-all "$@" || : \ + ' -- '{}' + \ + ; \ + cp -v php.ini-* "$PHP_INI_DIR/"; \ + cd /; \ + rm -rf /usr/src/php-src; \ + \ + # remove build dependencies again, same approach as the official image + apt-mark auto '.*' > /dev/null; \ + [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; \ + find /usr/local -type f -executable -exec ldd '{}' ';' \ + | awk '/=>/ { print $(NF-1) }' \ + | sort -u \ + | xargs -r dpkg-query --search \ + | cut -d: -f1 \ + | sort -u \ + | xargs -r apt-mark manual \ + ; \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + rm -rf /var/lib/apt/lists/*; \ + \ + # smoke test: the rebuilt binary and apache module must load + php --version; \ + php -m; \ + apache2ctl -M | grep -q php7_module + # install apt packages # gnupg - for installing Mongo GPG key below # git - for use by composer to install dependencies @@ -21,7 +121,10 @@ RUN apt-get update && apt-get -y install mongodb-org-tools mongodb-mongosh && rm # see https://github.com/mlocati/docker-php-extension-installer # PHP extensions required by the LF application COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ -RUN install-php-extensions mongodb intl @composer +# mongodb is pinned to the version the previously published image shipped with: +# 1.20 deprecates the integer ReadPreference modes still used by the +# mongodb/mongodb 1.15 library in composer.lock, which floods the logs. +RUN install-php-extensions mongodb-1.19.0 intl @composer # php customizations COPY docker/base-php/customizations.php.ini $PHP_INI_DIR/conf.d/ diff --git a/docker/base-php/customizations.php.ini b/docker/base-php/customizations.php.ini index 00989540a5..2a445e8daf 100644 --- a/docker/base-php/customizations.php.ini +++ b/docker/base-php/customizations.php.ini @@ -2,3 +2,9 @@ memory_limit = 256M post_max_size = 60M upload_max_filesize = 60M xdebug.log_level = 0 + +; Defence in depth for CVE-2026-109619 (RCE via stale global state). +; The session upload-progress feature is one of the confirmed attack paths and +; Language Forge does not use it. Must be set here (not via ini_set) so it +; applies before request initialisation. +session.upload_progress.enabled = Off diff --git a/docs/RELEASE.md b/docs/RELEASE.md index 23d7ae5848..9359fafc1e 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -40,6 +40,12 @@ Update the image tags in `prod/kustomization.yaml` Production deployments can be manually run with `make deploy-prod`. Note, this command assumes that the staging k8s context is named aws-rke +### Base PHP image + +The `app` image is built on top of `sillsdev/web-languageforge:base-php`, which is **not** rebuilt by the staging or production workflows. It is built and pushed only by the manual workflow `.github/workflows/build-and-publish-base-php.yml` (Actions, "Build and publish a base PHP image for LF", "Run workflow", pick the branch). + +Rebuild and publish it whenever `docker/base-php/` changes, **before** merging that change to `develop` or `master`, otherwise staging and production will be built on the old base image. The PHP interpreter itself is compiled from a pinned php-src commit in `docker/base-php/Dockerfile` (see the comments there); to pick up a newer security backport, update `PHP_SRC_REF`, re-run the base PHP workflow, then release as normal. + ### Revert Various tagged images are maintained in Docker Hub. If you need to revert to a previous version, you can do so by running the deployments scripts with the appropriate permissions or utilizing the Kubernetes UI to change the image of a deployment at any time.