Repository navigation
Improving future security releases and reducing dependencies on upstream builds (Post-mortem: PHP2Shell security release) #727
Replies: 10 comments
|
Thanks for the write-up. We run 32 WordPress sites on 1. What we rely on from the official image
2. Extensions on top
|
|
The PHP team has released backported source fixes for versions older than PHP 8.2 but I can see you have only published new docker images for 8.2+ Do you have any plans to patch older than PHP 8.2? |
|
Definitely agreed on getting PHP installed directly into the image instead of using the official PHP Docker image. It removes the liability and as long as the source is validated the outcome is basically the same, definitely a unique selling point if that would make your releases literally hours faster. |
|
Like everyone else said, thank you for everything you have done here!
We are just starting to use the FrankenPHP image with Laravel Octane (instead of our own previous custom builds of FrankenPHP images). We use a handful of the ENV-based configuration options, and add the following extensions on top of the default included list:
Here is our complete We use it with a Docker Compose setup that looks something like this: |
|
We add Yesterday when we deploy a mitigation by adding custom |
|
I use the frankenphp build with I had my own build running but it used to take ages basically just to add this and some image manipulation libraries so I stopped using my homegrown containers based off Debian and started building my containers based off of your images. This also kind of forced me to re-think my entire CI/CD process and everything is much smoother now thanks to the nudge by wanting to use your php images. |
|
First of all thank you very much for all the work you put into these images! ❤️ One small request I'd like to make is to separate PHP updates from image updates. For example we were on I'm sure you put a lot of care into not introducing breaking changes or similar but still the risk of affecting downstream projects would be lower if only the PHP version changed without any functional changes in the images. Of course, this would considerably blow up the range of tags as |
|
This timeline reminds of Chernobyl ^^' |
|
Thanks for taking the time and effort in improving build times. Already not looking forward to the next time this will inevitably happen.. Similar use case here, using the fpm-nginx images as our Wordpress bases:
|
|
Are we all installing almost the same extensions? I use |
Uh oh!
There was an error while loading. Please reload this page.
Background
On October 21, php.net released fixes for two critical remote code execution bugs (CVE-2026-109618 and CVE-2026-109619). Our patched images went live about 4 hours and 20 minutes after php.net published the source. Our own build took 27 minutes. The rest was waiting on upstream systems.
I don't think this is the last critical PHP vulnerability. Below is what happened and my plan to cut out the wait. Please share your thoughts in the comments.
The full play-by-play is in #721.
Timeline
All times are UTC.
pecl/redis, so I keep hitting retry.Where the time went
redisextension downloads from pecl.php.net. Everyone was rebuilding at once, and PECL couldn't keep up.8.4-fpm-nginx, so a v4 build would have moved them back to v4. I had to build a release-tags-only workflow during the incident. v4.5.2 also got burned: its release was published before the build failed, and published release tags can't be reused.The plan
Build straight from php.net and GitHub. Then we can start the moment a release is published. This will ship as v6, because it changes how PHP and extensions get into our images.
1. Compile PHP ourselves
@tomschlick started this in #725 and already opened a first version in #726.
We build our own base image,
ghcr.io/serversideup/php-base. It holds compiled PHP plus thedocker-php-ext-*helper scripts, which is what the officialphp:image gives us today.Inside our Dockerfiles,
FROM php:8.4.27-fpm-trixiebecomesFROM ghcr.io/serversideup/php-base:8.4.27-fpm-trixie. Everything after that line stays the same. You keep pullingserversideup/phplike you do now and never touchphp-basedirectly.FROM php:stays easy.PHP_VERSION,PHP_URL,PHP_SHA256, andGPG_KEYS, which is why each release waits on their PR. We pass those in from php.net instead.2. Install extensions without PECL
Of our default extensions (
opcache pcntl pdo_mysql pdo_pgsql redis zip), onlyredisdoesn't ship with PHP. We build it from a pinned phpredis release on GitHub instead of PECL.Your own extensions need a plan too. Our docs recommend
install-php-extensions, and most of you use it because it finds the system packages and cleans up after itself. It keeps working on PHP 8.5 and older. It doesn't support 8.6, and there's no date for when it will.The best outcome is
install-php-extensionssupporting 8.6, so first we'll see if mlocati adds support for PIE on 8.6 and newer. Our fallback is to document one command that we own:docker-php-serversideup-install-php-extensions. It's already in our images. You pass extension names the same way:install-php-extensions, same as todaydocker-php-ext-installfor built-in extensions and PIE for the rest, then removes the build toolsPIE uses Packagist names, so
redisisphpredis/phpredisandmongodbismongodb/mongodb-extension. The command looks up the PIE package from its extension name, so you keep typingredis.When
install-php-extensionssupports 8.6, we switch back behind the same command. Your Dockerfile doesn't change.PIE can install system libraries on its own, but only for some extensions. Its docs don't mention Alpine either. Before we commit, we test every extension in our docs on Debian and Alpine. When a library is missing, we contribute it to PIE instead of keeping our own list.
Built-in extensions like
gdandintlneed system libraries too.docker-php-ext-installwon't install them. We keep that short list ourselves.3. Start the build automatically
Our
get-php-versions.shalready reads php.net/releases/active.php. A scheduled workflow checks it every 15 minutes and starts a build when a new version shows up. I still approve the publish.4. Test PHP before we ship it
docker-library's Dockerfile ends with
php --version. Their CI runs four small PHP tests. Ourtest-image.shalready checks more than that. We add:php-hello-world,php-ext-install,php-argon2,php-fpm-hello-web), with creditphp -m, the configure flags, and the ini files. Add serversideup/php-base: PHP compiled from source, a drop-in for the official images #726 already includes this astest-base-image.sh. In an emergency, we compare against the last patch they shipped.5. Let CI publish the GitHub release
Today I publish a release in GitHub, which creates the tag and starts the build. If the build fails, the tag is already locked.
The new order:
If the build fails, nothing was published. We delete the tag, fix the problem, and run it again with the same version.
6. Make backports safe by default
Floating tags (
8.4-fpm-nginx,latest) only go out when the version we're building is the highest stable version tag in the repo. Anything older, like a v5 backport after v6 ships, only gets its release tags (8.4-fpm-nginx-v5.0.3). CI publishes its release with--latest=false.The check uses git tags, not GitHub's "Latest" label, because the release doesn't exist yet while we build. The weekly rebuild uses the same check. Backports then run through the same Release workflow from their branch, with no special workflow.
What it costs
The v5.0.2 build used about 290 job-minutes on Depot.
php-baseadds 57 images to compile. That only happens when PHP or the OS changes. Every other build reuses them. We'll measure the real compile time with #726 and share it with Depot before we switch.Rollout
php-baseand compare it to the official image (in progress in Add serversideup/php-base: PHP compiled from source, a drop-in for the official images #726)php-basematrix in CIphp-baseon v6 beta tagsOpen questions
FROM serversideup/php, what do you rely on from the official image that we must keep?Thanks again to everyone who helped in #721. And thank you to the PHP security team, who are mostly volunteers.
All reactions