feat(lsphp): emergency build path compiling lsphp from php.net source
For when a PHP security release is out and the LiteSpeed apt repo lags (2026-10-21: php2shell CVE-2026-109618/-109619 fixed in 8.2.35/8.3.36/ 8.4.27/8.5.12, LiteSpeed still on 8.2.34/8.3.35/8.4.26/8.5.11). Dockerfile.lsphp / Dockerfile.litespeed gain an `lsphp-src` stage that is idle unless PHP_SRC_VERSION is set; normal builds are unchanged (measured: module list, file manifest, ldd, ini and dpkg identical to the published images). When set (lsphp-src-build.sh): - tarball sha256 (php.net releases JSON) + OpenPGP signature against the committed php.net keyring, signer pinned per branch - configure flags replayed from the packaged lsphp's own Configure Command - LiteSpeed's php-litespeed-8.3 SAPI (what the packages actually compile, measured) instead of php-src's sapi/litespeed; sha256-pinned and checked against the packaged binary's LSAPI_* names - packaged compiled-in ini defaults reproduced (session.save_path on 8.2/ 8.3) and gated (`php -n` diff) - ABI gate (lsphp-abi-gate.sh, pahole) before reusing LiteSpeed's packaged PECL .so files; regression-tested in Shell-Checks lsphp-src-overlay.sh swaps only php-src-owned files (decided by dpkg) and fails the build unless extensions, versions, effective ini and feature lines match the packaged lsphp under both CLI and litespeed SAPIs. CI: lsphp-source-emergency.yaml (workflow_dispatch only). build-push.yaml refuses to overwrite a newer php.net image with an older packaged one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
be1540f559
commit
83c2ee9c2a
13 files changed
+2021
-3
No files matched your search
@@ -0,0 +1,139 @@
|
||||
# Emergency lsphp from php.net source
|
||||
|
||||
Use this only when PHP has published a security release and the LiteSpeed apt
|
||||
repo (`rpms.litespeedtech.com/debian`, noble) still ships the vulnerable lsphp.
|
||||
The emergency build replaces the PHP inside `cac-lsphp` and `cac-litespeed` with
|
||||
the same PHP version compiled from the php.net tarball. Everything else in the
|
||||
image stays as LiteSpeed packages it. The build fails unless the result offers
|
||||
the same support as the packaged lsphp.
|
||||
|
||||
This path was first built for php2shell (CVE-2026-109618/-109619, 2026-10-21):
|
||||
php.net released 8.2.35, 8.3.36, 8.4.27 and 8.5.12, and LiteSpeed was still on
|
||||
8.2.34, 8.3.35, 8.4.26 and 8.5.11.
|
||||
|
||||
## Trigger
|
||||
|
||||
```bash
|
||||
# 1. Staging tags only (:phpNN-phpnet-X.Y.Z). Test these on .148 first.
|
||||
curl -fsS -X POST -H "Authorization: token $TEA_TOKEN" -H 'Content-Type: application/json' \
|
||||
https://repo.anhonesthost.net/api/v1/repos/cloud-hosting-platform/cloud-apache-container/actions/workflows/lsphp-source-emergency.yaml/dispatches \
|
||||
-d '{"ref":"trunk","inputs":{"php_versions":"82=8.2.35 83=8.3.36 84=8.4.27 85=8.5.12","images":"both","push_production_tags":"false"}}'
|
||||
|
||||
# 2. Production tags (:phpNN, :latest for 85). WHP pulls these on recreate.
|
||||
# The JSON is the same as step 1 except push_production_tags is "true".
|
||||
```
|
||||
|
||||
Inputs:
|
||||
|
||||
- `php_versions`: space-separated `NN=X.Y.Z` pairs, with NN between 81 and 85.
|
||||
- `images`: `both`, `lsphp` or `litespeed`.
|
||||
- `push_production_tags`: `true` or `false`.
|
||||
|
||||
The job for a given PHP version refuses to run if php.net has no such release.
|
||||
It also refuses if LiteSpeed already ships that version or a newer one; in that
|
||||
case, run the normal build instead.
|
||||
|
||||
After pushing, check the labels on the pushed image:
|
||||
|
||||
```bash
|
||||
docker buildx imagetools inspect repo.anhonesthost.net/cloud-hosting-platform/cac-lsphp:php85 \
|
||||
--format '{{json .Image.Config.Labels}}'
|
||||
# org.whp.lsphp-source = php.net, org.whp.php-version = 8.5.12
|
||||
```
|
||||
|
||||
Inside the container, `/etc/cac-lsphp-source` records the tarball sha256, the
|
||||
signer fingerprint and the packaged release that was replaced.
|
||||
|
||||
Running containers do not change until they are recreated. Follow the normal
|
||||
deploy order: test.148, whp02, sdbees, then whp01.
|
||||
|
||||
## Getting back to the packaged lsphp
|
||||
|
||||
When LiteSpeed publishes the fixed lsphp, run the normal `build-push.yaml`
|
||||
build, either with a push to trunk or a dispatch. It overwrites `:phpNN` with
|
||||
the packaged build once the packaged version is the same as or newer than the
|
||||
php.net version.
|
||||
|
||||
Until that happens, the normal build refuses to overwrite a newer php.net image.
|
||||
It fails at the "Do not roll back a newer php.net emergency image" step. This is
|
||||
intentional, so a routine push during the emergency does not put the vulnerable
|
||||
PHP back. That step lets the build through if it cannot inspect the published
|
||||
tag.
|
||||
|
||||
## Local build
|
||||
|
||||
```bash
|
||||
docker build -f Dockerfile.lsphp --build-arg PHPVER=85 --build-arg OLS_VERSION=1.8.4 \
|
||||
--build-arg CACHE_BUST=$(date +%s) \
|
||||
--build-arg PHP_SRC_VERSION=8.5.12 \
|
||||
--build-arg PHP_SRC_SHA256=<sha256 of php-8.5.12.tar.xz from https://www.php.net/releases/index.php?json&version=8.5.12> \
|
||||
--build-arg LSPHP_SOURCE=php.net -t localhost/cac-lsphp-src:php85-test .
|
||||
```
|
||||
|
||||
The same build args work with `Dockerfile.litespeed`. Without
|
||||
`PHP_SRC_VERSION`, both Dockerfiles build exactly as before: the `lsphp-src`
|
||||
stage and the overlay steps do nothing.
|
||||
|
||||
## What the build does and what makes it fail
|
||||
|
||||
The build has four parts.
|
||||
|
||||
1. The `lsphp-src` stage runs `scripts/lsphp-src-build.sh`, FROM the same
|
||||
`litespeedtech/openlitespeed` base as the shipped image.
|
||||
2. In `Dockerfile.lsphp`, the ext-build stage runs `scripts/lsphp-src-overlay.sh dev`.
|
||||
This builds `cac_path_parity` and runs its `.phpt` suite against the php.net
|
||||
PHP.
|
||||
3. The shipped stage runs `scripts/lsphp-src-overlay.sh runtime`.
|
||||
4. The labels record the source: `org.whp.php-version` is the php.net version
|
||||
and `org.whp.lsphp-source` is `php.net`. `org.whp.lsphp-package-version`
|
||||
still names the LiteSpeed packages underneath.
|
||||
|
||||
Each of the following fails the build:
|
||||
|
||||
| Check | Fails when |
|
||||
|---|---|
|
||||
| Tarball sha256 | It does not match `PHP_SRC_SHA256`, which CI takes from the php.net releases JSON. |
|
||||
| OpenPGP signature | `gpgv` rejects the `.asc` against `keys/php-src/php-keyring.asc`, or the signer's primary fingerprint is not listed for that branch in `keys/php-src/release-managers.txt`. |
|
||||
| Version sanity | The version is not X.Y.Z, is not in the PHPVER branch, or is not newer than the packaged version. |
|
||||
| Configure | Replaying the packaged lsphp's own `Configure Command` fails. The usual cause is a `-dev` package missing from `DEVPKGS`. |
|
||||
| LSAPI SAPI | `php-litespeed-8.3.tgz` does not match its pinned sha256, or the built lsphp's set of `LSAPI_*` names differs from the packaged binary's. |
|
||||
| Compiled-in ini defaults | Any `php -n` ini default differs from the packaged binary. Directives that are new in the release are allowed and printed. |
|
||||
| ABI gate | A struct member's offset or size changed, or a non-`*_globals` struct changed size. See `scripts/lsphp-abi-gate.sh`. |
|
||||
| File replacement | A php-src ELF file owned by a package at the core version has no php.net counterpart. |
|
||||
| Same-support gate (both SAPIs) | Loaded extensions differ, a PECL or Zend extension changes version, a php-src extension is not at the new version, an effective ini value changes or disappears, or a gd/curl/openssl/icu/imagick/redis/memcached/password/hash/stream feature line changes. |
|
||||
| Build-env consistency | `Configure Command` differs beyond CFLAGS, an ELF file has an unresolved library, or the packaged release in the builder stage differs from the one in the shipped stage. |
|
||||
|
||||
## Differences from the packaged build
|
||||
|
||||
These were measured on 2026-10-21 for php85 and php82 (`cac-lsphp`) and php83
|
||||
(`cac-litespeed`):
|
||||
|
||||
- Version strings, build date and build host.
|
||||
- `memory_hard_limit_percent` exists. The php2shell release added this ini
|
||||
directive.
|
||||
- The bundled Olson timezone DB is 2026.5 instead of 2026.3 for 8.5. It comes
|
||||
from the newer tarball.
|
||||
- LDAP "Vendor Version" is 20610 instead of 20607. This is the version of the
|
||||
OpenLDAP headers it was compiled against; noble's libldap-dev is newer now.
|
||||
The runtime library is the same Ubuntu libldap.
|
||||
- CFLAGS has no `-ffile-prefix-map` or `-fdebug-prefix-map` pointing at
|
||||
LiteSpeed's build directory.
|
||||
- One more `_chk` (FORTIFY) symbol is used. Shared extensions get BIND_NOW,
|
||||
which is stricter than the packaged build.
|
||||
- The image is about 100 MB larger. The overlay layer holds a second copy of
|
||||
lsphp, php and php8.M.
|
||||
|
||||
## Maintenance: what must stay in sync
|
||||
|
||||
| Item | When it needs attention | How you find out |
|
||||
|---|---|---|
|
||||
| `keys/php-src/release-managers.txt` and `php-keyring.asc` | A new release manager, or a new PHP branch (8.6). | The build fails because the signer is not pinned or there is no key. Refresh from <https://www.php.net/gpg-keys.php> and <https://www.php.net/distributions/php-keyring.gpg>. |
|
||||
| `keys/php-src/litespeed-sapi.txt` | LiteSpeed publishes a new php-litespeed release and builds lsphp with it. | The build fails on the sha256 mismatch or on the LSAPI name-set difference. Re-measure the packaged binary, then re-pin. |
|
||||
| `DEVPKGS` in `lsphp-src-build.sh` | LiteSpeed adds a configure flag that needs a new `-dev` library. | configure fails and prints the error. |
|
||||
| session.save_path patch (step 2c) | LiteSpeed patches some other compiled-in default. | The compiled-in ini defaults check fails and shows a diff. |
|
||||
| PECL reuse | A PHP patch release changes a struct layout. | The ABI gate fails. PECL extensions would then have to be rebuilt from source, which this path does not do. Without that, the emergency path cannot be used for that release. |
|
||||
| 8.1 | php.net no longer publishes 8.1 (EOL). | The workflow refuses because there is no release. |
|
||||
|
||||
No configure flags, extension lists or PECL versions are kept in this repo.
|
||||
They are read from the packaged lsphp in the same build, so they cannot go out
|
||||
of date unnoticed.
|
||||
Reference in new issue
Block a user