fix/remi-origin
159
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c49f456336 |
fix(images): install Remi packages from the origin, not the lagging mirrors
During the php2shell release the Remi mirrors still served 8.2.34 hours after 8.2.35 was published, so cac/cac-fpm builds shipped vulnerable PHP. Enable the repo files' baseurl (rpms.remirepo.net) and comment the mirrorlist. Verified on the build server: a clean almalinux/10-base install now gets PHP 8.2.35. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
bcd737a9e2 |
Merge pull request 'ci: fail-fast false on every image matrix' (#27) from fix/ci-no-fail-fast into trunk
Cloud Apache Container / Build-and-Push (85) (push) Failing after 3m27s
Cloud Apache Container / Build-and-Push (80) (push) Failing after 13m6s
Cloud Apache Container / Build-FPM-Images (80) (push) Failing after 2m7s
Cloud Apache Container / Build-and-Push (84) (push) Failing after 11m45s
Cloud Apache Container / Build-FPM-Images (85) (push) Failing after 1m19s
Cloud Apache Container / Build-FPM-Images (83) (push) Failing after 4m30s
Cloud Apache Container / Build-and-Push (83) (push) Failing after 18m2s
Cloud Apache Container / Build-LiteSpeed-Images (81) (push) Successful in 5m20s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 2m26s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 2m11s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 36s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 4m5s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 6m21s
Cloud Apache Container / Shell-Checks (push) Successful in 42s
Cloud Apache Container / Build-FPM-Images (74) (push) Successful in 5m26s
Cloud Apache Container / Build-and-Push (74) (push) Successful in 15m27s
Cloud Apache Container / Build-FPM-Images (82) (push) Failing after 2m22s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 1m46s
Cloud Apache Container / Build-FPM-Images (81) (push) Failing after 7m47s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 1m31s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 5m22s
Cloud Apache Container / Build-FPM-Images (84) (push) Failing after 8m36s
Cloud Apache Container / Build-LSPHP-Images (81) (push) Successful in 6m5s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 6m14s
Cloud Apache Container / Build-and-Push (81) (push) Failing after 2m8s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 15m24s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 5m2s
|
||
|
|
955aec4573 |
ci: fail-fast false on every image matrix
One failing PHP version (runs 4645/4646: cac-fpm php80, cac php82) cancelled every other cac/cac-fpm build, so no PHP security build of 8.3-8.5 was pushed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
998eb17f39 |
Merge pull request 'fix(php): bake php2shell ini mitigation into cac/cac-fpm/cac-lsphp/cac-litespeed' (#26) from fix/php2shell-ini-mitigation into trunk
Cloud Apache Container / Build-and-Push (80) (push) Canceled after 3m58s
Cloud Apache Container / Build-and-Push (83) (push) Canceled after 2m29s
Cloud Apache Container / Build-and-Push (84) (push) Canceled after 31s
Cloud Apache Container / Build-and-Push (85) (push) Canceled after 15s
Cloud Apache Container / Build-FPM-Images (80) (push) Failing after 5m45s
Cloud Apache Container / Build-FPM-Images (83) (push) Canceled after 5m0s
Cloud Apache Container / Build-FPM-Images (85) (push) Canceled after 2m52s
Cloud Apache Container / Build-LiteSpeed-Images (81) (push) Successful in 1m27s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 2m35s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 46s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 6m32s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 6m19s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 4m54s
Cloud Apache Container / Shell-Checks (push) Successful in 1m25s
Cloud Apache Container / Build-and-Push (74) (push) Canceled after 4m13s
Cloud Apache Container / Build-and-Push (81) (push) Canceled after 3m41s
Cloud Apache Container / Build-FPM-Images (74) (push) Successful in 5m56s
Cloud Apache Container / Build-FPM-Images (81) (push) Canceled after 5m31s
Cloud Apache Container / Build-FPM-Images (82) (push) Canceled after 5m16s
Cloud Apache Container / Build-FPM-Images (84) (push) Canceled after 3m56s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 1m31s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 4m40s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 5m53s
Cloud Apache Container / Build-LSPHP-Images (81) (push) Successful in 5m28s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 6m36s
Cloud Apache Container / Build-and-Push (82) (push) Failing after 2m58s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 1m40s
|
||
|
|
f461d22aae |
fix(php): bake php2shell ini mitigation into cac/cac-fpm/cac-lsphp/cac-litespeed
CVE-2026-109618 / CVE-2026-109619 (GHSA-w8hw-w9fx-j79w, GHSA-6wmf-4rv5-p3rp). Adds configs/zz-php2shell-mitigation.ini (fatal_error_backtraces=0, session.upload_progress.enabled=0, max_input_nesting_level=16) and installs it into each image's PHP scan dir as zz-php2shell-mitigation.ini so it sorts after 99-prod-overrides.ini and the entrypoint-written 99-* fragments: - Dockerfile / Dockerfile.fpm: COPY to /etc/php.d/ next to prod-php.ini. - Dockerfile.lsphp / Dockerfile.litespeed: COPY to /etc/lsws-templates/ and cp into the computed lsphp $SCAN_DIR in the existing overrides RUN. fatal_error_backtraces is unknown to (and silently ignored by) PHP < 8.5, so one file serves every version. Remove once all images ship PHP >= 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
cfde82ae44 |
Merge pull request 'fix(lsphp-apt-upgrade): no early-exit readers under pipefail' (#25) from fix/lsphp-upgrade-pipefail into trunk
Cloud Apache Container / Build-and-Push (84) (push) Canceled after 0s
Cloud Apache Container / Build-and-Push (85) (push) Canceled after 0s
Cloud Apache Container / Build-and-Push (80) (push) Canceled after 56s
Cloud Apache Container / Build-and-Push (83) (push) Canceled after 10s
Cloud Apache Container / Build-FPM-Images (80) (push) Failing after 4m2s
Cloud Apache Container / Build-FPM-Images (83) (push) Canceled after 1m3s
Cloud Apache Container / Build-FPM-Images (85) (push) Canceled after 33s
Cloud Apache Container / Build-LiteSpeed-Images (81) (push) Successful in 6m22s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 1m56s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 2m2s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 39s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 45s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 5m43s
Cloud Apache Container / Shell-Checks (push) Successful in 40s
Cloud Apache Container / Build-and-Push (74) (push) Failing after 1m4s
Cloud Apache Container / Build-FPM-Images (74) (push) Successful in 1m54s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 1m40s
Cloud Apache Container / Build-FPM-Images (81) (push) Canceled after 3m46s
Cloud Apache Container / Build-FPM-Images (84) (push) Canceled after 47s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 4m7s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 4m30s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 4m34s
Cloud Apache Container / Build-LSPHP-Images (81) (push) Successful in 5m46s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 5m46s
Cloud Apache Container / Build-and-Push (81) (push) Canceled after 41s
Cloud Apache Container / Build-and-Push (82) (push) Canceled after 26s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 5m15s
|
||
|
|
132e591503 |
fix(lsphp-apt-upgrade): no early-exit readers under pipefail
apt-cache madison | grep -q and apt-cache policy | awk '...exit' can SIGPIPE the writer under pipefail and report lsphp missing when it is there. Read each output into a variable and match in the shell. Fixes the Shell-Checks failure on trunk (lsphp-info-probe.test.sh structural scan). Tested: lsphp-info-probe.test.sh 36/36, shellcheck clean, and the script run in litespeedtech/openlitespeed:1.8.4-lsphp85 upgrades 8.5.0 -> 8.5.11 OK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
642b97707d |
Merge pull request 'feat(images): upgrade lsphp from the LiteSpeed repo at build time; label PHP version' (#24) from feat/lsphp-upgrade-at-build into trunk
Cloud Apache Container / Build-FPM-Images (80) (push) Successful in 1m45s
Cloud Apache Container / Build-and-Push (80) (push) Successful in 5m50s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 5m55s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 5m37s
Cloud Apache Container / Build-and-Push (84) (push) Successful in 5m53s
Cloud Apache Container / Build-FPM-Images (85) (push) Successful in 1m43s
Cloud Apache Container / Build-FPM-Images (83) (push) Successful in 6m32s
Cloud Apache Container / Build-LiteSpeed-Images (81) (push) Successful in 5m30s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 3m1s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 34s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 3m8s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 5m9s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 5m25s
Cloud Apache Container / Shell-Checks (push) Failing after 12s
Cloud Apache Container / Build-and-Push (74) (push) Successful in 1m55s
Cloud Apache Container / Build-FPM-Images (74) (push) Successful in 1m47s
Cloud Apache Container / Build-and-Push (81) (push) Successful in 6m29s
Cloud Apache Container / Build-FPM-Images (81) (push) Successful in 1m52s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 1m39s
Cloud Apache Container / Build-LSPHP-Images (81) (push) Successful in 2m6s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 5m24s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 4m58s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 5m33s
Cloud Apache Container / Build-FPM-Images (84) (push) Successful in 6m41s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 6m5s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 6m6s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 1m27s
|
||
|
|
71ca114529 |
feat(images): upgrade lsphp from the LiteSpeed repo at build time; label PHP version
cac-litespeed and cac-lsphp start FROM litespeedtech/openlitespeed:1.8.4-lsphpNN, which ships lsphp85 8.5.0 / lsphp84 8.4.15 (built 2025-12). lsphp only moved forward as a side effect of installing lsphpNN-ldap. lsphp-apt-upgrade.sh now sweeps every installed lsphpNN* package (and OS packages) to the repo's current release on every build and fails the build if the repo is unreachable or the result doesn't match what CI resolved from the repo index. CI passes a per-run CACHE_BUST so the upgrade layer is never a cache hit, and images carry org.whp.php-version / org.whp.lsphp-package-version labels. Tested on .148 (2026-10-21): cac-lsphp php85/php84 and cac-litespeed php85 built from this branch report the same PHP (8.5.11 / 8.4.26), identical module lists and OLS 1.8.4 as the current registry images; with the hardening flags each serves/runs PHP as the customer uid, restarts, and stops cleanly (exit 0). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
74e12407f8 |
Merge pull request 'fix(entrypoint): no recursive chmod, graceful stop, fpm error_log owner, symlink-safe root paths, dotfile deny' (#23) from fix/no-recursive-chmod-on-start into trunk
Cloud Apache Container / Build-FPM-Images (80) (push) Successful in 1m51s
Cloud Apache Container / Build-and-Push (80) (push) Successful in 9m14s
Cloud Apache Container / Build-and-Push (84) (push) Successful in 8m24s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 8m51s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 8m43s
Cloud Apache Container / Build-LiteSpeed-Images (81) (push) Successful in 3m16s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 1m13s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 1m57s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 31s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 4m10s
Cloud Apache Container / Build-FPM-Images (83) (push) Successful in 9m24s
Cloud Apache Container / Build-FPM-Images (85) (push) Successful in 9m38s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 5m13s
Cloud Apache Container / Shell-Checks (push) Successful in 12s
Cloud Apache Container / Build-and-Push (74) (push) Successful in 1m58s
Cloud Apache Container / Build-FPM-Images (74) (push) Successful in 2m36s
Cloud Apache Container / Build-and-Push (81) (push) Successful in 8m48s
Cloud Apache Container / Build-FPM-Images (81) (push) Successful in 3m16s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 2m44s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 59s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 3m11s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 3m38s
Cloud Apache Container / Build-LSPHP-Images (81) (push) Successful in 1m26s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 3m34s
Cloud Apache Container / Build-FPM-Images (84) (push) Successful in 9m38s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 8m45s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 1m22s
|
||
|
|
74648ba34b |
fix(security): deny config/VCS dotfiles over HTTP on standalone cac and cac-litespeed
Measured on .148: standalone cac served /.env with its contents, and cac-litespeed served /.env even at mode 0640 (OLS runs as the customer uid there). shared-httpd and shared-ols already deny these via WHP's drop-ins; the standalone tiers never got them. - cac: new configs/whp-security.conf in /etc/httpd/conf.d — the same named FilesMatch + DirectoryMatch list as WHP's shared-httpd-security.conf, re-rooted to /home. - cac-litespeed: the same `context exp:` regex as WHP's shared-ols vhconf template, added to the vhost template. Named list (.user.ini .env* .git .svn .hg .bzr .ht* .whp .site-builder), not a blanket dotfile deny, so /.well-known/ stays reachable. HTTP only: PHP still reads these files from disk. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
86a88c87db |
fix(safe-paths): unlink planted symlinks as the customer, then fail closed
Root rm -f in drop_symlink could be redirected by a parent-directory swap to delete arbitrary files as root. drop_symlink and the logs/ symlink sweep now unlink AS THE CUSTOMER (can never delete anything the customer couldn't), and drop_symlink still refuses to start if the path is a symlink afterwards; the pre-daemon logs/ gate re-checks and fails closed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
02d93f4d1a |
fix(safe-paths): fail closed on planted symlinks before root daemons start
drop_symlink unlinks as root (rm on the final component never follows the target and the customer cannot deny it) and refuses to start if the path is still a symlink. user_touch refuses unless it ends up a regular file. New require_safe_logs runs immediately before httpd / php-fpm start (they open files under /home/$user/logs as root): logs/, logs/apache, logs/php-fpm must be real directories and nothing under logs/ may be other than a regular file or directory, else the entrypoint exits 1 without starting any daemon. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
94a73ea8ab |
fix(safe-paths): unlink symlinks and create files as the customer, never as root
drop_symlink and the logs/ symlink sweep now unlink AS THE CUSTOMER, and user_touch no longer falls back to a root chown -h (a parent dir swapped for a symlink between check and use could still redirect a root path operation). A root-owned leftover error_log is fixed by the existing chown -R, which never follows symlinks. Every operation these helpers perform is now safe even if the customer races it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0ffccac4b8 |
fix(entrypoint): root never follows customer symlinks under /home/$user
/home/$user is the customer's bind mount; they can plant symlinks in it. The entrypoints ran as root and wrote/chowned through customer paths, so e.g. logs/php-fpm/error.log -> /etc/shadow (or a dangling crontab -> /etc/...) handed the customer that file, i.e. container root. New scripts/safe-paths.sh, used by entrypoint.sh and entrypoint-fpm.sh: - secure_home: refuse to start if /home/$user is not a real dir; logs/ must be a real dir and every symlink under logs/ is removed and logged (apache and php-fpm open those files as root at start). - dirs, the pool error_log and the default crontab are created AS THE CUSTOMER (setpriv, runuser fallback); a pre-existing root-owned error_log is fixed with chown -h (never dereferences). - the user crontab is read as the customer and piped to `crontab -u $user -` (root reading it could install any root-readable file where the customer can `crontab -l` it). DEV-mode crontab/mysql_creds are written via as_user tee. - the o+x directory pass (find -type d, no -L) runs as the customer, so a dir swapped for a symlink before chmod can only reach what the customer owns. - chown -R is unchanged: GNU default -P lchown()s symlinks, never follows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
eaf616eb30 |
fix(entrypoint): graceful docker stop + cac-fpm error_log owner on first start
Graceful stop (cac, cac-fpm, shared-httpd, and form-sender which runs cac): entrypoint.sh, entrypoint-fpm.sh and entrypoint-shared-httpd.sh end with bash as PID 1 tailing logs. PID 1 only receives signals it has a handler for, so docker stop's SIGTERM was dropped; docker waited the full 10s and SIGKILLed apache / php-fpm mid-request (exit 137). The entrypoints now trap TERM/INT and stop each daemon they started via the new scripts/stop-services.sh: httpd graceful-stop (WINCH), php-fpm QUIT, crond TERM, each with a bounded wait and a TERM/KILL fallback, budgets summing to < 10s, then exit 0. The log tail is backgrounded + `wait`ed (and shared-httpd's 60s rescan sleep likewise) so the trap runs immediately. Log streaming to docker logs is unchanged. cac-fpm error_log: entrypoint-fpm.sh `touch`ed logs/php-fpm/error.log as root AFTER its chown -R, so on a site's first start the pool (customer uid) could not open it and PHP errors went out as FastCGI stderr to shared-httpd's log until the next restart. The file is now created and chowned to the customer right after the logs dir is made, before php-fpm starts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
14761597bb |
fix(entrypoint): stop chmod -R 755 on the customer docroot every start
entrypoint.sh and entrypoint-fpm.sh ran `chmod -R 755 /home/$user` on every container start. /home/$user is the bind-mounted customer site directory, so every file (wp-config.php included) became world-readable and executable, and any tighter permissions the customer set were reverted on each restart. The line dates from the initial commit ( |
||
|
|
db19ee24c3 |
ci: add workflow_dispatch trigger for manual rebuilds
Cloud Apache Container / Build-and-Push (80) (push) Successful in 2m32s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 3m13s
Cloud Apache Container / Build-FPM-Images (83) (push) Successful in 3m32s
Cloud Apache Container / Build-FPM-Images (85) (push) Successful in 1m25s
Cloud Apache Container / Build-LiteSpeed-Images (81) (push) Successful in 32s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 4m9s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 1m19s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 1m51s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 3m47s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 33s
Cloud Apache Container / Build-FPM-Images (80) (push) Successful in 2m37s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 7m42s
Cloud Apache Container / Build-and-Push (84) (push) Successful in 6m47s
Cloud Apache Container / Shell-Checks (push) Successful in 33s
Cloud Apache Container / Build-and-Push (74) (push) Successful in 2m32s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 1m31s
Cloud Apache Container / Build-FPM-Images (84) (push) Successful in 1m23s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 2m36s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 43s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 1m26s
Cloud Apache Container / Build-LSPHP-Images (81) (push) Successful in 4m28s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 1m41s
Cloud Apache Container / Build-FPM-Images (81) (push) Successful in 2m21s
Cloud Apache Container / Build-FPM-Images (74) (push) Successful in 6m19s
Cloud Apache Container / Build-and-Push (81) (push) Successful in 2m15s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 4m1s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 6m3s
|
||
|
|
3841b18820 |
fix(shared-ols): enableCache 0 so headerless responses are never cached
Cloud Apache Container / Build-and-Push (80) (push) Successful in 1m48s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 2m35s
Cloud Apache Container / Build-and-Push (84) (push) Successful in 1m58s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 2m14s
Cloud Apache Container / Build-FPM-Images (80) (push) Successful in 2m30s
Cloud Apache Container / Build-FPM-Images (83) (push) Successful in 2m47s
Cloud Apache Container / Build-FPM-Images (85) (push) Successful in 2m44s
Cloud Apache Container / Build-LiteSpeed-Images (81) (push) Successful in 37s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 1m2s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 2m6s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 1m1s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 33s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 31s
Cloud Apache Container / Shell-Checks (push) Successful in 31s
Cloud Apache Container / Build-and-Push (74) (push) Successful in 2m11s
Cloud Apache Container / Build-and-Push (81) (push) Successful in 3m18s
Cloud Apache Container / Build-FPM-Images (74) (push) Successful in 3m12s
Cloud Apache Container / Build-FPM-Images (81) (push) Successful in 2m30s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 2m15s
Cloud Apache Container / Build-FPM-Images (84) (push) Successful in 2m52s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 1m24s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 38s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 1m31s
Cloud Apache Container / Build-LSPHP-Images (81) (push) Successful in 1m1s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 58s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 2m25s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 34s
LSCWP's Divi compat disables the plugin for ?et_fb= / ?et_pb_preview= / ?preview=true, so those responses carry no X-LiteSpeed-Cache-Control header. With module-level enableCache 1, OLS cached them anyway (Divi Visual Builder pages served from cache; the plugin's own QS exclusion never runs). enableCache 0 caches only responses that opt in via an explicit X-LiteSpeed-Cache-Control: public header. Measured on OLS 1.8.4: headerless = miss,hit under 1 / not cached under 0; explicit public cached under both. Matches the dedicated cac-litespeed tier. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
48b714447f |
chore: pin the shared-memory project key
Cloud Apache Container / Build-and-Push (80) (push) Successful in 2m1s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 3m27s
Cloud Apache Container / Build-and-Push (84) (push) Successful in 2m8s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 1m28s
Cloud Apache Container / Build-FPM-Images (80) (push) Successful in 1m46s
Cloud Apache Container / Build-FPM-Images (83) (push) Successful in 2m9s
Cloud Apache Container / Build-FPM-Images (85) (push) Successful in 2m19s
Cloud Apache Container / Build-LiteSpeed-Images (81) (push) Successful in 1m14s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 2m33s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 1m56s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 1m2s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 29s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 33s
Cloud Apache Container / Shell-Checks (push) Successful in 12s
Cloud Apache Container / Build-and-Push (74) (push) Successful in 1m31s
Cloud Apache Container / Build-and-Push (81) (push) Successful in 2m28s
Cloud Apache Container / Build-FPM-Images (74) (push) Successful in 1m59s
Cloud Apache Container / Build-FPM-Images (81) (push) Successful in 2m10s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 1m26s
Cloud Apache Container / Build-FPM-Images (84) (push) Successful in 6m24s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 42s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 34s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 35s
Cloud Apache Container / Build-LSPHP-Images (81) (push) Successful in 1m17s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 59s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 2m32s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 1m25s
Ties every collaborator's Claude Code to the same shared-memory project automatically, instead of each machine inferring a key from the directory name. Read by project.identify at session start. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ba9650ee45 |
Merge branch 'fix/ols-watcher-scope'
Cloud Apache Container / Build-and-Push (80) (push) Successful in 2m18s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 2m16s
Cloud Apache Container / Build-and-Push (84) (push) Successful in 2m6s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 2m19s
Cloud Apache Container / Build-FPM-Images (80) (push) Successful in 2m45s
Cloud Apache Container / Build-FPM-Images (83) (push) Successful in 2m37s
Cloud Apache Container / Build-FPM-Images (85) (push) Successful in 2m16s
Cloud Apache Container / Build-LiteSpeed-Images (81) (push) Successful in 35s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 1m20s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 1m7s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 1m1s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 35s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 29s
Cloud Apache Container / Shell-Checks (push) Successful in 10s
Cloud Apache Container / Build-and-Push (81) (push) Successful in 2m6s
Cloud Apache Container / Build-FPM-Images (74) (push) Successful in 2m14s
Cloud Apache Container / Build-FPM-Images (81) (push) Successful in 2m26s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 2m28s
Cloud Apache Container / Build-FPM-Images (84) (push) Successful in 2m25s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 35s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 34s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 40s
Cloud Apache Container / Build-LSPHP-Images (81) (push) Successful in 1m7s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 1m4s
Cloud Apache Container / Build-and-Push (74) (push) Successful in 2m11s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 2m22s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 35s
|
||
|
|
77af001af6 |
fix(ols): pin procps explicitly for pgrep dependency
entrypoint-shared-ols.sh's ols_running() liveness check now shells out to pgrep, but procps was never in Dockerfile.shared-ols's apt-get install list — pgrep works today only because Ubuntu 24.04's base image pulls procps in transitively. If that stops being true, pgrep: command not found -> exit 127 -> ols_running() false forever -> the crash-loop breaker (MAX_STARTS/WINDOW) escalates to a hard exit 1 at boot. Make the dependency explicit so it can't be pruned as unused. |
||
|
|
cf6936e225 |
fix(ols): scope the htaccess watcher to docroots, stop lswsctrl status log spam
ols-htaccess-watcher.sh matched .htaccess by basename only, so ANY .htaccess under a tenant (WordPress plugin guard files, not just the docroot OLS reads) triggered a full graceful restart. Measured on whp01 over 24h: 63 restarts, 0 of them from a docroot .htaccess actually changing — all from Wordfence/W3TC/ WPForms/etc. self-healing files, mostly on tenants that aren't even on this tier. Now matches the full path (%w%f) against */public_html/.htaccess, the only .htaccess OLS ever reads, and logs which path triggered each restart. entrypoint-shared-ols.sh's 3s supervisor poll called `lswsctrl status`, which appends a line to lsrestart.log on every invocation. Measured on whp01: 1,819,286 status lines vs 2,429 real restarts in a 96 MB, never-rotated log. ols_running() now checks the process table directly (pgrep -f 'lshttpd - main', verified against the litespeedtech/openlitespeed base image) instead of shelling out to a logging tool on a fixed timer. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
b92725d2ec |
Merge branch 'fix/lsphp-keep-listen'
Cloud Apache Container / Build-and-Push (80) (push) Successful in 1m28s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 1m29s
Cloud Apache Container / Build-and-Push (84) (push) Successful in 1m27s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 1m28s
Cloud Apache Container / Build-FPM-Images (80) (push) Successful in 1m29s
Cloud Apache Container / Build-FPM-Images (83) (push) Successful in 1m27s
Cloud Apache Container / Build-FPM-Images (85) (push) Successful in 1m27s
Cloud Apache Container / Build-LiteSpeed-Images (81) (push) Successful in 37s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 1m3s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 1m1s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 57s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 34s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 31s
Cloud Apache Container / Shell-Checks (push) Successful in 9s
Cloud Apache Container / Build-and-Push (74) (push) Successful in 1m39s
Cloud Apache Container / Build-and-Push (81) (push) Successful in 1m27s
Cloud Apache Container / Build-FPM-Images (74) (push) Successful in 1m29s
Cloud Apache Container / Build-FPM-Images (81) (push) Successful in 1m26s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 1m31s
Cloud Apache Container / Build-FPM-Images (84) (push) Successful in 2m24s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 35s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 35s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 33s
Cloud Apache Container / Build-LSPHP-Images (81) (push) Successful in 1m1s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 57s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 1m27s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 35s
|
||
|
|
5e83c8db3b |
fix(lsphp): set LSAPI_KEEP_LISTEN=2 to stop idle-exit timing from following the leaked busy counter
lsphp's master keeps a `busy` worker counter in a MAP_SHARED page that drifts negative over days of uptime (measured live on whp01: busy=-8 after 6.9 days on arclightcourt.com-01 vs 0..9 on a healthy sibling). php-src's sapi/litespeed/lsapilib.c derives each child's idle-exit grace period from that counter (10 + busy*10, capped by LSAPI_MAX_IDLE) only inside `if (s_keep_listener == 1)`; with busy=-8 that's -70s, so workers exit after ~1s idle instead of 10-30s, no worker ever lingers in accept(), the "don't fork, one's already listening" guard never fires, and the master forks for every connection -- confirmed hitting the max-children ceiling and producing 503s under bot traffic (306 OLS-side ExtConn-timeout/503 errors on the affected site vs 0 on an identical healthy sibling). LSAPI_KEEP_LISTEN=2 skips the `== 1` branch entirely so idle-exit timing falls back to LSAPI_MAX_IDLE (already 30 by default here) instead of the leaked counter. is_enough_free_mem() sits above that branch, not inside it, so the memory-pressure guard is unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
a325615690 |
Merge branch 'fix/lsphp-pipefail-sigpipe'
Cloud Apache Container / Build-and-Push (80) (push) Successful in 1m22s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 1m24s
Cloud Apache Container / Build-and-Push (84) (push) Successful in 1m28s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 1m41s
Cloud Apache Container / Build-FPM-Images (80) (push) Successful in 1m22s
Cloud Apache Container / Build-FPM-Images (83) (push) Successful in 1m23s
Cloud Apache Container / Build-FPM-Images (85) (push) Successful in 1m22s
Cloud Apache Container / Build-LiteSpeed-Images (81) (push) Successful in 32s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 1m1s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 1m0s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 1m1s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 37s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 31s
Cloud Apache Container / Shell-Checks (push) Successful in 9s
Cloud Apache Container / Build-and-Push (74) (push) Successful in 1m27s
Cloud Apache Container / Build-and-Push (81) (push) Successful in 1m52s
Cloud Apache Container / Build-FPM-Images (74) (push) Successful in 1m39s
Cloud Apache Container / Build-FPM-Images (81) (push) Successful in 1m20s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 1m28s
Cloud Apache Container / Build-FPM-Images (84) (push) Successful in 1m25s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 32s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 33s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 34s
Cloud Apache Container / Build-LSPHP-Images (81) (push) Successful in 58s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 59s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 1m23s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 32s
Fix nine 'writer | early-exiting-reader' pipelines that return 141 under set -euo pipefail when the reader exits before the writer's final write(). The one that mattered: entrypoint-lsphp.sh reported the cac_path_parity extension as 'not loadable' BECAUSE it was present, and fell back to the degraded userland auto_prepend normaliser — the exact thing the extension exists to eliminate. Measured on whp02 against the published image: 141 on 5/5 runs. It shipped past a green build gate, a 10/10 .phpt suite, a 9/9 FPM harness and two review rounds, and was only caught by pulling the published image and running it on a real host. Two of the nine were bare command substitutions in entrypoints, where 141 under set -e kills PID 1 and the container never boots (entrypoint-lsphp.sh SCAN_DIR, entrypoint-litespeed.sh — boot-critical for cac-litespeed). Payload size is NOT the variable: 41144 bytes fails 32/300 well under a 65536-byte pipe capacity, while 500 KB into a 1 MiB pipe fails 200/200 with small writes and 0/200 with one large write. It is a race on whether the reader closes before the writer's last write() returns. The same image failed 5/5 on whp02 and 0/10 in a dev container because bash <=5.2.15 writes ~37 KB per syscall while >=5.2.21 writes 80-160 bytes. Boot-critical sites use pure-bash matching (no temp file); a regression test extracts the shipped helpers verbatim and statically outlaws the shape repo-wide, failing against the pre-fix tree with all 9 lines named. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e28c3dcce4 |
fix(lsphp): correct the SIGPIPE explanation, drop the temp-dir boot precondition, close two scanner blind spots
Three non-blocking findings from the review of
|
||
|
|
584099ff19 |
docs: record boot-posture and CI-gating decisions
Two judgement calls arising from the pipefail/SIGPIPE fix, reasoned through and written up rather than decided silently: 1. Boot posture when 'lsphp -i' genuinely fails. Recommendation is fail-open plus a machine-detectable degraded marker and one probe retry, NOT fail-closed: a transient fork() failure during this fleet's backup windows produces the identical empty-LSPHP_INFO signature, so fail-closed would refuse to boot healthy sites under host pressure. Includes the caution that degraded must not map onto Docker 'unhealthy', or a watchdog would restart-loop a serving site over something restarts cannot fix. Corrects a premise the fix rested on: the two entrypoints were NOT already consistent — entrypoint-litespeed.sh fails open silently, with no warning. 2. Whether the new static check should gate releases. Recommendation is to hard-gate, but only after back-testing the job against trunk and recent tags to bound false positives. Recommendations only; no implementation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
8790b027a9 |
fix(lsphp): stop SIGPIPE+pipefail reporting the parity extension as missing
`entrypoint-lsphp.sh` decided whether cac_path_parity was loaded with
printf '%s\n' "$LSPHP_INFO" | grep -q '^cac_path_parity support => enabled$'
under `set -euo pipefail`. `grep -q` exits on its first match; printf is still
writing the remaining ~40 KB of `lsphp -i`, takes SIGPIPE, exits 141, and
pipefail prefers 141 over grep's 0. The branch therefore evaluated FALSE
*because the extension was present* — present early enough to stop the reader —
and every affected container fell back to the auto_prepend normaliser that a
customer's own .user.ini silently displaces, i.e. the exact failure the
extension exists to remove. Measured on whp02 against the published
cac-lsphp:php83: 5/5 runs status=141 with pipefail, 0 without.
The race is decided by pipe capacity, which is why it reproduced on whp02 and
not on other daemons: while the payload fits the pipe the writer never blocks
and always finishes first. Forced over the limit it is deterministic — 3x the
same `lsphp -i` (122100 bytes) gives 141 every time in the built image.
Fixed by reading with here-strings, which are not pipelines at all, so there is
no second exit status for pipefail to adopt. Same grep/awk patterns; plumbing
only. Same class fixed everywhere it existed under pipefail:
* entrypoint-lsphp.sh parity probe, and the SCAN_DIR awk probe
* entrypoint-litespeed.sh SCAN_DIR probe (a bare assignment: 141 there does
not degrade, `set -e` kills PID 1), and ols_running
* entrypoint-shared-ols.sh ols_running
* render-shared-ols-config.sh site.meta parsing (`sed | head -1`): measured
141 at 6000 duplicate keys, which under `set -e`
aborts the whole render
* fpm-parity-check.sh the `php-fpm -m` pre-flight, whose whole job is to
stop a harness fault being blamed on the extension
Also: the fallback used to announce "cac_path_parity extension not loadable in
this image" for every reason the branch was reached, including its own plumbing
breaking — a false diagnosis that sends operators to rebuild a good image whose
build gate passed. Verdicts now carry the evidence they rest on, and a probe
that produced nothing is reported as a probe failure that establishes nothing
about the image. Fail-open posture is unchanged: no probe failure is fatal.
Adds scripts/tests/lsphp-info-probe.test.sh, which runs the shipped probes
(extracted verbatim, so they cannot drift from what runs in production) under
`set -euo pipefail` against a realistic ~40 KB phpinfo body, and statically
outlaws the shape repo-wide. Against trunk it fails, naming all 9 offending
lines. Wired into CI as a new Shell-Checks job, because no existing gate ever
executed the entrypoint's branch logic — the .phpt suite and the Dockerfile's
own `lsphp -i | grep -q` probe (which has no pipefail) were both green for the
release whose entrypoint declared that same extension missing.
Verified: PHP 8.3 --no-cache build green, 10/10 .phpt, 9/9 FPM harness; the
built image logs `path parity = extension` and reports `Rewriting => active`
with .from/.to populated; ext-removed and probe-broken variants each produce
their own honest message and still start.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
9343a56ccf |
Merge branch 'fix/lsphp-parity-hardening'
Cloud Apache Container / Build-and-Push (80) (push) Successful in 2m6s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 1m23s
Cloud Apache Container / Build-and-Push (84) (push) Successful in 1m24s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 1m26s
Cloud Apache Container / Build-FPM-Images (80) (push) Successful in 1m29s
Cloud Apache Container / Build-FPM-Images (83) (push) Successful in 2m7s
Cloud Apache Container / Build-FPM-Images (85) (push) Successful in 1m25s
Cloud Apache Container / Build-LiteSpeed-Images (81) (push) Successful in 33s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 59s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 1m2s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 1m4s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 33s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 30s
Cloud Apache Container / Build-and-Push (74) (push) Successful in 1m35s
Cloud Apache Container / Build-and-Push (81) (push) Successful in 1m23s
Cloud Apache Container / Build-FPM-Images (74) (push) Successful in 1m30s
Cloud Apache Container / Build-FPM-Images (81) (push) Successful in 4m40s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 1m25s
Cloud Apache Container / Build-FPM-Images (84) (push) Successful in 1m28s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 32s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 32s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 34s
Cloud Apache Container / Build-LSPHP-Images (81) (push) Successful in 1m1s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 1m0s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 1m22s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 33s
Hardening for the cac-path-parity PHP extension, which moves
cac-lsphp-normalize.php out of userland so the php-fpm -> ols/lsphp switch
is clean.
- pin the runtime PHP to the headers the extension was built against
(the reverse, pinning -dev to the runtime, is unsatisfiable: the
LiteSpeed repo carries only the current release)
- close an ini-injection hole in the entrypoint heredoc (quoting plus a
charset check, since quoting alone does not stop php.ini ${VAR}
interpolation)
- make the FPM parity harness actually runnable (it exited 0 while
verifying nothing) and add the .phpt suite as a build gate
- guard to="/" and non-absolute mappings; both narrow what runs, neither
adds an error path
- fix PHP_MINFO reporting 'active' for an inert mapping — that signal is
what the post-deploy canary reads
Reviewed twice; RINIT verified byte-identical at the instruction level
across the predicate refactor. Fail-open invariant intact.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
3047123f2b |
docs(lsphp): correct three comments that overstated what the code does
Review found all three describing behaviour the code does not have: - The range bound does NOT prevent opcache's shared-memory startup failure. With 99-prod-overrides setting interned_strings_buffer=16, a memory_consumption of 8 or 16 is accepted here and still aborts opcache. Documented rather than raising the floor, which would forfeit the superset property. - memory_consumption's 4096 ceiling is ours, not PHP's — PHP imposes no upper bound on that directive. Only the max_accelerated_files range is a vendor clamp. Also records that an out-of-range value resets to PHP's COMPILED default, discarding the image's own override. - The stale-fragment rm -f is defensive, not a bug fix: changing these env vars requires a recreate, which starts from a fresh layer, so the scenario the comment described is not reachable via docker restart. Comments only; no behaviour change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
07378506a7 |
harden(cac-lsphp): close the two remaining unvetted ini emissions; stop MINFO lying
Three non-blocking findings from the re-review of this branch. No design change:
the extension's fail-open-absolute invariant is untouched (still zero error
emitters, every RINIT return is SUCCESS) and both RINIT guards stay pure
narrowing.
1. entrypoint-lsphp.sh: 99-user-opcache.ini was still emitted unquoted
-------------------------------------------------------------------
Twenty-five lines below the mapping fix, the opcache override block
interpolated the raw env into an unquoted `echo` — the same injection class
the mapping fix closed. Measured on this branch's image, before this commit:
OPCACHE_MEMORY_MB=$'128\nprecision = 7\n; '
-> 99-user-opcache.ini gained a `precision = 7` line
-> lsphp -i reported precision => 7 => 7
WHP casts (int) and clamps 32-512 / 2000-32000 (site-pool-env.php), so this
is not exploitable today — but "the panel validates it" is precisely the
argument this branch already rejected for `domain`, and the panel is a
different repo on a different release cadence. Both siblings in the block are
now validated at the point of use (digits only, length-capped, range-checked)
and emitted double-quoted. A rejected value is dropped with a WARNING and the
image default applies; nothing here is ever fatal.
The accepted ranges are PHP's own limits for these directives (>= 8 MB;
[200, 1000000] files), deliberately a strict SUPERSET of the panel's clamps,
so widening a panel clamp later cannot start silently rejecting real sites.
The block now also removes a stale fragment when it has nothing valid to
write: the container filesystem outlives `docker restart`, so without that an
override that is later cleared — or rejected — would keep applying from the
previous boot's file.
2. 99-user-error-log.ini was written from an unvetted $user
--------------------------------------------------------
It was emitted before the INI_TOKENS_OK branch. Contained in practice (a
newline is inert inside the quotes, and a `${`-bearing user cannot exist
because useradd would have failed under `set -euo pipefail`), but "this
particular unvetted value happens to be contained" is the reasoning this
branch rejected one screenful up. Now gated identically.
Costs a rejected user nothing it needs: `log_errors = On` is already baked in
by 99-prod-overrides.ini, so PHP still logs — to stderr, i.e. `docker logs`,
which is more visible than a per-site file, not less. Verified fleet-wide
that no legitimate user reaches the branch (30 shared_ols sites, 4 hosts).
3. MINFO reported "active" for mappings RINIT ignores
---------------------------------------------------
The absolute-path guard was added to RINIT and MINFO kept testing only "both
values non-empty", so:
from=mnt/users/bob/site.com (relative -> INERT since the guard landed)
lsphp -i -> Rewriting => active
That row is what the post-deploy fleet canary greps to confirm parity is live,
so the diagnostic would have masked exactly the failure the canary exists to
find — and the C comment added by this branch documents it as the only runtime
signal. RINIT and MINFO now share one predicate pair
(cacpp_mapping_configured / cacpp_mapping_active) rather than two longhand
copies, which is what drifted. MINFO now distinguishes "inactive (mapping not
absolute)" from "inactive (unconfigured)" — different operational problems.
Pure reporting change: the predicates are side-effect-free and cannot fail, so
MINFO gains no error path.
Tests: two new .phpt cover both directions of the MINFO fix (009 relative
mapping must report inactive, 010 well-formed mapping must still report active),
so tightening it cannot overshoot into the opposite lie. The build gate's
EXPECTED count is derived from `ls tests/*.phpt`, so it picked them up: 10/10.
Non-vacuity, all five demonstrated by mutation:
- opcache quoting reverted -> injection lands, `precision => 7` observed
- error-log gate removed -> fragment written from the unvetted user
- MINFO reverted to non-empty -> 009 FAILS, build gate exits 1
- MINFO forced always-inactive -> 010 FAILS, build gate exits 1
- all restored -> 10/10, build exit 0
|
||
|
|
9761157a6b |
harden(cac-path-parity): make degenerate mappings inert instead of subtly wrong
Three loose ends from the review, none reachable from entrypoint-lsphp.sh today.
The rewrite semantics and the prefix-boundary logic are untouched; both new
guards only NARROW the set of configurations that do anything, and neither adds
an error path — fail-open is unchanged.
- to="/" produced "//public_html": cacpp_trim() keeps a lone separator, and
the tail already starts with one. Collapse the prefix when there is a tail,
keep it when there is not (value == from exactly, where "/" is correct).
A doubled leading slash is not the same string as the cac-fpm value, which
is the entire point of the extension.
- a non-absolute `from`/`to` was accepted and applied. Both are now required
to start with '/', otherwise RINIT returns exactly as it does for an absent
mapping: inert, no diagnostic, request proceeds.
- a well-formed but WRONG mapping stays undetectable, and now the FAILURE
MODES block says so explicitly rather than leaving it as an unlisted gap,
along with why that is acceptable (the entrypoint derives from/to from the
same two variables it builds the compatibility symlink from, so a wrong
mapping means the symlink is wrong too and the site is already broken more
loudly) and where the only runtime signal is (`lsphp -i`).
Two tests added, both non-vacuous — 007 rewrites without the absolute-path
guard, 008 returns "//public_html" without the collapse. 8/8 pass on PHP
8.1/8.3/8.5, and the FPM harness still reports 9/9 against the changed .so.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
61bfdcfaf9 |
test(cac-path-parity): run the .phpt suite as a build gate
Nothing executed ext/cac-path-parity/tests/ — not Dockerfile.lsphp, not
.gitea/workflows/build-push.yaml. The suite passed, but as shipped it was
documentation, not a gate.
`make test` now runs in the ext-build stage, against the same lsphp build the
.so ships next to. It costs ~1s per PHP version. The lsphp packages turn out to
include a real CLI binary (php-config --php-binary =>
/usr/local/lsws/lsphpNN/bin/phpN.N), so run-tests.php works with no extra
tooling.
Guarded twice, because `make test` fails silently by default:
- if PHP_EXECUTABLE is missing, the Makefile prints "Cannot run tests without
CLI sapi." and EXITS 0. Asserted rather than assumed.
- a run that executes zero tests also exits 0, so the summary is checked
against the number of .phpt files on disk, plus "Tests failed : 0".
Same reasoning as the `lsphp -i` probe: an assertion that cannot fail is
worse than no assertion.
Verified 8/8 on PHP 8.1/8.3/8.5. Mutation-tested both guards: breaking
001-rewrite.phpt's expectation fails the build ("FATAL: cac_path_parity .phpt
suite FAILED"); adding a test that always SKIPs makes run-tests.php still exit 0
but the build fails on "expected all 9 .phpt tests to run" (summary read
"Number of tests : 9 8").
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
fb4946641a |
fix(cac-path-parity): make the FPM proof harness actually runnable as shipped
The artifact cited as the web-SAPI evidence could not have been run as it stood.
Measured in an official php:8.3-fpm container with the extension built in place:
- as shipped, no args: "SKIP: php-fpm not found", exit 0.
The default was `php-fpm8.3`, which matches neither the official images
(`php-fpm`) nor this repo's images.
- with the binary supplied by hand: 9 FAIL, every one with an empty `got:`.
The generated pool had no user/group, so php-fpm refused to start as root
("please specify user and group other than root"). A startup failure was
wearing the costume of nine parity bugs.
Changes:
- auto-detect the binary (php-fpm, php-fpm8.N, /usr/local/sbin, /usr/sbin) and
print which one was chosen plus its version;
- pre-flight the extension with `php-fpm -m`, so a .so that will not load into
THIS php-fpm reports as a harness failure naming the ABI mismatch rather
than as nine wrong paths;
- emit user/group in the pool when running as root, resolved from accounts
that actually exist (www-data / nobody / daemon), and chmod the fixture tmpdir
so the non-root worker can read it;
- run_case() now returns non-zero when php-fpm never answered, and every call
site routes that to die_startup(), which prints the php-fpm output and the
pool error_log and exits 2 — an exit code deliberately distinct from 1
(assertion failure).
After: 9/9 ALL PASS from a clean checkout with no arguments and no environment
fixing, running as root in php:8.3-fpm. Mutation-tested both new paths: a pool
user that does not exist reports "HARNESS FAILURE ... STARTUP/environment
failure" with the real php-fpm error and exit 2; an EXT_SO that is not a loadable
extension is caught by the pre-flight, also exit 2.
Also fixes doc drift: 001-rewrite.phpt pointed at tests/web-sapi-parity-check.sh,
which has never existed. The file it means is tests/fpm-parity-check.sh.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
690ff8738d |
fix(cac-lsphp): stop generating php.ini fragments from unquoted interpolation
The two generated ini drop-ins interpolated $user/$domain into an unquoted
heredoc. Measured against the pre-fix script in a real cac-lsphp:php83 container
with domain=$'evil.com\nprecision = 7\n; ':
99-cac-path-parity.ini contained the injected line and lsphp reported
`precision => 7 => 7` — an arbitrary ini directive supplied through the domain
env var and applied to every request. The `from` value was silently truncated
at the newline too, so the site also got a wrong (but "active") mapping.
Both values are panel-validated and both already feed `ln -sfn` and the
shared-ols vhost config, so this is defense-in-depth rather than a live hole. It
is worth closing anyway because the OTHER two hostile inputs the reviewer
measured — `$(...)` (ini parse error) and `"` (empty value) — leave the parity
extension INERT, which is precisely the silent failure this whole change set
exists to eliminate.
Two layers, neither of which can fatal a request:
- values are emitted double-quoted via printf instead of heredoc
interpolation. php.ini double-quoted values may span newlines, so a newline
is data, not a new directive.
- $user/$SAFE_DOMAIN are checked against [A-Za-z0-9._-]+ first, because
quoting does NOT stop php.ini's own ${VAR} interpolation. A rejected value
logs a WARNING, writes no mapping at all (not even the degraded
auto_prepend fallback, which would not be right for such a site either) and
reports `path parity = none (user/domain rejected)` on the startup line.
After: same container, same hostile domain — no 99-cac-path-parity.ini is
written, `precision => 14` (default), and the warning names the rejected values.
Happy path re-verified for domain=site.com and domain=*.site.com: mapping
written, `Rewriting => active`, from/to parse back byte-identical.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
06df1c410b |
fix(cac-lsphp): pin the shipped lsphp to the version the .so was built against
The comment claimed the extension was "built against THIS image's own lsphp so
the API/ABI always match". It was not, and could not be: `lsphp<NN>-dev` is
absent from the LiteSpeed apt repo at the version every prebuilt OLS base image
ships, because that repo carries only the current release. Measured on
OLS 1.8.4 (2026-08-05), base image vs repo candidate:
lsphp81 8.1.33-5+noble -> 8.1.34-1+noble
lsphp83 8.3.28-1+noble -> 8.3.32-1+noble
lsphp85 8.5.0-3+noble -> 8.5.8-1+noble
and the literal fix — `apt-get install lsphp<NN>-dev="$(dpkg-query lsphp<NN>)"` —
fails on all three with `E: Version '<base>' for 'lsphp<NN>-dev' was not found`
(apt exit 100). So installing -dev necessarily upgrades lsphp in the build stage;
the only satisfiable direction is to move the runtime to meet it.
The skew the reviewer measured (a .so built on 8.3.32 shipped beside an 8.3.30
runtime, lsphp83-common at 8.3.31) was not vendor randomness: BOTH stages resolve
"repo latest" independently and Docker caches them independently. `COPY ./ext`
sits at the top of the ext-build stage, so editing the extension invalidated that
stage's apt layer while stage 2's stayed cached — i.e. every extension edit
rebuilt the .so against fresh headers and shipped it next to a stale runtime.
Fixed by making them one system:
- the toolchain layer moves ABOVE the source COPY, so editing the extension no
longer re-resolves the PHP version;
- it records the resolved version to /build-out/lsphp.version and asserts
lsphp<NN> == lsphp<NN>-dev in that stage;
- stage 2 COPYs that file in BEFORE its apt layer (so the version is part of
that layer's cache key) and pins lsphp/-common/-ldap to it, then asserts the
installed versions match. Unsatisfiable pin => loud apt failure with the
remediation, never a silent fallback.
Verified: builds clean on PHP 8.1/8.3/8.5; the shipped php83 image now reports a
uniform lsphp83 family at 8.3.32 (the previous image shipped lsphp83 8.3.30 /
-common 8.3.31 / -ldap 8.3.30). Mutation-tested by recording a version the repo
no longer has: build fails at stage 2 rather than shipping the skew.
The `lsphp -i | grep` build assertion is kept but its comment now says what it
does and does not prove: it catches a .so that will not LOAD, never silent
struct-layout drift.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
a3aa9f2b26 |
fix(lsphp): repair the comment my sed mangled
My previous commit used sed with | as both delimiter and literal, which corrupted the comment line rather than changing -m to -i. The assertion command itself was never touched and is correct. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
83522b00ef |
Merge branch 'feat/lsphp-server-path-parity'
Guarantees $_SERVER path parity between cac-fpm and cac-lsphp via a PHP extension rewriting the filesystem-path keys from RINIT, configured by two PHP_INI_SYSTEM entries a customer's .user.ini cannot reach. The prepend it replaces was PHP_INI_PERDIR, so any site with its own auto_prepend_file displaced it -- 7 live shared_ols sites are in that state. Hardening the hook was self-defeating: PHP resolves ONE winning auto_prepend_file after the .user.ini chain, so you cannot chain from the losing side, and php_admin_value would make ours win by making the customer's Wordfence WAF never run. An extension occupies no userland hook at all. The symlink-farm alternative was rejected twice over: followSymLink in a shared multi-tenant OLS is a cross-tenant read risk, and /home/<user> is ambiguous in the single shared-ols container when one user has several sites. |
||
|
|
15e304e0c3 |
docs(lsphp): the build assertion probes with -i, not -m
The comment describing the assertion still said 'lsphp -m | grep' while the code correctly uses -i. That is the exact trap documented three lines below -- lsphp is the LSAPI SAPI and answers -m by printing usage and exiting 0, so an -m based check never matches and never fails. Leaving the comment wrong would invite someone to 'restore' it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
da16faaff5 |
feat(cac-lsphp): guarantee $_SERVER path parity via a PHP extension
A site moved from cac-fpm to cac-lsphp must see byte-identical $_SERVER['DOCUMENT_ROOT'] and ['SCRIPT_FILENAME'] (/home/<user>/...). The auto_prepend_file normaliser that did this was PHP_INI_PERDIR, so any site with its own .user.ini auto_prepend_file silently displaced it — the state 7 live shared_ols sites (Wordfence, cPanel imports) are actually in. Hardening the hook was not an option either: making our prepend win would have disabled those Wordfence WAFs. Replace it with cac_path_parity, a small PHP extension that rewrites the filesystem-path $_SERVER keys from RINIT. RINIT cannot be displaced by .user.ini, and it occupies no userland hook, so the customer's own auto_prepend_file stays the only prepend in play and keeps working. The mapping lives in two PHP_INI_SYSTEM settings, which .user.ini (PERDIR / USER only) and ini_set() cannot reach. Mechanism is a path-component-bounded string prefix swap, not realpath(): byte-identical to cac-fpm by construction (realpath would resolve a customer's own symlinked public_html to some third path), no syscall, and no failure path. Every guard fails open and leaves $_SERVER untouched; nothing here can warn, throw or 500 a site. Unconfigured it is fully inert, so cac-fpm and cac-litespeed are unaffected. Built in a separate Dockerfile stage keyed off the existing ARG PHPVER — gcc/phpize/headers never reach the shipped image (verified absent; the image grows ~155kB), and a base-image PHP bump recompiles with no human step. A `lsphp -i | grep` assertion fails the build if the .so does not load, so an image can never ship having silently lost parity. The entrypoint selects the extension when present and removes any stale prepend ini left by an older image; if the extension is somehow not loadable it falls back to the old normaliser and logs a WARNING rather than losing normalisation entirely. It also now logs the active parity mode, and warns when lsphp reports no ini scan dir (previously silent). Probe lsphp with `-i` only: it is the LSAPI SAPI, not the CLI, and answers `-m`/`-r` by printing usage and exiting 0 — a `lsphp -m | grep` check never matches and never errors, which is the exact class of silent always-false assertion this change exists to remove. Verified: 6 .phpt tests; tests/fpm-parity-check.sh proves under the FPM SAPI that with a customer .user.ini auto_prepend_file present both keys are still corrected AND the customer's prepend still runs, and that the old mechanism does not; and in a real built cac-lsphp:php83 container that SCRIPT_FILENAME is rewritten, the customer prepend still fires, and another tenant's path is left untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
03b8f3f730 |
Merge pull request 'fix(cac-lsphp): enable .user.ini support (LSPHP_ENABLE_USER_INI)' (#21) from fix/lsphp-enable-user-ini into trunk
Cloud Apache Container / Build-and-Push (84) (push) Successful in 2m25s
Cloud Apache Container / Build-FPM-Images (80) (push) Successful in 2m16s
Cloud Apache Container / Build-FPM-Images (81) (push) Successful in 2m57s
Cloud Apache Container / Build-FPM-Images (83) (push) Successful in 2m40s
Cloud Apache Container / Build-FPM-Images (85) (push) Successful in 2m21s
Cloud Apache Container / Build-LiteSpeed-Images (81) (push) Successful in 48s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 33s
Cloud Apache Container / Build-LSPHP-Images (81) (push) Successful in 55s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 30s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 32s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 1m22s
Cloud Apache Container / Build-and-Push (74) (push) Successful in 3m48s
Cloud Apache Container / Build-and-Push (80) (push) Successful in 2m32s
Cloud Apache Container / Build-and-Push (81) (push) Successful in 2m27s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 3m2s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 1m22s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 3m21s
Cloud Apache Container / Build-FPM-Images (74) (push) Successful in 1m36s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 2m43s
Cloud Apache Container / Build-FPM-Images (84) (push) Successful in 3m39s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 1m13s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 1m10s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 51s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 36s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 32s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 1m20s
|
||
|
|
433e74975c |
fix(cac-lsphp): enable .user.ini support (LSPHP_ENABLE_USER_INI)
php-lsapi compiles .user.ini support in but leaves it DISABLED by default.
sapi/litespeed/lsapi_main.c has `static int parse_user_ini = 0;` and only
sets it when the process environment contains LSPHP_ENABLE_USER_INI=on.
WHP never set it, so lsphp never entered the user-ini chain at all.
The failure was silent: phpinfo() still reports user_ini.filename=.user.ini
and user_ini.cache_ttl=300, because those are core INI defaults that are
simply inert under this SAPI. Every other WHP PHP tier (cac, cac-fpm,
cac-litespeed) honors .user.ini, so shared_ols was quietly inconsistent.
Impact found in production (whp01/whp02/sdbees/TrueSelfCA, 29 sites):
- Per-site memory_limit / max_input_vars overrides were ignored. A Divi
site's max_input_vars stayed at the 2000 default while its .user.ini
asked for 20000.
- Wordfence's auto_prepend_file WAF never loaded on ANY shared_ols site.
11 sites had the plugin installed and reporting "Extended Protection"
enabled while the prepend was never executed.
Verified on a live sidecar (shadowdao.com, whp01) before this commit by
injecting the env var via whp.container_types.startup_env and recreating:
before: auto_prepend_file=/scripts/cac-lsphp-normalize.php (WAF absent)
after: auto_prepend_file=/home/shadowdao/public_html/wordfence-waf.php
wordfence-waf.php present in get_included_files()
class_exists('wfWAF') === true
The platform normalize prepend still chains in behind Wordfence's bootstrap,
so DOCUMENT_ROOT canonicalisation is not lost.
Set in two places on purpose: the Dockerfile ENV makes the value visible in
`docker inspect` and survives an entrypoint override, and the entrypoint
re-exports it with the same default because the runuser fallback exec path
resets the environment. Still overridable per-container
(LSPHP_ENABLE_USER_INI=off) as an escape hatch for a site whose legacy
cPanel-generated .user.ini has not been remediated yet.
NOTE: enabling this activates every previously-inert .user.ini at once.
Audit the fleet for stale cPanel directives before rolling this image —
session.save_path values under /var/cpanel/ that do not exist in the
container, memory_limit above the cgroup cap, and upload_max_filesize
values below the platform default were all found and remediated first.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
cc72fda741 |
Merge pull request 'fix(shared-ols): never cache logged-in pages (disable tier private cache)' (#20) from fix/shared-ols-no-logged-in-cache into trunk
Cloud Apache Container / Build-and-Push (84) (push) Successful in 1m16s
Cloud Apache Container / Build-FPM-Images (80) (push) Successful in 1m18s
Cloud Apache Container / Build-FPM-Images (81) (push) Successful in 1m18s
Cloud Apache Container / Build-FPM-Images (83) (push) Successful in 2m11s
Cloud Apache Container / Build-FPM-Images (85) (push) Successful in 2m22s
Cloud Apache Container / Build-LiteSpeed-Images (81) (push) Successful in 34s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 46s
Cloud Apache Container / Build-LSPHP-Images (81) (push) Successful in 1m24s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 30s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 30s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 29s
Cloud Apache Container / Build-and-Push (74) (push) Successful in 1m22s
Cloud Apache Container / Build-and-Push (80) (push) Successful in 1m17s
Cloud Apache Container / Build-and-Push (81) (push) Successful in 1m17s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 1m17s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 1m19s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 1m25s
Cloud Apache Container / Build-FPM-Images (74) (push) Successful in 1m18s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 1m16s
Cloud Apache Container / Build-FPM-Images (84) (push) Successful in 1m57s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 1m12s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 36s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 1m20s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 30s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 27s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 27s
Reviewed-on: #20 |
||
|
|
a865a13940 |
fix(shared-ols): never cache logged-in pages (disable tier private cache)
OLS tier had enablePrivateCache=1 + checkPrivateCache=1 at module scope on the assumption that with no LiteSpeed Cache WP plugin nothing would be cached. In practice OLS privately cached logged-in / cookie-bearing responses regardless of the plugin, serving stale wp-admin pages for the full privateExpireInSeconds TTL (observed: a WordPress 'automated update failed' nag persisting after the cause was cleared). Disable private caching at the tier (enablePrivateCache 0 + checkPrivateCache 0) so logged-in pages are always served fresh. Public/anonymous caching is unchanged (enableCache 1 + checkPublicCache 1), still honored from the plugin's X-LiteSpeed-Cache-Control headers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
8dbfdf599a |
fix(shared-ols): useIpInProxyHeader 2->1 so real client IP reaches lsphp
Cloud Apache Container / Build-and-Push (84) (push) Successful in 1m20s
Cloud Apache Container / Build-FPM-Images (80) (push) Successful in 1m20s
Cloud Apache Container / Build-FPM-Images (81) (push) Successful in 1m18s
Cloud Apache Container / Build-FPM-Images (83) (push) Successful in 1m17s
Cloud Apache Container / Build-FPM-Images (85) (push) Successful in 1m19s
Cloud Apache Container / Build-LiteSpeed-Images (81) (push) Successful in 56s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 29s
Cloud Apache Container / Build-LSPHP-Images (81) (push) Successful in 27s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 26s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 26s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 28s
Cloud Apache Container / Build-and-Push (74) (push) Successful in 1m26s
Cloud Apache Container / Build-and-Push (80) (push) Successful in 1m24s
Cloud Apache Container / Build-and-Push (81) (push) Successful in 1m20s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 1m22s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 1m21s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 1m19s
Cloud Apache Container / Build-FPM-Images (74) (push) Successful in 1m17s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 1m16s
Cloud Apache Container / Build-FPM-Images (84) (push) Successful in 1m22s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 34s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 29s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 30s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 25s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 26s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 25s
Mode 2 ("trusted IP only") extracts the real client IP from X-Forwarded-For
ONLY when the connecting peer is in a TRUSTED access-control list — which this
tier never configured (accessControl is `allow ALL`, no trusted designation).
So OLS kept HAProxy's container IP (172.18.0.34) as REMOTE_ADDR for EVERY
request across ALL tenants. WP security plugins (Wordfence etc.) then saw all
traffic as one IP; blocking it locked every site — and the admin — out.
HAProxy already sends X-Forwarded-For and is the ONLY peer that connects to
this tier (client-net, no host-published ports), and it OVERWRITES XFF with
%[src] (set-header), so spoofing is impossible. Mode 1 (always trust XFF) is
correct and safe here — it matches the working standalone configs/litespeed
config which has always used 1.
Verified on whp01: lsphp now receives the forwarded client IP end-to-end
(REMOTE_ADDR=<real-ip>, was 172.18.0.34). Live-hotpatched whp01+whp02 pending
this image rebuild.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
2e85f458d3 |
Merge pull request 'feat: OLS tier images — cac-lsphp (detached lsphp) + shared-ols' (#19) from feature/cac-lsphp-image into trunk
Cloud Apache Container / Build-and-Push (84) (push) Successful in 2m16s
Cloud Apache Container / Build-FPM-Images (80) (push) Successful in 2m17s
Cloud Apache Container / Build-FPM-Images (81) (push) Successful in 2m18s
Cloud Apache Container / Build-FPM-Images (83) (push) Successful in 2m22s
Cloud Apache Container / Build-FPM-Images (85) (push) Successful in 2m13s
Cloud Apache Container / Build-LiteSpeed-Images (81) (push) Successful in 1m8s
Cloud Apache Container / Build-LiteSpeed-Images (83) (push) Successful in 30s
Cloud Apache Container / Build-LSPHP-Images (81) (push) Successful in 1m30s
Cloud Apache Container / Build-LSPHP-Images (83) (push) Successful in 29s
Cloud Apache Container / Build-LSPHP-Images (84) (push) Successful in 51s
Cloud Apache Container / Build-LSPHP-Images (85) (push) Successful in 59s
Cloud Apache Container / Build-and-Push (74) (push) Successful in 1m22s
Cloud Apache Container / Build-and-Push (80) (push) Successful in 2m15s
Cloud Apache Container / Build-and-Push (81) (push) Successful in 1m18s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 2m24s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 2m23s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 2m22s
Cloud Apache Container / Build-FPM-Images (74) (push) Successful in 2m18s
Cloud Apache Container / Build-FPM-Images (82) (push) Successful in 1m17s
Cloud Apache Container / Build-FPM-Images (84) (push) Successful in 2m15s
Cloud Apache Container / Build-LiteSpeed-Images (82) (push) Successful in 47s
Cloud Apache Container / Build-LiteSpeed-Images (84) (push) Successful in 31s
Cloud Apache Container / Build-LiteSpeed-Images (85) (push) Successful in 1m11s
Cloud Apache Container / Build-LSPHP-Images (82) (push) Successful in 35s
Cloud Apache Container / Build-Shared-httpd (push) Successful in 45s
Cloud Apache Container / Build-Shared-OLS (push) Successful in 1m3s
Reviewed-on: #19 |
||
|
|
08f35032c5 |
fix(shared-ols): re-review hardening — bounded flock + stale-tmp sweep
Follow-up to the review fixes, from a second review pass: - flock now uses -w 30 (bounded wait) so a hung render can't block the panel's docker-exec (and the site-save request) indefinitely; the dead-code timeout error path is now reachable. - sweep stale .httpd_config.conf.tmp.* left by a prior SIGKILL (trap EXIT doesn't run on SIGKILL); safe under flock since each render uses a unique $$ suffix. Verified: render still produces a valid config + serves; stale tmp is swept. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6bb494c72f |
fix(shared-ols): review fixes — watcher starvation, atomic render, O(N) chown, safe meta parse
Addresses the local code-review on the OLS-tier images: - [HIGH] ols-htaccess-watcher.sh: the debounce drain read ALL inotify events unfiltered, so on a busy multi-tenant server it never timed out and the restart was STARVED (rewrite changes silently never applied). Now coalesces with a hard DEBOUNCE-bounded window. Verified under continuous noise. - [HIGH] render-shared-ols-config.sh: built httpd_config.conf in-place across several appends, so a concurrent OLS restart (watcher) or parallel render could read a half-written config and 503 the whole tier. Now flock-serialized, built in a temp file and atomically moved into place; refuses to publish empty. - [MED] render + entrypoint: replaced recursive chown of the whole conf tree (O(N-sites) on every single-site change / boot) with a targeted chown of just the file written. - [MED] render: parse site.meta with sed instead of sourcing it (do not execute panel-written data as shell). - [cleanup] removed the unused configs/shared-ols/vhconf.tpl (the panel copy is the single source; the image never read it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7552760ba0 |
fix(cac-lsphp): normalize $_SERVER DOCUMENT_ROOT/SCRIPT_FILENAME to /home
The symlink makes __FILE__/__DIR__/realpath/getcwd report /home/<user>/public_html (WordPress/frameworks), but $_SERVER['DOCUMENT_ROOT']/['SCRIPT_FILENAME'] are raw env vars OLS sets to its /mnt/users view — apps that build/compare paths from them would see /mnt/users. Added a tiny auto_prepend (cac-lsphp-normalize.php, wired via a scan-dir ini) that realpath-canonicalises those two back to /home. Customer sites have no auto_prepend by default, so no conflict. Verified clean-room (committed image, fresh boot): DOCUMENT_ROOT and SCRIPT_FILENAME both report /home/<user>/public_html through the shared OLS. Now byte-for-byte 1:1 with cac-fpm/cac-litespeed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |