433e74975c
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>
70 lines
3.4 KiB
Docker
70 lines
3.4 KiB
Docker
## cac-lsphp — per-site DETACHED lsphp (LSAPI) backend for the shared-ols tier.
|
|
##
|
|
## The LiteSpeed analogue of cac-fpm: a slim, single-tenant PHP backend that
|
|
## runs `lsphp -b 0.0.0.0:9000` (detached LSAPI mode) and NOTHING ELSE — no
|
|
## webserver. The shared OpenLiteSpeed container (shared-ols) sits in front and
|
|
## reaches this over the docker network via an extProcessor of type lsapi,
|
|
## address <this-container>:9000 — structurally identical to how shared-httpd
|
|
## reaches a cac-fpm container's php-fpm on :9000.
|
|
##
|
|
## Built on the SAME LiteSpeed prebuilt base as cac-litespeed so the lsphp
|
|
## binary + extension set are byte-for-byte the runtime customers already get
|
|
## on the litespeed tier (memcached, redis, imagick, mbstring, mysqlnd, intl,
|
|
## gd, soap, bcmath, gmp, sodium, opcache, ... + lsphpNN-ldap added below).
|
|
## We do NOT strip the bundled OpenLiteSpeed binaries: the "no webserver"
|
|
## guarantee comes from the ENTRYPOINT (it only ever execs lsphp), and deleting
|
|
## OLS files from the upstream image risks breaking lsphp's shared libs for no
|
|
## real benefit. Only :9000 is EXPOSEd, and OLS is never started.
|
|
##
|
|
## See the design spec + PoC: whp docs/superpowers/plans/2026-06-09-ols-lsphp-tier.md
|
|
## and the LSAPI path-parity finding (feedback_ols_lsapi_no_script_filename_remap).
|
|
|
|
ARG OLS_VERSION=1.8.4
|
|
ARG PHPVER=83
|
|
FROM litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER}
|
|
ARG PHPVER=83
|
|
ENV PHPVER=${PHPVER}
|
|
|
|
## Match the cac-litespeed extension surface exactly: the only ext the prebuilt
|
|
## base lacks is lsphpNN-ldap. setpriv (util-linux) is already on the Ubuntu
|
|
## base; we add nothing else the sidecar doesn't need. All apt cache cleaned in
|
|
## the same layer to keep the image small.
|
|
RUN apt-get update && \
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
|
ca-certificates \
|
|
lsphp${PHPVER}-ldap && \
|
|
apt-get clean && \
|
|
rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
|
|
|
|
## Scripts + the SHARED production lsphp ini (reused verbatim from the litespeed
|
|
## image — same runtime, same tuning). Scripts layer last (they change most).
|
|
COPY ./scripts/entrypoint-lsphp.sh \
|
|
./scripts/detect-memory-lsphp.sh \
|
|
./scripts/healthcheck-lsphp.sh \
|
|
./scripts/cac-lsphp-normalize.php \
|
|
/scripts/
|
|
RUN chmod +x /scripts/entrypoint-lsphp.sh /scripts/detect-memory-lsphp.sh /scripts/healthcheck-lsphp.sh
|
|
|
|
## Apply production lsphp ini overrides into lsphp's scan dir (path varies by
|
|
## PHP minor version; ask lsphp directly — same idiom as Dockerfile.litespeed).
|
|
COPY ./configs/litespeed/lsphp-overrides.ini /etc/lsws-templates/lsphp-overrides.ini
|
|
RUN bash -c 'set -e; \
|
|
SCAN_DIR=$(/usr/local/lsws/lsphp${PHPVER}/bin/lsphp -i 2>/dev/null | awk -F"=> " "/^Scan this dir/ {print \$2; exit}"); \
|
|
mkdir -p "$SCAN_DIR"; \
|
|
cp /etc/lsws-templates/lsphp-overrides.ini "$SCAN_DIR/99-prod-overrides.ini"; \
|
|
echo "wrote overrides to $SCAN_DIR"'
|
|
|
|
## php-lsapi gates .user.ini parsing behind this env var (see entrypoint-lsphp.sh
|
|
## for the full explanation). Set here so the value is visible in `docker inspect`
|
|
## and survives an entrypoint override; the entrypoint re-exports it with the same
|
|
## default so the runuser exec path can't drop it.
|
|
ENV LSPHP_ENABLE_USER_INI=on
|
|
|
|
EXPOSE 9000
|
|
|
|
## TCP-connect + lsphp-alive check (LSAPI isn't FastCGI, so no cgi-fcgi ping).
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
|
CMD /scripts/healthcheck-lsphp.sh
|
|
|
|
ENTRYPOINT ["/scripts/entrypoint-lsphp.sh"]
|