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.