Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af7fd193e5 | ||
|
|
e8e3bfd490 | ||
|
|
c49f456336 | ||
|
|
bcd737a9e2 | ||
|
|
955aec4573 | ||
|
|
998eb17f39 | ||
|
|
f461d22aae | ||
|
|
cfde82ae44 | ||
|
|
132e591503 | ||
|
|
642b97707d | ||
|
|
71ca114529 | ||
|
|
74e12407f8 |
No files matched your search
@@ -51,6 +51,7 @@ jobs:
|
||||
scripts/ols-htaccess-watcher.sh \
|
||||
scripts/create-vhost-litespeed.sh \
|
||||
scripts/install-lscache-wp.sh \
|
||||
scripts/lsphp-apt-upgrade.sh \
|
||||
scripts/tune-mpm.sh \
|
||||
scripts/tests/lsphp-info-probe.test.sh
|
||||
|
||||
@@ -60,6 +61,9 @@ jobs:
|
||||
Build-and-Push:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
# One PHP version failing (e.g. an EOL stream) must not cancel the
|
||||
# security builds of the others.
|
||||
fail-fast: false
|
||||
matrix:
|
||||
phpver: [74, 80, 81, 82, 83, 84, 85]
|
||||
steps:
|
||||
@@ -72,6 +76,28 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# Per-run cache-bust value. Each job's buildx builder is created fresh by
|
||||
# setup-buildx-action (no cache-from/cache-to here), so today nothing is
|
||||
# cached between runs — this keeps it that way if a cache is ever added:
|
||||
# the package-upgrade layers consume CACHE_BUST and can never be reused.
|
||||
- name: Cache-bust value
|
||||
id: bust
|
||||
run: echo "value=$(date -u +%Y%m%dT%H%M%SZ)-${{ gitea.run_id }}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# The minimum PHP version this build must ship: php.net's current release for
|
||||
# the branch (over HTTPS). The Dockerfile fails if Remi gave us anything older,
|
||||
# so lagging mirrors or replayed repo metadata can't ship a vulnerable PHP.
|
||||
- name: Resolve current PHP release from php.net
|
||||
id: phpmin
|
||||
run: |
|
||||
set -euo pipefail
|
||||
v="${{ matrix.phpver }}"; branch="${v:0:1}.${v:1}"
|
||||
json=$(curl -fsS --retry 5 --max-time 60 "https://www.php.net/releases/index.php?json&max=1&version=${branch}")
|
||||
min=$(printf '%s' "$json" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(next(iter(d)) if isinstance(d,dict) and d else "")')
|
||||
if [ -z "$min" ]; then echo "::error::no php.net release found for ${branch}"; exit 1; fi
|
||||
echo "php.net current ${branch}: ${min}"
|
||||
echo "min=${min}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Login to Gitea
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -86,6 +112,8 @@ jobs:
|
||||
push: true
|
||||
build-args: |
|
||||
PHPVER=${{ matrix.phpver }}
|
||||
CACHE_BUST=${{ steps.bust.outputs.value }}
|
||||
EXPECT_PHP_MIN=${{ steps.phpmin.outputs.min }}
|
||||
tags: |
|
||||
repo.anhonesthost.net/cloud-hosting-platform/cac:php${{ matrix.phpver }}
|
||||
${{ matrix.phpver == '85' && 'repo.anhonesthost.net/cloud-hosting-platform/cac:latest' || '' }}
|
||||
@@ -93,6 +121,9 @@ jobs:
|
||||
Build-FPM-Images:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
# One PHP version failing (e.g. an EOL stream) must not cancel the
|
||||
# security builds of the others.
|
||||
fail-fast: false
|
||||
matrix:
|
||||
phpver: [74, 80, 81, 82, 83, 84, 85]
|
||||
steps:
|
||||
@@ -105,6 +136,28 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# Per-run cache-bust value. Each job's buildx builder is created fresh by
|
||||
# setup-buildx-action (no cache-from/cache-to here), so today nothing is
|
||||
# cached between runs — this keeps it that way if a cache is ever added:
|
||||
# the package-upgrade layers consume CACHE_BUST and can never be reused.
|
||||
- name: Cache-bust value
|
||||
id: bust
|
||||
run: echo "value=$(date -u +%Y%m%dT%H%M%SZ)-${{ gitea.run_id }}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# The minimum PHP version this build must ship: php.net's current release for
|
||||
# the branch (over HTTPS). The Dockerfile fails if Remi gave us anything older,
|
||||
# so lagging mirrors or replayed repo metadata can't ship a vulnerable PHP.
|
||||
- name: Resolve current PHP release from php.net
|
||||
id: phpmin
|
||||
run: |
|
||||
set -euo pipefail
|
||||
v="${{ matrix.phpver }}"; branch="${v:0:1}.${v:1}"
|
||||
json=$(curl -fsS --retry 5 --max-time 60 "https://www.php.net/releases/index.php?json&max=1&version=${branch}")
|
||||
min=$(printf '%s' "$json" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(next(iter(d)) if isinstance(d,dict) and d else "")')
|
||||
if [ -z "$min" ]; then echo "::error::no php.net release found for ${branch}"; exit 1; fi
|
||||
echo "php.net current ${branch}: ${min}"
|
||||
echo "min=${min}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Login to Gitea
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -120,6 +173,8 @@ jobs:
|
||||
push: true
|
||||
build-args: |
|
||||
PHPVER=${{ matrix.phpver }}
|
||||
CACHE_BUST=${{ steps.bust.outputs.value }}
|
||||
EXPECT_PHP_MIN=${{ steps.phpmin.outputs.min }}
|
||||
tags: |
|
||||
repo.anhonesthost.net/cloud-hosting-platform/cac-fpm:php${{ matrix.phpver }}
|
||||
${{ matrix.phpver == '85' && 'repo.anhonesthost.net/cloud-hosting-platform/cac-fpm:latest' || '' }}
|
||||
@@ -127,6 +182,9 @@ jobs:
|
||||
Build-LiteSpeed-Images:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
# One PHP version failing (e.g. an EOL stream) must not cancel the
|
||||
# security builds of the others.
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# PHP 7.4/8.0 deliberately excluded — the LiteSpeed prebuilt base
|
||||
# images stop at older OLS releases for those PHP versions, and the
|
||||
@@ -143,6 +201,29 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# Per-run cache-bust value. Each job's buildx builder is created fresh by
|
||||
# setup-buildx-action (no cache-from/cache-to here), so today nothing is
|
||||
# cached between runs — this keeps it that way if a cache is ever added:
|
||||
# the package-upgrade layers consume CACHE_BUST and can never be reused.
|
||||
- name: Cache-bust value
|
||||
id: bust
|
||||
run: echo "value=$(date -u +%Y%m%dT%H%M%SZ)-${{ gitea.run_id }}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Ask the LiteSpeed apt repo (the same one the base image's apt uses) for the
|
||||
# CURRENT lsphp version. The Dockerfile asserts the installed package equals
|
||||
# this (build fails if the repo changed mid-build — just re-run) and writes
|
||||
# it into the image labels org.whp.php-version / org.whp.lsphp-package-version.
|
||||
- name: Resolve current lsphp${{ matrix.phpver }} from the LiteSpeed repo
|
||||
id: lsphp
|
||||
run: |
|
||||
set -euo pipefail
|
||||
idx=$(curl -fsS --retry 3 --max-time 60 http://rpms.litespeedtech.com/debian/dists/noble/main/binary-amd64/Packages)
|
||||
pkg=$(printf '%s\n' "$idx" | awk -v p="lsphp${{ matrix.phpver }}" '/^Package: /{n=$2} /^Version: / && n==p {print $2}' | sort -V | tail -n1)
|
||||
if [ -z "$pkg" ]; then echo "::error::lsphp${{ matrix.phpver }} not found in the LiteSpeed repo index"; exit 1; fi
|
||||
echo "lsphp${{ matrix.phpver }} in repo: $pkg (PHP ${pkg%%-*})"
|
||||
echo "pkg=$pkg" >> "$GITHUB_OUTPUT"
|
||||
echo "php=${pkg%%-*}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Login to Gitea
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -159,6 +240,9 @@ jobs:
|
||||
build-args: |
|
||||
PHPVER=${{ matrix.phpver }}
|
||||
OLS_VERSION=1.8.4
|
||||
CACHE_BUST=${{ steps.bust.outputs.value }}
|
||||
EXPECT_LSPHP_PKG=${{ steps.lsphp.outputs.pkg }}
|
||||
EXPECT_PHP_VERSION=${{ steps.lsphp.outputs.php }}
|
||||
# OLS_VERSION pinned to 1.8.4 — only release with prebuilt images
|
||||
# for every PHP version we ship (1.8.5 and 1.9.0 don't have an
|
||||
# lsphp81 variant on Docker Hub). Bump alongside a local rebuild
|
||||
@@ -171,6 +255,9 @@ jobs:
|
||||
Build-LSPHP-Images:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
# One PHP version failing (e.g. an EOL stream) must not cancel the
|
||||
# security builds of the others.
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Same PHP matrix as cac-litespeed (81–85): cac-lsphp is the detached
|
||||
# backend for the shared-ols tier and shares the litespeed prebuilt
|
||||
@@ -187,6 +274,29 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# Per-run cache-bust value. Each job's buildx builder is created fresh by
|
||||
# setup-buildx-action (no cache-from/cache-to here), so today nothing is
|
||||
# cached between runs — this keeps it that way if a cache is ever added:
|
||||
# the package-upgrade layers consume CACHE_BUST and can never be reused.
|
||||
- name: Cache-bust value
|
||||
id: bust
|
||||
run: echo "value=$(date -u +%Y%m%dT%H%M%SZ)-${{ gitea.run_id }}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Ask the LiteSpeed apt repo (the same one the base image's apt uses) for the
|
||||
# CURRENT lsphp version. The Dockerfile asserts the installed package equals
|
||||
# this (build fails if the repo changed mid-build — just re-run) and writes
|
||||
# it into the image labels org.whp.php-version / org.whp.lsphp-package-version.
|
||||
- name: Resolve current lsphp${{ matrix.phpver }} from the LiteSpeed repo
|
||||
id: lsphp
|
||||
run: |
|
||||
set -euo pipefail
|
||||
idx=$(curl -fsS --retry 3 --max-time 60 http://rpms.litespeedtech.com/debian/dists/noble/main/binary-amd64/Packages)
|
||||
pkg=$(printf '%s\n' "$idx" | awk -v p="lsphp${{ matrix.phpver }}" '/^Package: /{n=$2} /^Version: / && n==p {print $2}' | sort -V | tail -n1)
|
||||
if [ -z "$pkg" ]; then echo "::error::lsphp${{ matrix.phpver }} not found in the LiteSpeed repo index"; exit 1; fi
|
||||
echo "lsphp${{ matrix.phpver }} in repo: $pkg (PHP ${pkg%%-*})"
|
||||
echo "pkg=$pkg" >> "$GITHUB_OUTPUT"
|
||||
echo "php=${pkg%%-*}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Login to Gitea
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -203,6 +313,9 @@ jobs:
|
||||
build-args: |
|
||||
PHPVER=${{ matrix.phpver }}
|
||||
OLS_VERSION=1.8.4
|
||||
CACHE_BUST=${{ steps.bust.outputs.value }}
|
||||
EXPECT_LSPHP_PKG=${{ steps.lsphp.outputs.pkg }}
|
||||
EXPECT_PHP_VERSION=${{ steps.lsphp.outputs.php }}
|
||||
# OLS_VERSION pinned to 1.8.4 to match Build-LiteSpeed-Images — same
|
||||
# prebuilt base, same lsphp binaries. Bump both together.
|
||||
tags: |
|
||||
|
||||
+24
@@ -1,10 +1,24 @@
|
||||
FROM almalinux/10-base
|
||||
ARG PHPVER=83
|
||||
# Per-CI-run value: the first RUN below (dnf update) and everything after it can
|
||||
# never be a layer-cache hit, so every build gets the repos' current packages.
|
||||
# Local builds: --build-arg CACHE_BUST=$(date +%s) or --no-cache.
|
||||
ARG CACHE_BUST=
|
||||
# php.net's current release for this branch (CI); the build fails on older PHP.
|
||||
ARG EXPECT_PHP_MIN=
|
||||
|
||||
# Install repos, update, install only needed packages, clean up in one layer
|
||||
RUN dnf install -y \
|
||||
https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm \
|
||||
https://rpms.remirepo.net/enterprise/remi-release-10.rpm && \
|
||||
# Use Remi's origin, not the mirrorlist: during the 2026-10-21 PHP security
|
||||
# release the mirrors lagged hours behind (still 8.2.34 when 8.2.35 was out),
|
||||
# so builds shipped unpatched PHP. HTTPS first; plain http only as a fallback
|
||||
# because the origin's HTTPS dropped/timed out under that day's load. A
|
||||
# replayed (old) http index can't ship old PHP: packages stay GPG-checked and
|
||||
# the EXPECT_PHP_MIN gate below fails the build on anything older than php.net.
|
||||
sed -i -e 's|^#baseurl=http://\(.*\)$|baseurl=https://\1 http://\1|' -e 's/^mirrorlist=/#mirrorlist=/' /etc/yum.repos.d/remi*.repo && \
|
||||
printf 'retries=10\ntimeout=60\n' >> /etc/dnf/dnf.conf && \
|
||||
dnf update -y && \
|
||||
dnf install -y httpd mod_ssl openssl wget procps cronie iproute postgresql-devel microdnf less git \
|
||||
nano rsync unzip zip mariadb bind-utils jq patch nc tree dos2unix && \
|
||||
@@ -23,6 +37,12 @@ RUN dnf install -y ImageMagick ImageMagick-libs ImageMagick-heic && \
|
||||
RUN openssl req -newkey rsa:2048 -nodes -keyout /etc/pki/tls/private/localhost.key -x509 -days 3650 -subj "/CN=localhost" -out /etc/pki/tls/certs/localhost.crt && \
|
||||
mkdir -p /run/php-fpm/ && \
|
||||
/scripts/install-php$PHPVER.sh && \
|
||||
# CI passes php.net's current release for the branch; never ship older PHP.
|
||||
if [ -n "$EXPECT_PHP_MIN" ]; then \
|
||||
php -r 'exit(version_compare(PHP_VERSION, getenv("EXPECT_PHP_MIN"), ">=") ? 0 : 1);' || \
|
||||
{ echo "FATAL: built PHP $(php -r 'echo PHP_VERSION;') is older than php.net $EXPECT_PHP_MIN"; exit 1; }; \
|
||||
else echo "WARNING: no EXPECT_PHP_MIN (local build) - PHP version not gated"; fi && \
|
||||
echo "PHP version report:" && php -v && rpm -q php-cli php-common php-fpm && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
# Download and install wp-cli (consider pinning version for reproducibility)
|
||||
@@ -36,6 +56,10 @@ RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local
|
||||
# Copy configs and web files
|
||||
COPY ./configs/default-index.conf /etc/httpd/conf.d/
|
||||
COPY ./configs/prod-php.ini /etc/php.ini
|
||||
# php2shell mitigation (CVE-2026-109618/-109619). Named zz- so it sorts after
|
||||
# every other scan-dir ini (incl. runtime-written 99-*) and wins. Remove once
|
||||
# all images ship PHP >= 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12.
|
||||
COPY ./configs/zz-php2shell-mitigation.ini /etc/php.d/zz-php2shell-mitigation.ini
|
||||
COPY ./configs/phpinfo.php /var/www/html/
|
||||
COPY ./configs/mariadb.repo /etc/yum.repos.d/
|
||||
COPY ./configs/index.php /var/www/html/
|
||||
|
||||
@@ -1,10 +1,24 @@
|
||||
FROM almalinux/10-base
|
||||
ARG PHPVER=83
|
||||
# Per-CI-run value: the first RUN below (dnf update) and everything after it can
|
||||
# never be a layer-cache hit, so every build gets the repos' current packages.
|
||||
# Local builds: --build-arg CACHE_BUST=$(date +%s) or --no-cache.
|
||||
ARG CACHE_BUST=
|
||||
# php.net's current release for this branch (CI); the build fails on older PHP.
|
||||
ARG EXPECT_PHP_MIN=
|
||||
|
||||
# Install repos, update, install only needed packages (no httpd/mod_ssl), clean up in one layer
|
||||
RUN dnf install -y \
|
||||
https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm \
|
||||
https://rpms.remirepo.net/enterprise/remi-release-10.rpm && \
|
||||
# Use Remi's origin, not the mirrorlist: during the 2026-10-21 PHP security
|
||||
# release the mirrors lagged hours behind (still 8.2.34 when 8.2.35 was out),
|
||||
# so builds shipped unpatched PHP. HTTPS first; plain http only as a fallback
|
||||
# because the origin's HTTPS dropped/timed out under that day's load. A
|
||||
# replayed (old) http index can't ship old PHP: packages stay GPG-checked and
|
||||
# the EXPECT_PHP_MIN gate below fails the build on anything older than php.net.
|
||||
sed -i -e 's|^#baseurl=http://\(.*\)$|baseurl=https://\1 http://\1|' -e 's/^mirrorlist=/#mirrorlist=/' /etc/yum.repos.d/remi*.repo && \
|
||||
printf 'retries=10\ntimeout=60\n' >> /etc/dnf/dnf.conf && \
|
||||
dnf update -y && \
|
||||
dnf install -y openssl wget procps cronie iproute postgresql-devel microdnf less git \
|
||||
nano rsync unzip zip mariadb bind-utils jq patch nc tree dos2unix fcgi && \
|
||||
@@ -22,6 +36,12 @@ RUN dnf install -y ImageMagick ImageMagick-libs ImageMagick-heic && \
|
||||
# Create needed dirs, install PHP, clean up (no SSL cert, no httpd)
|
||||
RUN mkdir -p /run/php-fpm/ && \
|
||||
/scripts/install-php$PHPVER.sh && \
|
||||
# CI passes php.net's current release for the branch; never ship older PHP.
|
||||
if [ -n "$EXPECT_PHP_MIN" ]; then \
|
||||
php -r 'exit(version_compare(PHP_VERSION, getenv("EXPECT_PHP_MIN"), ">=") ? 0 : 1);' || \
|
||||
{ echo "FATAL: built PHP $(php -r 'echo PHP_VERSION;') is older than php.net $EXPECT_PHP_MIN"; exit 1; }; \
|
||||
else echo "WARNING: no EXPECT_PHP_MIN (local build) - PHP version not gated"; fi && \
|
||||
echo "PHP version report:" && php -v && rpm -q php-cli php-common php-fpm && \
|
||||
rm -rf /tmp/*
|
||||
|
||||
# Download and install wp-cli
|
||||
@@ -34,6 +54,10 @@ RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local
|
||||
|
||||
# Copy configs (PHP only, no Apache configs)
|
||||
COPY ./configs/prod-php.ini /etc/php.ini
|
||||
# php2shell mitigation (CVE-2026-109618/-109619). Named zz- so it sorts after
|
||||
# every other scan-dir ini (incl. runtime-written 99-*) and wins. Remove once
|
||||
# all images ship PHP >= 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12.
|
||||
COPY ./configs/zz-php2shell-mitigation.ini /etc/php.d/zz-php2shell-mitigation.ini
|
||||
COPY ./configs/mariadb.repo /etc/yum.repos.d/
|
||||
|
||||
# Set up cron job for log rotation
|
||||
|
||||
+30
-2
@@ -45,8 +45,23 @@ ENV PHPVER=${PHPVER}
|
||||
## lsphp${PHPVER}-ldap is the only extra ext we add (everything else WP needs
|
||||
## ships in the prebuilt base). lsphp84 + lsphp85 don't ship imap or pspell
|
||||
## from LiteSpeed — customers needing imap should pin to 8.3 or earlier.
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
##
|
||||
## SECURITY UPDATES AT BUILD TIME: lsphp-apt-upgrade.sh also upgrades the OS and
|
||||
## sweeps EVERY installed lsphp${PHPVER}* package to the LiteSpeed repo's current
|
||||
## release, failing the build if the repo is unreachable — so a rebuild ships a
|
||||
## PHP security fix as soon as LiteSpeed publishes the .deb, without waiting for
|
||||
## a new litespeedtech/openlitespeed base tag. See the script header.
|
||||
##
|
||||
## CACHE_BUST: CI passes a per-run value so this layer can never be a cache hit
|
||||
## (an ARG's first use in a RUN is part of that layer's cache key). Local builds:
|
||||
## pass --build-arg CACHE_BUST=$(date +%s) or use --no-cache, or you may get the
|
||||
## lsphp from your last build. EXPECT_* are resolved by CI from the repo index
|
||||
## and asserted here; the same values become the image labels below.
|
||||
ARG CACHE_BUST=
|
||||
ARG EXPECT_LSPHP_PKG=
|
||||
ARG EXPECT_PHP_VERSION=
|
||||
COPY ./scripts/lsphp-apt-upgrade.sh /usr/local/sbin/lsphp-apt-upgrade.sh
|
||||
RUN /usr/local/sbin/lsphp-apt-upgrade.sh "${PHPVER}" \
|
||||
gettext-base sudo cron \
|
||||
ca-certificates curl wget \
|
||||
lsphp${PHPVER}-ldap && \
|
||||
@@ -68,6 +83,10 @@ COPY ./scripts/entrypoint-litespeed.sh \
|
||||
/scripts/
|
||||
RUN chmod +x /scripts/*
|
||||
COPY ./configs/litespeed/ /etc/lsws-templates/
|
||||
## php2shell mitigation (CVE-2026-109618/-109619). Named zz- so it sorts after
|
||||
## every other scan-dir ini (incl. runtime-written 99-*) and wins. Remove once
|
||||
## all images ship PHP >= 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12.
|
||||
COPY ./configs/zz-php2shell-mitigation.ini /etc/lsws-templates/zz-php2shell-mitigation.ini
|
||||
|
||||
## Apply our production lsphp ini overrides. Ask lsphp for its scan dir
|
||||
## directly (varies by PHP minor version: 8.3/8.4/8.5 each have their own
|
||||
@@ -77,6 +96,7 @@ 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"; \
|
||||
cp /etc/lsws-templates/zz-php2shell-mitigation.ini "$SCAN_DIR/zz-php2shell-mitigation.ini"; \
|
||||
echo "wrote overrides to $SCAN_DIR"'
|
||||
|
||||
## Disable the OLS WebAdmin port for customer-facing containers. Bind admin
|
||||
@@ -87,6 +107,14 @@ RUN sed -i 's|^[[:space:]]*address[[:space:]]\+\*:| address 127.0
|
||||
## Cron entry for log rotation (mirrors cac:phpXX).
|
||||
RUN echo "15 */12 * * * root /scripts/log-rotate.sh" >> /etc/crontab
|
||||
|
||||
## Inspect without running the image:
|
||||
## docker buildx imagetools inspect <ref> --format '{{json .Image.Config.Labels}}'
|
||||
## Values come from CI's repo-index lookup and were asserted against the
|
||||
## installed packages by lsphp-apt-upgrade.sh above ("unverified" = local build).
|
||||
LABEL org.whp.php-version="${EXPECT_PHP_VERSION:-unverified}" \
|
||||
org.whp.lsphp-package-version="${EXPECT_LSPHP_PKG:-unverified}" \
|
||||
org.whp.php-flavour="lsphp${PHPVER}"
|
||||
|
||||
EXPOSE 80 443
|
||||
|
||||
## Healthcheck: the entrypoint drops a static /healthz into the customer
|
||||
|
||||
+44
-8
@@ -60,9 +60,20 @@ ARG PHPVER=83
|
||||
## Toolchain layer, deliberately BEFORE the source COPY so editing the extension
|
||||
## does not re-resolve the PHP version (which is what caused the skew above).
|
||||
## Records the exact lsphp version the headers belong to; stage 2 pins to it.
|
||||
##
|
||||
## lsphp-apt-upgrade.sh (see its header) refuses to run on stale/unreachable
|
||||
## repo lists and sweeps every lsphp${PHPVER}* package to the repo's current
|
||||
## release. CACHE_BUST is per-CI-run so this layer — and therefore the recorded
|
||||
## version stage 2 pins to — is never a cache hit; it is re-declared in stage 2.
|
||||
## --no-os-upgrade: this stage is thrown away, OS updates would only cost time.
|
||||
## EXPECT_* (CI's repo-index lookup) are asserted here too, so a mismatch fails
|
||||
## before the compile rather than after it.
|
||||
ARG CACHE_BUST=
|
||||
ARG EXPECT_LSPHP_PKG=
|
||||
ARG EXPECT_PHP_VERSION=
|
||||
COPY ./scripts/lsphp-apt-upgrade.sh /usr/local/sbin/lsphp-apt-upgrade.sh
|
||||
RUN set -e; \
|
||||
apt-get update; \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
/usr/local/sbin/lsphp-apt-upgrade.sh --no-os-upgrade "${PHPVER}" \
|
||||
build-essential autoconf pkg-config \
|
||||
lsphp${PHPVER}-dev; \
|
||||
RTV=$(dpkg-query -W -f='${Version}' lsphp${PHPVER}); \
|
||||
@@ -141,17 +152,29 @@ ENV PHPVER=${PHPVER}
|
||||
## cache hit, new version => this layer re-runs and lands on the same one. The
|
||||
## explicit `=$V` pins then make a mid-build repo roll a LOUD apt failure instead
|
||||
## of a silent skew. Verified satisfiable on PHP 8.1/8.3/8.5 (2026-08-05).
|
||||
##
|
||||
## SECURITY UPDATES AT BUILD TIME: lsphp-apt-upgrade.sh upgrades the Ubuntu OS
|
||||
## packages and every other lsphp${PHPVER}* package (extensions such as -msgpack
|
||||
## are NOT dragged along by the -common pin) to the repos' current versions, and
|
||||
## fails the build if the repo is unreachable. CACHE_BUST makes this layer a
|
||||
## guaranteed cache miss per CI run; EXPECT_* are CI's repo-index lookup,
|
||||
## asserted by the script and written into the image labels below.
|
||||
ARG CACHE_BUST=
|
||||
ARG EXPECT_LSPHP_PKG=
|
||||
ARG EXPECT_PHP_VERSION=
|
||||
COPY --from=ext-build /build-out/lsphp.version /etc/cac-lsphp-build.version
|
||||
COPY ./scripts/lsphp-apt-upgrade.sh /usr/local/sbin/lsphp-apt-upgrade.sh
|
||||
RUN set -e; \
|
||||
V=$(cat /etc/cac-lsphp-build.version); \
|
||||
apt-get update; \
|
||||
if ! DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
if ! /usr/local/sbin/lsphp-apt-upgrade.sh "${PHPVER}" \
|
||||
ca-certificates \
|
||||
lsphp${PHPVER}="$V" lsphp${PHPVER}-common="$V" lsphp${PHPVER}-ldap="$V"; then \
|
||||
echo "FATAL: lsphp${PHPVER} $V is what cac_path_parity was compiled against," >&2; \
|
||||
echo " but the LiteSpeed repo no longer offers it (it keeps only the" >&2; \
|
||||
echo " current release). The ext-build stage is almost certainly a stale" >&2; \
|
||||
echo " cache hit — rebuild with --no-cache." >&2; \
|
||||
echo "FATAL: lsphp-apt-upgrade.sh failed (reason above). If apt said" >&2; \
|
||||
echo " \"Version '$V' ... was not found\" or refused a downgrade: $V is what" >&2; \
|
||||
echo " cac_path_parity was compiled against, but the LiteSpeed repo (which" >&2; \
|
||||
echo " keeps only the current release) now offers a newer one — either it" >&2; \
|
||||
echo " published mid-build or ext-build was a stale cache hit. Re-run the" >&2; \
|
||||
echo " build (CI) / rebuild with --no-cache (local)." >&2; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
apt-get clean; \
|
||||
@@ -178,10 +201,15 @@ RUN chmod +x /scripts/entrypoint-lsphp.sh /scripts/detect-memory-lsphp.sh /scrip
|
||||
## 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
|
||||
## php2shell mitigation (CVE-2026-109618/-109619). Named zz- so it sorts after
|
||||
## every other scan-dir ini (incl. runtime-written 99-*) and wins. Remove once
|
||||
## all images ship PHP >= 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12.
|
||||
COPY ./configs/zz-php2shell-mitigation.ini /etc/lsws-templates/zz-php2shell-mitigation.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"; \
|
||||
cp /etc/lsws-templates/zz-php2shell-mitigation.ini "$SCAN_DIR/zz-php2shell-mitigation.ini"; \
|
||||
echo "wrote overrides to $SCAN_DIR"'
|
||||
|
||||
## Install the cac_path_parity extension into lsphp's own extension_dir and load
|
||||
@@ -218,6 +246,14 @@ RUN bash -c 'set -e; \
|
||||
## default so the runuser exec path can't drop it.
|
||||
ENV LSPHP_ENABLE_USER_INI=on
|
||||
|
||||
## Inspect without running the image:
|
||||
## docker buildx imagetools inspect <ref> --format '{{json .Image.Config.Labels}}'
|
||||
## Values come from CI's repo-index lookup and were asserted against the
|
||||
## installed packages by lsphp-apt-upgrade.sh above ("unverified" = local build).
|
||||
LABEL org.whp.php-version="${EXPECT_PHP_VERSION:-unverified}" \
|
||||
org.whp.lsphp-package-version="${EXPECT_LSPHP_PKG:-unverified}" \
|
||||
org.whp.php-flavour="lsphp${PHPVER}"
|
||||
|
||||
EXPOSE 9000
|
||||
|
||||
## TCP-connect + lsphp-alive check (LSAPI isn't FastCGI, so no cgi-fcgi ping).
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
; php2shell mitigation (CVE-2026-109618 / CVE-2026-109619, GHSA-w8hw-w9fx-j79w, GHSA-6wmf-4rv5-p3rp)
|
||||
; Remove once all images ship PHP >= 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12.
|
||||
fatal_error_backtraces = 0
|
||||
session.upload_progress.enabled = 0
|
||||
max_input_nesting_level = 16
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
#!/bin/bash
|
||||
## lsphp-apt-upgrade.sh — BUILD-TIME ONLY. Brings every installed lsphp${PHPVER}*
|
||||
## package (and, by default, the Ubuntu OS packages) up to what the repos offer
|
||||
## RIGHT NOW, then proves it.
|
||||
##
|
||||
## Why this exists: cac-litespeed / cac-lsphp start FROM the LiteSpeed prebuilt
|
||||
## image litespeedtech/openlitespeed:<OLS>-lsphpNN, which LiteSpeed republishes
|
||||
## rarely (the 1.8.4 tags were built 2025-12-16 and ship lsphp85 8.5.0 /
|
||||
## lsphp84 8.4.15, measured 2026-10-21, while rpms.litespeedtech.com/debian
|
||||
## offers 8.5.11 / 8.4.26). The base image already carries that apt repo, so a
|
||||
## PHP security release reaches us the moment LiteSpeed publishes the .deb —
|
||||
## provided every build actually asks for it. Before this script the lsphp core
|
||||
## only moved forward as a SIDE EFFECT of `apt-get install lsphpNN-ldap` (whose
|
||||
## exact-version dependency on lsphpNN-common drags the family along); lsphp
|
||||
## extension packages without that coupling (e.g. lsphpNN-msgpack) and every
|
||||
## Ubuntu package stayed at the base image's December versions.
|
||||
##
|
||||
## Fails LOUDLY — never ships the base image's PHP by accident:
|
||||
## * `apt-get update --error-on=any`: plain `apt-get update` only WARNS when a
|
||||
## repo is unreachable and carries on with no/old lists, exit 0.
|
||||
## * the LiteSpeed repo must be the candidate source for lsphp${PHPVER}.
|
||||
## * after upgrading, no lsphp${PHPVER}* package may remain upgradable.
|
||||
## * if CI passed the version it resolved from the repo index (EXPECT_LSPHP_PKG,
|
||||
## EXPECT_PHP_VERSION), the installed package / running PHP must match it;
|
||||
## that same value becomes the image LABEL, so the label cannot lie.
|
||||
##
|
||||
## Usage: lsphp-apt-upgrade.sh [--no-os-upgrade] PHPVER [extra apt packages / pkg=ver pins...]
|
||||
## Extra packages are installed in the same transaction set (after the OS
|
||||
## upgrade, before the lsphp sweep). Does NOT clean apt lists — the caller
|
||||
## decides (multi-stage builders want them; shipped layers delete them).
|
||||
set -euo pipefail
|
||||
|
||||
OS_UPGRADE=1
|
||||
if [ "${1:-}" = "--no-os-upgrade" ]; then OS_UPGRADE=0; shift; fi
|
||||
PHPVER="${1:?usage: lsphp-apt-upgrade.sh [--no-os-upgrade] PHPVER [pkgs...]}"
|
||||
shift
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
APT=(apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold)
|
||||
LSPHP="/usr/local/lsws/lsphp${PHPVER}/bin/lsphp"
|
||||
PHPCLI="/usr/local/lsws/lsphp${PHPVER}/bin/php"
|
||||
|
||||
die() { echo "FATAL(lsphp-apt-upgrade): $*" >&2; exit 1; }
|
||||
|
||||
echo "lsphp-apt-upgrade: PHPVER=${PHPVER} os_upgrade=${OS_UPGRADE} cache_bust=${CACHE_BUST:-<none>}"
|
||||
echo "lsphp-apt-upgrade: base image lsphp${PHPVER}=$(dpkg-query -W -f='${Version}' "lsphp${PHPVER}" 2>/dev/null || echo none)"
|
||||
|
||||
"${APT[@]}" update --error-on=any || die "apt-get update failed (repo unreachable?). Refusing to build with stale package lists."
|
||||
|
||||
# The candidate for lsphpNN must come from the LiteSpeed repo — not "none", and
|
||||
# not merely whatever is already installed.
|
||||
# Read each output into a variable and match in the shell: piping into an
|
||||
# early-exit reader (grep -q, awk ... exit) under pipefail can SIGPIPE the writer
|
||||
# and report "missing" for a package that is there (scripts/tests/lsphp-info-probe.test.sh).
|
||||
MADISON=$(apt-cache madison "lsphp${PHPVER}")
|
||||
if [[ "$MADISON" != *rpms.litespeedtech.com* ]]; then
|
||||
apt-cache policy "lsphp${PHPVER}" >&2 || true
|
||||
die "the LiteSpeed apt repo does not offer lsphp${PHPVER} (repo missing from sources, or its index is empty)."
|
||||
fi
|
||||
POLICY=$(apt-cache policy "lsphp${PHPVER}")
|
||||
CAND=$(awk '/Candidate:/ {print $2; exit}' <<<"$POLICY")
|
||||
echo "lsphp-apt-upgrade: repo candidate lsphp${PHPVER}=${CAND}"
|
||||
|
||||
if [ "$OS_UPGRADE" = 1 ]; then
|
||||
# --with-new-pkgs: let an upgrade pull in a new dependency instead of being
|
||||
# silently "kept back". Never removes packages (that would be dist-upgrade).
|
||||
"${APT[@]}" upgrade --with-new-pkgs
|
||||
fi
|
||||
|
||||
if [ $# -gt 0 ]; then
|
||||
"${APT[@]}" install --no-install-recommends "$@"
|
||||
fi
|
||||
|
||||
# Sweep EVERY installed lsphpNN* package (core + all extensions) to the candidate.
|
||||
mapfile -t PKGS < <(dpkg-query -W -f='${db:Status-Abbrev} ${Package}\n' "lsphp${PHPVER}*" 2>/dev/null | awk '$1 == "ii" {print $2}')
|
||||
[ "${#PKGS[@]}" -gt 0 ] || die "no lsphp${PHPVER}* packages installed — wrong base image?"
|
||||
"${APT[@]}" install --only-upgrade --no-install-recommends "${PKGS[@]}"
|
||||
|
||||
LEFT=$(apt list --upgradable 2>/dev/null | grep "^lsphp${PHPVER}" || true)
|
||||
[ -z "$LEFT" ] || die "lsphp packages still upgradable after the sweep:
|
||||
$LEFT"
|
||||
|
||||
INST=$(dpkg-query -W -f='${Version}' "lsphp${PHPVER}")
|
||||
RUNV=$("$PHPCLI" -r 'echo PHP_VERSION;') || die "lsphp${PHPVER} CLI does not run"
|
||||
|
||||
if [ -n "${EXPECT_LSPHP_PKG:-}" ] && [ "$INST" != "$EXPECT_LSPHP_PKG" ]; then
|
||||
die "installed lsphp${PHPVER}=$INST but CI resolved $EXPECT_LSPHP_PKG from the repo index (repo changed mid-build? re-run)."
|
||||
fi
|
||||
if [ -n "${EXPECT_PHP_VERSION:-}" ] && [ "$RUNV" != "$EXPECT_PHP_VERSION" ]; then
|
||||
die "lsphp${PHPVER} reports PHP $RUNV but the image will be labelled $EXPECT_PHP_VERSION."
|
||||
fi
|
||||
if [ -z "${EXPECT_PHP_VERSION:-}" ]; then
|
||||
echo "lsphp-apt-upgrade: WARNING no EXPECT_PHP_VERSION build-arg — image label will read 'unverified' (fine for local builds; CI always sets it)." >&2
|
||||
fi
|
||||
|
||||
printf '%s\n' "$RUNV" > /etc/cac-php-version
|
||||
|
||||
echo "lsphp-apt-upgrade: ===== version report ====="
|
||||
dpkg-query -W -f=' ${Package} ${Version}\n' "lsphp${PHPVER}*"
|
||||
echo " lsphp -v: $("$LSPHP" -v | head -n1)"
|
||||
echo " OS packages still upgradable: $(apt list --upgradable 2>/dev/null | grep -vc '^Listing' || true)"
|
||||
echo "lsphp-apt-upgrade: OK lsphp${PHPVER}=${INST} (PHP ${RUNV})"
|
||||
Reference in new issue
Block a user