diff --git a/.gitea/workflows/build-push.yaml b/.gitea/workflows/build-push.yaml index 43e7620..403d6ed 100644 --- a/.gitea/workflows/build-push.yaml +++ b/.gitea/workflows/build-push.yaml @@ -321,6 +321,10 @@ jobs: Build-LiteSpeed-Images: runs-on: ubuntu-latest + # One place for the OLS base tag: digest lookup and build-args both read it. + # OLS_VERSION pinned to 1.8.4 (see the build step); Dockerfile default differs. + env: + OLS_VERSION: "1.8.4" strategy: # One PHP version failing (e.g. an EOL stream) must not cancel the # security builds of the others. @@ -335,10 +339,63 @@ jobs: - name: Checkout uses: actions/checkout@v4 + # Base image pinned by DIGEST, looked up directly at Docker Hub (never via + # the pkg-cache mirror), so the mirror can only serve content-addressed + # blobs that BuildKit verifies. If the lookup fails this job does NOT use + # the mirror and pulls the tag straight from Docker Hub. docs/pkg-cache.md. + - name: Resolve base image digest (Docker Hub, direct) + id: base + env: + BASE_TAG: litespeedtech/openlitespeed:${{ env.OLS_VERSION }}-lsphp${{ matrix.phpver }} + run: | + set -uo pipefail + if ref=$(.gitea/scripts/resolve-base-image.sh "$BASE_TAG") && [[ "$ref" =~ ^[a-z0-9][a-z0-9._/-]*@sha256:[0-9a-f]{64}$ ]]; then + echo "base-image: ${BASE_TAG} -> ${ref} (digest from registry-1.docker.io)" + echo "image=${ref}" >> "$GITHUB_OUTPUT" + echo "pinned=true" >> "$GITHUB_OUTPUT" + else + echo "::warning::base image ${BASE_TAG}: Docker Hub digest lookup failed - pulling the tag directly from Docker Hub, pkg-cache registry mirror NOT used" + echo "image=${BASE_TAG}" >> "$GITHUB_OUTPUT" + echo "pinned=false" >> "$GITHUB_OUTPUT" + fi + + # Internal package cache (CT130 pkg-cache, docs/pkg-cache.md). Used ONLY if + # it answers over TLS with our pinned CA; otherwise (down, unreachable, + # bad/expired cert) this job builds from the upstream repos and Docker Hub + # exactly as before. The repo cache and the registry mirror are probed + # separately so either can fail back on its own. + - name: Probe package cache + id: pkgcache + # The registry mirror is only used for a digest-pinned base image. + env: + BASE_PINNED: ${{ steps.base.outputs.pinned }} + run: | + url=""; mirror="" + if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40/; then url="https://192.168.1.40"; fi + if [ "$BASE_PINNED" = true ] && curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40:5000/v2/; then mirror="192.168.1.40:5000"; fi + echo "pkg-cache: repos=${url:-} registry-mirror=${mirror:-}" + [ -n "$url" ] || echo "::warning::pkg-cache unreachable - building from upstream repositories" + echo "url=${url}" >> "$GITHUB_OUTPUT" + echo "mirror=${mirror}" >> "$GITHUB_OUTPUT" + - name: Set up QEMU uses: docker/setup-qemu-action@v3 + # BuildKit (docker-container driver) ignores dockerd's registry-mirrors, + # so the mirror is configured on the builder; BuildKit itself still falls + # back to Docker Hub if the mirror stops answering mid-run. + - name: Set up Docker Buildx (Docker Hub via pkg-cache mirror) + if: steps.pkgcache.outputs.mirror != '' + uses: docker/setup-buildx-action@v3 + with: + buildkitd-config-inline: | + [registry."docker.io"] + mirrors = ["192.168.1.40:5000"] + [registry."192.168.1.40:5000"] + ca = ["${{ github.workspace }}/keys/pkg-cache/ca.crt"] + - name: Set up Docker Buildx + if: steps.pkgcache.outputs.mirror == '' uses: docker/setup-buildx-action@v3 # Per-run cache-bust value. Each job's buildx builder is created fresh by @@ -409,7 +466,9 @@ jobs: push: true build-args: | PHPVER=${{ matrix.phpver }} - OLS_VERSION=1.8.4 + OLS_VERSION=${{ env.OLS_VERSION }} + BASE_IMAGE=${{ steps.base.outputs.image }} + PKG_CACHE=${{ steps.pkgcache.outputs.url }} CACHE_BUST=${{ steps.bust.outputs.value }} EXPECT_LSPHP_PKG=${{ steps.lsphp.outputs.pkg }} EXPECT_PHP_VERSION=${{ steps.lsphp.outputs.php }} @@ -423,6 +482,10 @@ jobs: Build-LSPHP-Images: runs-on: ubuntu-latest + # One place for the OLS base tag: digest lookup and build-args both read it. + # OLS_VERSION pinned to 1.8.4 (see the build step); Dockerfile default differs. + env: + OLS_VERSION: "1.8.4" strategy: # One PHP version failing (e.g. an EOL stream) must not cancel the # security builds of the others. @@ -437,10 +500,63 @@ jobs: - name: Checkout uses: actions/checkout@v4 + # Base image pinned by DIGEST, looked up directly at Docker Hub (never via + # the pkg-cache mirror), so the mirror can only serve content-addressed + # blobs that BuildKit verifies. If the lookup fails this job does NOT use + # the mirror and pulls the tag straight from Docker Hub. docs/pkg-cache.md. + - name: Resolve base image digest (Docker Hub, direct) + id: base + env: + BASE_TAG: litespeedtech/openlitespeed:${{ env.OLS_VERSION }}-lsphp${{ matrix.phpver }} + run: | + set -uo pipefail + if ref=$(.gitea/scripts/resolve-base-image.sh "$BASE_TAG") && [[ "$ref" =~ ^[a-z0-9][a-z0-9._/-]*@sha256:[0-9a-f]{64}$ ]]; then + echo "base-image: ${BASE_TAG} -> ${ref} (digest from registry-1.docker.io)" + echo "image=${ref}" >> "$GITHUB_OUTPUT" + echo "pinned=true" >> "$GITHUB_OUTPUT" + else + echo "::warning::base image ${BASE_TAG}: Docker Hub digest lookup failed - pulling the tag directly from Docker Hub, pkg-cache registry mirror NOT used" + echo "image=${BASE_TAG}" >> "$GITHUB_OUTPUT" + echo "pinned=false" >> "$GITHUB_OUTPUT" + fi + + # Internal package cache (CT130 pkg-cache, docs/pkg-cache.md). Used ONLY if + # it answers over TLS with our pinned CA; otherwise (down, unreachable, + # bad/expired cert) this job builds from the upstream repos and Docker Hub + # exactly as before. The repo cache and the registry mirror are probed + # separately so either can fail back on its own. + - name: Probe package cache + id: pkgcache + # The registry mirror is only used for a digest-pinned base image. + env: + BASE_PINNED: ${{ steps.base.outputs.pinned }} + run: | + url=""; mirror="" + if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40/; then url="https://192.168.1.40"; fi + if [ "$BASE_PINNED" = true ] && curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40:5000/v2/; then mirror="192.168.1.40:5000"; fi + echo "pkg-cache: repos=${url:-} registry-mirror=${mirror:-}" + [ -n "$url" ] || echo "::warning::pkg-cache unreachable - building from upstream repositories" + echo "url=${url}" >> "$GITHUB_OUTPUT" + echo "mirror=${mirror}" >> "$GITHUB_OUTPUT" + - name: Set up QEMU uses: docker/setup-qemu-action@v3 + # BuildKit (docker-container driver) ignores dockerd's registry-mirrors, + # so the mirror is configured on the builder; BuildKit itself still falls + # back to Docker Hub if the mirror stops answering mid-run. + - name: Set up Docker Buildx (Docker Hub via pkg-cache mirror) + if: steps.pkgcache.outputs.mirror != '' + uses: docker/setup-buildx-action@v3 + with: + buildkitd-config-inline: | + [registry."docker.io"] + mirrors = ["192.168.1.40:5000"] + [registry."192.168.1.40:5000"] + ca = ["${{ github.workspace }}/keys/pkg-cache/ca.crt"] + - name: Set up Docker Buildx + if: steps.pkgcache.outputs.mirror == '' uses: docker/setup-buildx-action@v3 # Per-run cache-bust value. Each job's buildx builder is created fresh by @@ -511,7 +627,9 @@ jobs: push: true build-args: | PHPVER=${{ matrix.phpver }} - OLS_VERSION=1.8.4 + OLS_VERSION=${{ env.OLS_VERSION }} + BASE_IMAGE=${{ steps.base.outputs.image }} + PKG_CACHE=${{ steps.pkgcache.outputs.url }} CACHE_BUST=${{ steps.bust.outputs.value }} EXPECT_LSPHP_PKG=${{ steps.lsphp.outputs.pkg }} EXPECT_PHP_VERSION=${{ steps.lsphp.outputs.php }} diff --git a/Dockerfile.litespeed b/Dockerfile.litespeed index f362f9f..7960e7f 100644 --- a/Dockerfile.litespeed +++ b/Dockerfile.litespeed @@ -22,12 +22,18 @@ ## after FROM for any RUN steps that need it. ARG OLS_VERSION=1.8.5 ARG PHPVER=83 +## CI passes BASE_IMAGE=litespeedtech/openlitespeed@sha256: of the +## ${OLS_VERSION}-lsphp${PHPVER} tag, resolved DIRECTLY from Docker Hub in the same +## run (.gitea/scripts/resolve-base-image.sh), so the pkg-cache registry mirror can +## only serve content-addressed blobs. Local builds use the tag. Every stage below +## (incl. the emergency lsphp-src stage) starts from this one image. +ARG BASE_IMAGE=litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER} ## ---- EMERGENCY ONLY: lsphp compiled from php.net source ---------------------- ## Idle unless PHP_SRC_VERSION is set (then /lsphp-src-out stays empty and the ## overlay step below is a no-op). Same stage as Dockerfile.lsphp — see there and ## docs/lsphp-source-emergency.md. -FROM litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER} AS lsphp-src +FROM ${BASE_IMAGE} AS lsphp-src ARG PHPVER=83 ARG CACHE_BUST= ARG EXPECT_LSPHP_PKG= @@ -42,7 +48,7 @@ RUN --mount=type=bind,source=scripts,target=/mnt/scripts \ fi; \ PHPVER="${PHPVER}" bash /mnt/scripts/lsphp-src-build.sh -FROM litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER} +FROM ${BASE_IMAGE} ARG PHPVER=83 ENV PHPVER=${PHPVER} @@ -80,11 +86,20 @@ ENV PHPVER=${PHPVER} ARG CACHE_BUST= ARG EXPECT_LSPHP_PKG= ARG EXPECT_PHP_VERSION= +## OPTIONAL internal package cache, e.g. --build-arg PKG_CACHE=https://192.168.1.40 +## (CT130 pkg-cache, https only). Unset = upstream repositories (Ubuntu + +## rpms.litespeedtech.com). Repo files are restored at the end of the RUN, so no +## layer keeps the cache address. See scripts/pkg-cache-repos.sh. +ARG PKG_CACHE= +COPY ./scripts/pkg-cache-repos.sh /usr/local/sbin/pkg-cache-repos.sh +COPY ./keys/pkg-cache/ /usr/local/share/pkg-cache/ COPY ./scripts/lsphp-apt-upgrade.sh /usr/local/sbin/lsphp-apt-upgrade.sh -RUN /usr/local/sbin/lsphp-apt-upgrade.sh "${PHPVER}" \ +RUN /usr/local/sbin/pkg-cache-repos.sh apt && \ + /usr/local/sbin/lsphp-apt-upgrade.sh "${PHPVER}" \ gettext-base sudo cron \ ca-certificates curl wget \ lsphp${PHPVER}-ldap && \ + /usr/local/sbin/pkg-cache-repos.sh restore && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/* diff --git a/Dockerfile.lsphp b/Dockerfile.lsphp index 9820a31..c057c4e 100644 --- a/Dockerfile.lsphp +++ b/Dockerfile.lsphp @@ -21,6 +21,12 @@ ARG OLS_VERSION=1.8.4 ARG PHPVER=83 +## CI passes BASE_IMAGE=litespeedtech/openlitespeed@sha256: of the +## ${OLS_VERSION}-lsphp${PHPVER} tag, resolved DIRECTLY from Docker Hub in the same +## run (.gitea/scripts/resolve-base-image.sh), so the pkg-cache registry mirror can +## only serve content-addressed blobs. Local builds use the tag. Every stage below +## (incl. the emergency lsphp-src stage) starts from this one image. +ARG BASE_IMAGE=litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER} ## ---- stage 0 (EMERGENCY ONLY): lsphp compiled from php.net source ----------- ## Idle unless PHP_SRC_VERSION is set: lsphp-src-build.sh then exits at once and @@ -30,7 +36,7 @@ ARG PHPVER=83 ## — CI: .gitea/workflows/lsphp-source-emergency.yaml; runbook: ## docs/lsphp-source-emergency.md. The tarball is sha256- AND OpenPGP-verified ## against keys/php-src/; configure flags are replayed from the packaged lsphp. -FROM litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER} AS lsphp-src +FROM ${BASE_IMAGE} AS lsphp-src ARG PHPVER=83 ARG CACHE_BUST= ARG EXPECT_LSPHP_PKG= @@ -77,7 +83,7 @@ RUN --mount=type=bind,source=scripts,target=/mnt/scripts \ ## Separate stage on purpose: the compiler + headers (~400MB) stay out of the ## shipped image, which gains only the ~40KB .so. Costs ~1-2 min of CI per PHP ## version; both stages share the same base layer, so no extra pull. -FROM litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER} AS ext-build +FROM ${BASE_IMAGE} AS ext-build ARG PHPVER=83 ## Toolchain layer, deliberately BEFORE the source COPY so editing the extension @@ -94,11 +100,20 @@ ARG PHPVER=83 ARG CACHE_BUST= ARG EXPECT_LSPHP_PKG= ARG EXPECT_PHP_VERSION= +## OPTIONAL internal package cache, e.g. --build-arg PKG_CACHE=https://192.168.1.40 +## (CT130 pkg-cache, https only). Unset = upstream repositories (Ubuntu + +## rpms.litespeedtech.com). Repo files are restored at the end of the RUN, so no +## layer keeps the cache address. See scripts/pkg-cache-repos.sh. +ARG PKG_CACHE= +COPY ./scripts/pkg-cache-repos.sh /usr/local/sbin/pkg-cache-repos.sh +COPY ./keys/pkg-cache/ /usr/local/share/pkg-cache/ COPY ./scripts/lsphp-apt-upgrade.sh /usr/local/sbin/lsphp-apt-upgrade.sh RUN set -e; \ + /usr/local/sbin/pkg-cache-repos.sh apt; \ /usr/local/sbin/lsphp-apt-upgrade.sh --no-os-upgrade "${PHPVER}" \ build-essential autoconf pkg-config \ lsphp${PHPVER}-dev; \ + /usr/local/sbin/pkg-cache-repos.sh restore; \ RTV=$(dpkg-query -W -f='${Version}' lsphp${PHPVER}); \ DEVV=$(dpkg-query -W -f='${Version}' lsphp${PHPVER}-dev); \ if [ "$RTV" != "$DEVV" ]; then \ @@ -168,7 +183,7 @@ RUN set -e; \ cp modules/cac_path_parity.so /build-out/ ## ---- stage 2: the shipped sidecar image ------------------------------------ -FROM litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER} +FROM ${BASE_IMAGE} ARG PHPVER=83 ENV PHPVER=${PHPVER} @@ -196,10 +211,18 @@ ENV PHPVER=${PHPVER} ARG CACHE_BUST= ARG EXPECT_LSPHP_PKG= ARG EXPECT_PHP_VERSION= +## OPTIONAL internal package cache, e.g. --build-arg PKG_CACHE=https://192.168.1.40 +## (CT130 pkg-cache, https only). Unset = upstream repositories (Ubuntu + +## rpms.litespeedtech.com). Repo files are restored at the end of the RUN, so no +## layer keeps the cache address. See scripts/pkg-cache-repos.sh. +ARG PKG_CACHE= +COPY ./scripts/pkg-cache-repos.sh /usr/local/sbin/pkg-cache-repos.sh +COPY ./keys/pkg-cache/ /usr/local/share/pkg-cache/ 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); \ + /usr/local/sbin/pkg-cache-repos.sh apt; \ if ! /usr/local/sbin/lsphp-apt-upgrade.sh "${PHPVER}" \ ca-certificates \ lsphp${PHPVER}="$V" lsphp${PHPVER}-common="$V" lsphp${PHPVER}-ldap="$V"; then \ @@ -211,6 +234,7 @@ RUN set -e; \ echo " build (CI) / rebuild with --no-cache (local)." >&2; \ exit 1; \ fi; \ + /usr/local/sbin/pkg-cache-repos.sh restore; \ apt-get clean; \ rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*; \ RTV=$(dpkg-query -W -f='${Version}' lsphp${PHPVER}); \ diff --git a/docs/pkg-cache.md b/docs/pkg-cache.md index b32b772..0b2a368 100644 --- a/docs/pkg-cache.md +++ b/docs/pkg-cache.md @@ -21,6 +21,10 @@ On that day a build without the cache spent **1031 s** in its first dnf step. Th | `https://192.168.1.40/ubuntu/` | `https://archive.ubuntu.com/ubuntu`, `https://us.archive.ubuntu.com/ubuntu` | | `https://192.168.1.40/ubuntu-security/` | `https://security.ubuntu.com/ubuntu`, `https://archive.ubuntu.com/ubuntu` | | `https://192.168.1.40/litespeed/` | `https://rpms.litespeedtech.com`, then `http://rpms.litespeedtech.com` | +| `https://192.168.1.40/nodesource/` | `https://rpm.nodesource.com` (origin only; RPMs signed, repomd not) — added 2026-10-22 | +| `https://192.168.1.40/debian/` | `https://deb.debian.org/debian` (CDN origin; princeton was 6 h behind on trixie-updates) — added 2026-10-22 | +| `https://192.168.1.40/debian-security/` | `https://security.debian.org/debian-security`, `https://deb.debian.org/debian-security` — added 2026-10-22 | +| `https://192.168.1.40/goproxy/` | `https://proxy.golang.org` (Go module proxy; `/sumdb/` is 404 there, so `go` checks sum.golang.org directly) — added 2026-10-22 | | `192.168.1.40:5000` (TLS, pkg-cache CA) | Docker Hub pull-through cache (`registry-1.docker.io`) | Plain `http://192.168.1.40/` answers **403** to LAN clients. @@ -32,6 +36,7 @@ Cache rules: - apt `dists/`, except `by-hash/` - `*-latest*.rpm` and `remi-release-N.rpm` - `RPM-GPG-KEY*` + - Go proxy `/@v/list` and `/@latest` - directory listings - **Packages** (`*.rpm`, `*.deb`, hash-named repodata, `by-hash/`) are cached for 30 days and evicted after 60 days unused. Total size is capped at 110 GB, with 15 GB kept free. - On a connect error, timeout or 5xx, the request falls through to the next tier. Only the **last** tier may answer from a stale copy, so the order is primary, then fallback, then stale. @@ -65,6 +70,18 @@ docker buildx build --build-arg PKG_CACHE=https://192.168.1.40 --build-arg PHPVE - Requires `rpm -K` to report `digests signatures OK`. - Installs with `--setopt=localpkg_gpgcheck=1`. +**`nodesource MAJOR`** (added 2026-10-22) replaces `curl https://rpm.nodesource.com/setup_N.x | bash`: +- writes `/etc/yum.repos.d/nodesource-nodejs.repo` with the ORIGIN baseurl, `gpgcheck=1` and `gpgkey=file:///usr/local/share/pkg-cache/RPM-GPG-KEY-nodesource` (key committed in `keys/pkg-cache/`); +- with `PKG_CACHE` it is then rewritten like the other repos; `restore` puts the origin version back, so the shipped image keeps a normal NodeSource repo. + +**`go-env`** (added 2026-10-22): `eval "$(pkg-cache-repos.sh go-env)"` inside the RUN that runs `go mod download`/`go build`. Prints `GOPROXY=/goproxy|https://proxy.golang.org|direct` (`|` = fall through on ANY error) and `SSL_CERT_FILE=/ca-bundle.pem`; nothing without `PKG_CACHE`. Modules stay verified by `go.sum` + sum.golang.org. End the RUN with `restore` (removes the bundle). + +**EL9**: `release-install epel|remi` picks the EL major from `rpm -E %rhel` (9 or 10); `RPM-GPG-KEY-EPEL-9` is committed. + +**Debian** (`apt` mode): `deb.debian.org/debian`, `deb.debian.org/debian-security` and `security.debian.org` (classic and deb822 `.sources`) are rewritten to `/debian/` and `/debian-security/`. + +**Not proxied, on purpose:** PyPI and the npm registry. Our pip/npm installs are not hash-locked (`pip install -r` without `--require-hashes`, `npm install -g pm2@latest`), so the client would trust whatever the cache served — a compromised cache would mean code execution in shipped images. Proxy them only after the installs are hash-pinned. GitHub-release downloads are likewise left direct. + **`restore`**: - Puts rewritten files back. - Deletes files the script created. @@ -75,9 +92,31 @@ Every RUN that rewrites ends with `restore`, so no shipped layer carries the LAN `PKG_CACHE` must start with `https://`. `http://` is refused. -`Dockerfile.litespeed` and `Dockerfile.lsphp` are **not wired yet**. Two things are needed first: -- the multi-stage `lsphp-apt-upgrade.sh` flow needs a `PKG_CACHE` ARG in each stage; -- its madison check must accept the cache URL. It currently requires `*rpms.litespeedtech.com*`, and via the cache madison shows `https://192.168.1.40/litespeed/debian`. +`Dockerfile.litespeed` and `Dockerfile.lsphp` are wired since 2026-10-22 (normal apt path; the php.net EMERGENCY stage `lsphp-src` stays on upstream). `lsphp-apt-upgrade.sh`'s madison gate accepts `/litespeed/` only when `PKG_CACHE` is set. + +## Repositories wired (2026-10-22) + +The script and `keys/pkg-cache/` are **canonical here**; every other repo carries byte-identical copies (`sha256sum scripts/pkg-cache-repos.sh` to compare). Change it here first, then sync. Same for `.gitea/scripts/resolve-base-image.sh`. + +| Repo | Image(s) | Bases | Through the cache | Base digest-pinned | +|---|---|---|---|---| +| cloud-apache-container | cac, cac-fpm, shared-httpd (almalinux/10-base); shared-ols, cac-litespeed, cac-lsphp (litespeedtech/openlitespeed) | Docker Hub | AlmaLinux/EPEL/Remi (rpm); Ubuntu + LiteSpeed (apt) | yes, all 6 jobs | +| cloud-node-container | cnoc:node18..26 | almalinux/10-base | AlmaLinux/EPEL + NodeSource (`nodesource N`) | yes | +| cloud-memcached-container | cmc | almalinux/9-base | AlmaLinux 9 + EPEL 9 | yes | +| cloud-haproxy-container | chc | almalinux/9-base | AlmaLinux 9 + EPEL 9 (inotify-tools GitHub tarball direct) | yes | +| whp-default-container | whp-default | httpd:alpine | nothing to install (mirror only) | yes | +| versitygw-container | versitygw | ubuntu:latest | Ubuntu (versitygw .deb from GitHub direct) | yes | +| haproxy-manager-base | haproxy-manager-base, coraza-spoa | repo.anhonesthost.net python:3.12-slim / golang:1.25 (not Docker Hub: no mirror); gcr.io distroless | Debian (apt) + Go modules (`go-env`); pip direct | n/a; `mirror-base-image.yaml` now copies Docker Hub images by the digest resolved directly from Docker Hub | +| cpanel-importer | cpanel-importer | almalinux:10-minimal | AlmaLinux/EPEL/Remi — **PR #1 open, not merged** (pending rebase onto the PHP 8.5 work) | in the PR | + +Not image builds (nothing to wire): kb-anhonesthost, anhonesthost-blog, dao-mail-signup, renovate-runner. ols-mail-client builds images in CI only for scanning/smoke tests (never pushed), its bases are already digest-pinned, and they install no OS packages. + +## Adding a new upstream + +1. On CT130: back up, then add the prefix to `UPSTREAMS` in `/usr/local/sbin/pkgcache-gen-nginx` (origin first; add a mirror tier only if you measured it is as fresh as the origin). If its mutable metadata isn't covered by `META_RE`, extend it there AND in `/usr/local/sbin/pkgcache-refresh`. Add the prefix to the `location = /` text in `/etc/nginx/pkgcache/server-common.conf`. +2. `pkgcache-gen-nginx > /etc/nginx/sites-available/pkgcache && nginx -t && systemctl reload nginx` (`pct exec` has no /usr/local/sbin in PATH: use full paths). Verify old and new prefixes with `curl --cacert keys/pkg-cache/ca.crt https://192.168.1.40//...` (look at `X-Cache-Status`/`X-Pkgcache-Tier`). CT130 has no egress filter, so no firewall change is needed for new origins. +3. Only proxy upstreams whose content the CLIENT verifies end to end (GPG-signed packages/metadata, Go sumdb, content digests). Commit any signing key under `keys/pkg-cache/` with its fingerprint and provenance in the README. +4. Teach `scripts/pkg-cache-repos.sh` the rewrite (keep the origin as a fallback where the client supports it), test through the cache from a runner-network host, then sync the copies. ## Trust model @@ -174,8 +213,7 @@ Hub, direct)"**: - Log lines to look for: `base-image: almalinux/10-base:latest -> almalinux/10-base@sha256:...` and `pkg-cache: repos=https://192.168.1.40 registry-mirror=192.168.1.40:5000`. -Applied to: Build-and-Push, Build-FPM-Images, Build-Shared-httpd, Build-Shared-OLS -(`litespeedtech/openlitespeed:${OLS_VERSION}-lsphp${PHPVER}`) here, and cloud-node-container. +Applied to every job here (Build-and-Push, FPM, Shared-httpd, Shared-OLS, LiteSpeed, LSPHP) and to every other repo with a Docker Hub base (see "Repositories wired"). The script is canonical here (`.gitea/scripts/resolve-base-image.sh`); other repos carry byte-identical copies. diff --git a/keys/pkg-cache/README b/keys/pkg-cache/README index bdcc04b..c8edeec 100644 --- a/keys/pkg-cache/README +++ b/keys/pkg-cache/README @@ -18,3 +18,15 @@ RPM-GPG-KEY-remi2024 Remi's RPM repository 2024 CF1D F005 7CE8 5DFF 5B2F 2A37 Remi rotates keys (remi2025, ...): when remi-release starts being signed by a new key, `release-install remi` fails loudly - add the new key here (and to pkg-cache-repos.sh). +RPM-GPG-KEY-EPEL-9 Fedora (epel9) FF8A D134 4597 106E CE81 3B91 8A38 72BF 3228 467C + fetched 2026-10-22 over HTTPS from dl.fedoraproject.org and + mirror.math.princeton.edu (identical); used by `release-install epel` + on EL9 images (cloud-memcached-container, cloud-haproxy-container). +RPM-GPG-KEY-nodesource Nodesource Operations + 242B 8138 31AF 0956 2B6C 46F7 6B88 DA4E 3AF2 8A14 (rsa4096, 2023-12-22) + fetched 2026-10-22 over HTTPS from + https://rpm.nodesource.com/gpgkey/ns-operations-public.key (the key + NodeSource's own setup_N.x scripts reference); verified by dnf + gpgcheck against the nodejs RPMs. Not on keys.openpgp.org / + keyserver.ubuntu.com - confirm out of band. Used by + `pkg-cache-repos.sh nodesource MAJOR` (cloud-node-container). diff --git a/keys/pkg-cache/RPM-GPG-KEY-EPEL-9 b/keys/pkg-cache/RPM-GPG-KEY-EPEL-9 new file mode 100644 index 0000000..234c12f --- /dev/null +++ b/keys/pkg-cache/RPM-GPG-KEY-EPEL-9 @@ -0,0 +1,29 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGE3mOsBEACsU+XwJWDJVkItBaugXhXIIkb9oe+7aadELuVo0kBmc3HXt/Yp +CJW9hHEiGZ6z2jwgPqyJjZhCvcAWvgzKcvqE+9i0NItV1rzfxrBe2BtUtZmVcuE6 +2b+SPfxQ2Hr8llaawRjt8BCFX/ZzM4/1Qk+EzlfTcEcpkMf6wdO7kD6ulBk/tbsW +DHX2lNcxszTf+XP9HXHWJlA2xBfP+Dk4gl4DnO2Y1xR0OSywE/QtvEbN5cY94ieu +n7CBy29AleMhmbnx9pw3NyxcFIAsEZHJoU4ZW9ulAJ/ogttSyAWeacW7eJGW31/Z +39cS+I4KXJgeGRI20RmpqfH0tuT+X5Da59YpjYxkbhSK3HYBVnNPhoJFUc2j5iKy +XLgkapu1xRnEJhw05kr4LCbud0NTvfecqSqa+59kuVc+zWmfTnGTYc0PXZ6Oa3rK +44UOmE6eAT5zd/ToleDO0VesN+EO7CXfRsm7HWGpABF5wNK3vIEF2uRr2VJMvgqS +9eNwhJyOzoca4xFSwCkc6dACGGkV+CqhufdFBhmcAsUotSxe3zmrBjqA0B/nxIvH +DVgOAMnVCe+Lmv8T0mFgqZSJdIUdKjnOLu/GRFhjDKIak4jeMBMTYpVnU+HhMHLq +uDiZkNEvEEGhBQmZuI8J55F/a6UURnxUwT3piyi3Pmr2IFD7ahBxPzOBCQARAQAB +tCdGZWRvcmEgKGVwZWw5KSA8ZXBlbEBmZWRvcmFwcm9qZWN0Lm9yZz6JAk4EEwEI +ADgWIQT/itE0RZcQbs6BO5GKOHK/MihGfAUCYTeY6wIbDwULCQgHAgYVCgkICwIE +FgIDAQIeAQIXgAAKCRCKOHK/MihGfFX/EACBPWv20+ttYu1A5WvtHJPzwbj0U4yF +3zTQpBglQ2UfkRpYdipTlT3Ih6j5h2VmgRPtINCc/ZE28adrWpBoeFIS2YAKOCLC +nZYtHl2nCoLq1U7FSttUGsZ/t8uGCBgnugTfnIYcmlP1jKKA6RJAclK89evDQX5n +R9ZD+Cq3CBMlttvSTCht0qQVlwycedH8iWyYgP/mF0W35BIn7NuuZwWhgR00n/VG +4nbKPOzTWbsP45awcmivdrS74P6mL84WfkghipdmcoyVb1B8ZP4Y/Ke0RXOnLhNe +CfrXXvuW+Pvg2RTfwRDtehGQPAgXbmLmz2ZkV69RGIr54HJv84NDbqZovRTMr7gL +9k3ciCzXCiYQgM8yAyGHV0KEhFSQ1HV7gMnt9UmxbxBE2pGU7vu3CwjYga5DpwU7 +w5wu1TmM5KgZtZvuWOTDnqDLf0cKoIbW8FeeCOn24elcj32bnQDuF9DPey1mqcvT +/yEo/Ushyz6CVYxN8DGgcy2M9JOsnmjDx02h6qgWGWDuKgb9jZrvRedpAQCeemEd +fhEs6ihqVxRFl16HxC4EVijybhAL76SsM2nbtIqW1apBQJQpXWtQwwdvgTVpdEtE +r4ArVJYX5LrswnWEQMOelugUG6S3ZjMfcyOa/O0364iY73vyVgaYK+2XtT2usMux +VL469Kj5m13T6w== +=Mjs/ +-----END PGP PUBLIC KEY BLOCK----- diff --git a/keys/pkg-cache/RPM-GPG-KEY-nodesource b/keys/pkg-cache/RPM-GPG-KEY-nodesource new file mode 100644 index 0000000..0618885 --- /dev/null +++ b/keys/pkg-cache/RPM-GPG-KEY-nodesource @@ -0,0 +1,52 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +oSVkzT/Mwp9zFVQu5w== +=drMP +-----END PGP PUBLIC KEY BLOCK----- diff --git a/scripts/lsphp-apt-upgrade.sh b/scripts/lsphp-apt-upgrade.sh index 6f59090..88c123d 100755 --- a/scripts/lsphp-apt-upgrade.sh +++ b/scripts/lsphp-apt-upgrade.sh @@ -18,7 +18,8 @@ ## 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}. +## * the LiteSpeed repo must be the candidate source for lsphp${PHPVER} +## (rpms.litespeedtech.com, or ${PKG_CACHE}/litespeed/ when PKG_CACHE is set). ## * 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; @@ -52,8 +53,18 @@ echo "lsphp-apt-upgrade: base image lsphp${PHPVER}=$(dpkg-query -W -f='${Version # 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). +# With the internal package cache (PKG_CACHE build-arg, scripts/pkg-cache-repos.sh +# apt) the LiteSpeed source reads ${PKG_CACHE}/litespeed/... instead; that URL is +# accepted ONLY when PKG_CACHE is set, and only under its /litespeed/ path. MADISON=$(apt-cache madison "lsphp${PHPVER}") -if [[ "$MADISON" != *rpms.litespeedtech.com* ]]; then +LS_REPO_OK=0 +if [[ "$MADISON" == *rpms.litespeedtech.com* ]]; then + LS_REPO_OK=1 +elif [ -n "${PKG_CACHE:-}" ] && [[ "$MADISON" == *"${PKG_CACHE%/}/litespeed/"* ]]; then + LS_REPO_OK=1 + echo "lsphp-apt-upgrade: LiteSpeed repo via package cache ${PKG_CACHE%/}/litespeed/" +fi +if [ "$LS_REPO_OK" != 1 ]; 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 diff --git a/scripts/pkg-cache-repos.sh b/scripts/pkg-cache-repos.sh index 37580a0..ee2bd7c 100755 --- a/scripts/pkg-cache-repos.sh +++ b/scripts/pkg-cache-repos.sh @@ -4,11 +4,16 @@ ## when PKG_CACHE is set; without it the repo files are left alone and builds ## use the upstream repositories as before. ## -## PKG_CACHE=https://192.168.1.40 pkg-cache-repos.sh rpm # AlmaLinux/EPEL/Remi -## PKG_CACHE=https://192.168.1.40 pkg-cache-repos.sh apt # Ubuntu + LiteSpeed -## pkg-cache-repos.sh release-install epel [remi] # GPG-verified release RPMs +## PKG_CACHE=https://192.168.1.40 pkg-cache-repos.sh rpm # AlmaLinux/EPEL/Remi/NodeSource +## PKG_CACHE=https://192.168.1.40 pkg-cache-repos.sh apt # Ubuntu, Debian, LiteSpeed +## pkg-cache-repos.sh release-install epel [remi] # GPG-verified release RPMs (EL9/EL10) +## pkg-cache-repos.sh nodesource MAJOR # NodeSource repo file + pinned key +## eval "$(pkg-cache-repos.sh go-env)" # GOPROXY via the cache (go.sum/sumdb verified) ## pkg-cache-repos.sh restore # undo everything above ## +## CANONICAL copy: cloud-apache-container/scripts/pkg-cache-repos.sh (docs/pkg-cache.md +## there). Other repos carry byte-identical copies - change it there first. +## ## Every RUN that rewrites must `restore` before it ends: it puts rewritten files ## back, deletes files this script created, deletes any NEW repo/apt file that ## still references the cache, then FAILS if any reference is left — so no shipped @@ -43,7 +48,7 @@ ## cache (with the CA), and a build without PKG_CACHE uses upstream sources. set -euo pipefail -MODE="${1:?usage: pkg-cache-repos.sh rpm|apt|release-install NAME...|restore}" +MODE="${1:?usage: pkg-cache-repos.sh rpm|apt|release-install NAME...|nodesource MAJOR|go-env|restore}" shift || true STATE=/var/lib/pkg-cache-repos # saved originals + manifest + created files SHARE="${PKG_CACHE_SHARE:-/usr/local/share/pkg-cache}" # ca.crt + RPM-GPG-KEY-* @@ -57,10 +62,22 @@ if [ -n "$C" ]; then https://*) ;; *) die "PKG_CACHE must be an https:// URL (plain http is refused), got '$C'" ;; esac + [[ "$C" =~ ^https://[A-Za-z0-9.-]+(:[0-9]+)?(/[A-Za-z0-9._/-]*)?$ ]] || die "PKG_CACHE has unexpected characters: '$C'" HOSTPORT="${C#https://}"; HOSTPORT="${HOSTPORT%%/*}" [ -s "$SHARE/ca.crt" ] || die "PKG_CACHE set but $SHARE/ca.crt (pkg-cache CA) is missing" fi +# System CA bundle (EL vs Debian/Ubuntu path); the pkg-cache CA is appended to a +# copy in the state dir, never to the system store. +sys_bundle() { + local b + for b in /etc/pki/tls/certs/ca-bundle.crt /etc/ssl/certs/ca-certificates.crt; do + [ -s "$b" ] && { echo "$b"; return 0; } + done + die "no system CA bundle found" +} +make_bundle() { cat "$(sys_bundle)" "$SHARE/ca.crt" > "$STATE/ca-bundle.pem"; } + # Files that may hold repo configuration; restore re-checks all of them. repo_files() { find /etc/yum.repos.d /etc/dnf /etc/apt -type f 2>/dev/null | sort || true @@ -112,18 +129,20 @@ if [ "$MODE" = release-install ]; then curl_opts=(-fsS --proto '=https' --retry 5 --retry-all-errors --max-time 120) if [ -n "$C" ]; then start_session - cat /etc/pki/tls/certs/ca-bundle.crt "$SHARE/ca.crt" > "$STATE/ca-bundle.pem" + make_bundle curl_opts+=(--cacert "$STATE/ca-bundle.pem") fi files=() + el=$(rpm -E '%{?rhel}' 2>/dev/null || true) + case "$el" in 9|10) ;; *) die "release-install supports EL9/EL10 only (rpm -E %rhel = '$el')" ;; esac for name in "$@"; do case "$name" in - epel) pkg=epel-release key=RPM-GPG-KEY-EPEL-10 - path=/epel/epel-release-latest-10.noarch.rpm - origin=https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm ;; + epel) pkg=epel-release key=RPM-GPG-KEY-EPEL-$el + path=/epel/epel-release-latest-$el.noarch.rpm + origin=https://dl.fedoraproject.org/pub/epel/epel-release-latest-$el.noarch.rpm ;; remi) pkg=remi-release key=RPM-GPG-KEY-remi2024 - path=/remi/enterprise/remi-release-10.rpm - origin=https://rpms.remirepo.net/enterprise/remi-release-10.rpm ;; + path=/remi/enterprise/remi-release-$el.rpm + origin=https://rpms.remirepo.net/enterprise/remi-release-$el.rpm ;; *) die "unknown release package '$name'" ;; esac url="$origin"; [ -n "$C" ] && url="$C$path" @@ -142,14 +161,14 @@ if [ "$MODE" = release-install ]; then exit 0 fi -if [ -z "$C" ]; then +if [ -z "$C" ] && [ "$MODE" != nodesource ] && [ "$MODE" != go-env ]; then echo "pkg-cache-repos($MODE): PKG_CACHE not set - using upstream repositories" exit 0 fi rewrite_rpm() { local f="$1" - grep -q -E '^#? ?baseurl=https?://(repo\.almalinux\.org|download\.example|rpms\.remirepo\.net)/' "$f" || return 0 + grep -q -E '^#? ?baseurl=https?://(repo\.almalinux\.org|download\.example|rpms\.remirepo\.net|rpm\.nodesource\.com)/' "$f" || return 0 save "$f" # Per [section]: a (commented or live) baseurl on a known origin becomes # "baseurl=// /"; that section's @@ -185,6 +204,8 @@ rewrite_rpm() { l = rw(l, "^#? ?baseurl=https?://repo\\.almalinux\\.org/almalinux/", "almalinux", "https://repo.almalinux.org/almalinux", 1) l = rw(l, "^#? ?baseurl=https?://download\\.example/pub/epel/", "epel", "https://dl.fedoraproject.org/pub/epel", 0) l = rw(l, "^#? ?baseurl=https?://rpms\\.remirepo\\.net/", "remi", "https://rpms.remirepo.net", 1) + # NodeSource signs its RPMs but not repomd.xml (404): gpgcheck only. + l = rw(l, "^#? ?baseurl=https?://rpm\\.nodesource\\.com/", "nodesource", "https://rpm.nodesource.com", 0) } buf[++n] = l } @@ -194,19 +215,65 @@ rewrite_rpm() { rewrite_apt() { local f="$1" - grep -q -E 'https?://([a-z]{2}\.)?(archive|security)\.ubuntu\.com/|https?://rpms\.litespeedtech\.com/' "$f" || return 0 + grep -q -E 'https?://([a-z]{2}\.)?(archive|security)\.ubuntu\.com/|https?://rpms\.litespeedtech\.com/|https?://(deb|security)\.debian\.org/' "$f" || return 0 save "$f" + # Debian: debian-security first, and "debian" only as a whole path segment, + # so .../debian-security is never rewritten as /debian/. sed -i -E \ -e "s%https?://(archive|[a-z]{2}\.archive)\.ubuntu\.com/ubuntu/?%$C/ubuntu/%g" \ -e "s%https?://security\.ubuntu\.com/ubuntu/?%$C/ubuntu-security/%g" \ -e "s%https?://rpms\.litespeedtech\.com/%$C/litespeed/%g" \ + -e "s%https?://(deb|security)\.debian\.org/debian-security/?([[:space:]]|\$)%$C/debian-security/\\2%g" \ + -e "s%https?://security\.debian\.org/?([[:space:]]|\$)%$C/debian-security/\\1%g" \ + -e "s%https?://deb\.debian\.org/debian/?([[:space:]]|\$)%$C/debian/\\1%g" \ "$f" } +if [ "$MODE" = go-env ]; then + # Prints shell exports for `eval`. Without PKG_CACHE: nothing (Go defaults). + # GOPROXY "a|b" falls through on ANY error, so a dead cache costs one + # failed request, not the build. GOSUMDB stays the default sum.golang.org, + # reached directly: modules are still verified against go.sum + the sumdb. + [ -n "$C" ] || { echo "pkg-cache-repos(go-env): PKG_CACHE not set - default GOPROXY" >&2; exit 0; } + start_session + make_bundle + printf "export GOPROXY='%s/goproxy|https://proxy.golang.org|direct' SSL_CERT_FILE='%s'\n" "$C" "$STATE/ca-bundle.pem" + echo "pkg-cache-repos(go-env): GOPROXY via $C/goproxy (fallback proxy.golang.org)" >&2 + exit 0 +fi + +if [ "$MODE" = nodesource ]; then + # Replaces `curl https://rpm.nodesource.com/setup_N.x | bash` (unverified + # remote script): writes the same nodesource-nodejs repo (origin URL, so the + # shipped image keeps a normal repo file) with gpgcheck=1 and the signing key + # committed in keys/pkg-cache/; with PKG_CACHE it is then rewritten like any + # other repo and `restore` puts the origin version back. + major="${1:?usage: pkg-cache-repos.sh nodesource MAJOR}" + [[ "$major" =~ ^[0-9]{1,3}$ ]] || die "bad Node major '$major'" + key="$SHARE/RPM-GPG-KEY-nodesource" + [ -s "$key" ] || die "missing $key" + rpm --import "$key" || die "cannot import $key" + rm -f /etc/yum.repos.d/nodesource*.repo + f=/etc/yum.repos.d/nodesource-nodejs.repo + printf '%s\n' "[nodesource-nodejs]" \ + "name=Node.js Packages for Linux RPM based distros - \$basearch" \ + "baseurl=https://rpm.nodesource.com/pub_${major}.x/nodistro/nodejs/\$basearch" \ + "priority=9" "enabled=1" "gpgcheck=1" \ + "gpgkey=file://$key" "module_hotfixes=1" > "$f" + echo "pkg-cache-repos: wrote $f (Node ${major}.x, key $(basename "$key"))" + if [ -n "$C" ]; then + start_session + make_bundle + rewrite_rpm "$f" + grep -h '^baseurl=' "$f" | sed 's/^/ /' + fi + exit 0 +fi + case "$MODE" in rpm) start_session - cat /etc/pki/tls/certs/ca-bundle.crt "$SHARE/ca.crt" > "$STATE/ca-bundle.pem" + make_bundle for f in /etc/yum.repos.d/*.repo; do [ -f "$f" ] && rewrite_rpm "$f"; done echo "pkg-cache-repos(rpm): repos now via $C:" grep -h '^baseurl=' /etc/yum.repos.d/*.repo | grep -F "$C/" | sort -u | sed 's/^/ /' @@ -221,7 +288,9 @@ case "$MODE" in [ -f "$f" ] && rewrite_apt "$f" done echo "pkg-cache-repos(apt): sources now via $C:" - grep -h -E '^(deb|URIs:)' /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null | grep -F "$C/" | sort -u | sed 's/^/ /' + # (|| true: Debian 13 images have no /etc/apt/sources.list; grep's exit 2 + # for the missing file must not fail the build under pipefail) + { grep -h -E '^(deb|URIs:)' /etc/apt/sources.list /etc/apt/sources.list.d/* 2>/dev/null || true; } | grep -F "$C/" | sort -u | sed 's/^/ /' || true ;; *) die "unknown mode '$MODE'" ;; esac