ci: enable PKG_CACHE via a TLS probe that falls back to upstream; host repo tool
- build-push.yaml: Build-and-Push, Build-FPM-Images, Build-Shared-httpd and Build-Shared-OLS probe https://192.168.1.40 and :5000 with the pinned pkg-cache CA (5 s timeouts). When the cache answers they pass PKG_CACHE and configure the BuildKit Docker Hub mirror (ca=). When it doesn't, they build from upstream exactly as before and emit a warning. Fallback measured from the runner bridge: refused <1 s, silent drop 10 s. - scripts/host/pkgcache-host-repos.sh: apply/revert/status for WHP hosts. Each rewritten repo uses baseurl=<cache> <origin> as the real fallback; mirrorlist/metalink stay commented because measured dnf ignores baseurl when they are present. Keeps gpgcheck=1 / repo_gpgcheck=1 and backs up every file. Tested on 192.168.1.148, including cache-down and silent-drop fallback. - docs: runner firewall rule (applied), measured fallback results. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
18f65c6d84
commit
2b9e7f302c
3 files changed
+301
-51
No files matched your search
+69
-51
@@ -95,72 +95,90 @@ Remaining risks:
|
||||
- It passes the results as `EXPECT_OPENSSL_MIN` / `EXPECT_HTTPD_MIN` / `EXPECT_IMAGEMAGICK_MIN`.
|
||||
- The Dockerfile asserts `rpm -q --qf '%{EVR}'` is not older (`rpm --eval "%{lua: print(rpm.vercmp('A','B'))}"`).
|
||||
|
||||
## CI integration plan (NOT applied — needs Josh)
|
||||
## CI integration (APPLIED 2026-10-21)
|
||||
|
||||
1. **Network (blocker).** The ephemeral runners are NATed, and `iptables FORWARD` **drops all RFC1918 destinations**, including 192.168.1.40:
|
||||
1. **Runner network.** Ephemeral runners are NATed and `iptables FORWARD` drops
|
||||
all RFC1918 destinations:
|
||||
- `eph-pve-*` on `pve`, vmbr2, 10.99.98.0/24;
|
||||
- `eph-pve3-*` on `pve3`, vmbr1, 10.99.99.0/24.
|
||||
|
||||
A narrow exception is needed, placed before the DROP lines:
|
||||
One rule per node now sits **above** those DROPs. It is persistent through a
|
||||
`post-up`/`post-down` pair placed before the first DROP line in
|
||||
`/etc/network/interfaces.d/vmbr2-ci-isolated` (pve) and
|
||||
`/etc/network/interfaces.d/vmbr1-ci-isolated` (pve3). The Proxmox firewall is
|
||||
disabled on both nodes, so its reloads do not touch these chains. Backups:
|
||||
`/root/vmbr{2,1}-ci-isolated.bak-pkgcache-20261021`.
|
||||
```
|
||||
# pve, /etc/network/interfaces.d/ vmbr2 stanza, BEFORE the 192.168.0.0/16 DROP:
|
||||
post-up iptables -A FORWARD -i vmbr2 -o vmbr0 -d 192.168.1.40 -p tcp -m multiport --dports 443,5000 -j ACCEPT
|
||||
post-down iptables -D FORWARD -i vmbr2 -o vmbr0 -d 192.168.1.40 -p tcp -m multiport --dports 443,5000 -j ACCEPT
|
||||
# live: iptables -I FORWARD 1 -i vmbr2 -o vmbr0 -d 192.168.1.40 -p tcp -m multiport --dports 443,5000 -j ACCEPT
|
||||
# pve3: the same with vmbr1
|
||||
iptables -I FORWARD 1 -i vmbr2 -o vmbr0 -d 192.168.1.40/32 -p tcp -m multiport --dports 443,5000 \
|
||||
-m comment --comment pkg-cache-ct130 -j ACCEPT
|
||||
```
|
||||
`gitea-runner.internal.cloud-hosting.io` resolves to 10.10.14.20, which is not on this LAN. Whether it can reach the cache is untested.
|
||||
2. **Probe with the CA, then opt in.** Keep `${{ }}` out of `run:`. The probe fails when the cache is down **or its certificate is expired or untrusted**, and the build then uses upstream.
|
||||
```yaml
|
||||
- name: Probe package cache
|
||||
id: pkgcache
|
||||
run: |
|
||||
if curl -fsS --max-time 5 --cacert keys/pkg-cache/ca.crt -o /dev/null https://192.168.1.40/ ; then
|
||||
echo "url=https://192.168.1.40" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "url=" >> "$GITHUB_OUTPUT"; echo "::warning::pkg-cache unreachable - building from upstream"
|
||||
fi
|
||||
# docker/build-push-action `build-args:` add PKG_CACHE=${{ steps.pkgcache.outputs.url }}
|
||||
```
|
||||
3. **Docker Hub mirror for BuildKit**, over TLS and without `insecure`. `setup-buildx-action` uses the docker-container driver, which **ignores dockerd's `registry-mirrors`**. buildx copies the `ca` file into the builder.
|
||||
```yaml
|
||||
- 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 = ["keys/pkg-cache/ca.crt"]
|
||||
```
|
||||
The exact equivalent (absolute `ca` path) was measured on 192.168.1.193. BuildKit falls back to Docker Hub when the mirror is down; this was measured too, with the registry stopped. The relative `ca` path inside a workflow is **untested**; use `${{ github.workspace }}/keys/pkg-cache/ca.crt` if it doesn't resolve.
|
||||
4. **dockerd clients** (plain `docker pull`). Untested, because it needs a daemon restart. Use TLS, no `insecure-registries`:
|
||||
Verified from throwaway CTs on vmbr2 and vmbr1:
|
||||
- :443 and :5000 work with TLS verified;
|
||||
- CT130 :22/:80, pve :8006, .193 :22, the router's :53 and 10.10.14.20 stay **blocked**;
|
||||
- internet access is unchanged.
|
||||
|
||||
The LAN runner VM *Git-Builders* (VM103, 192.168.1.126 on vmbr0) needs no
|
||||
rule. `gitea-runner.internal.cloud-hosting.io` resolves to 10.10.14.20, which
|
||||
is unroutable from both the LAN and the DC (stale record?).
|
||||
2. **Workflow** (`.gitea/workflows/build-push.yaml`). Jobs Build-and-Push,
|
||||
Build-FPM-Images, Build-Shared-httpd and Build-Shared-OLS run a
|
||||
**"Probe package cache"** step. It curls `https://192.168.1.40/` and
|
||||
`https://192.168.1.40:5000/v2/` with `--cacert keys/pkg-cache/ca.crt
|
||||
--max-time 5`.
|
||||
- If both answer: `PKG_CACHE=https://192.168.1.40` is passed as a build arg,
|
||||
and buildx is set up with the Docker Hub mirror (`buildkitd-config-inline`,
|
||||
`ca=`).
|
||||
- If either fails: that output is empty, the build uses upstream repos /
|
||||
Docker Hub, and a `::warning::` is emitted.
|
||||
|
||||
Litespeed and lsphp jobs are unchanged (their Dockerfiles are not wired).
|
||||
**Fail-back measured** with the workflow's own step code, from a runner-bridge CT:
|
||||
|
||||
| cache state | probe result | time |
|
||||
|---|---|---|
|
||||
| up | url + mirror set | <1 s |
|
||||
| nginx + registry stopped | both empty, warning | <1 s (connection refused) |
|
||||
| silently dropped | both empty, warning | 10 s (2 x 5 s) |
|
||||
|
||||
Mid-build cache loss is also covered: dnf has the origin as a 2nd baseurl, and
|
||||
BuildKit falls back from the mirror to Docker Hub (measured with the registry stopped).
|
||||
3. **dockerd clients** (plain `docker pull`). Untested, because it needs a daemon restart.
|
||||
Use TLS, no `insecure-registries`:
|
||||
```
|
||||
install -D -m0644 keys/pkg-cache/ca.crt /etc/docker/certs.d/192.168.1.40:5000/ca.crt
|
||||
# /etc/docker/daemon.json: { "registry-mirrors": ["https://192.168.1.40:5000"] } then restart docker
|
||||
```
|
||||
|
||||
## WHP hosts (plan only)
|
||||
## WHP hosts (`scripts/host/pkgcache-host-repos.sh`)
|
||||
|
||||
The production hosts (whp01, whp02, sdbees) are in the datacenter and **cannot reach 192.168.1.40**. Only LAN hosts can, e.g. 192.168.1.148. Options for Josh:
|
||||
- (a) a second identical cache in the datacenter, with its own name-constrained CA;
|
||||
- (b) expose this one over NetBird;
|
||||
- (c) LAN hosts only.
|
||||
`pkgcache-host-repos.sh apply https://192.168.1.40` does the following:
|
||||
- backs up every `/etc/yum.repos.d/*.repo` to `/root/pkgcache-repo-backup-<UTC>/`;
|
||||
- installs the name-constrained CA as a system anchor;
|
||||
- rewrites the AlmaLinux, EPEL and Remi sections to `baseurl=<cache>/... <origin>/...`;
|
||||
- comments out mirrorlist/metalink in those sections;
|
||||
- sets `gpgcheck=1`, plus `repo_gpgcheck=1` for AlmaLinux and Remi.
|
||||
|
||||
For a host that can reach a cache:
|
||||
- install the CA at `/etc/pki/pkg-cache/ca.crt`;
|
||||
- create a bundle (`cat /etc/pki/tls/certs/ca-bundle.crt /etc/pki/pkg-cache/ca.crt > /etc/pki/pkg-cache/bundle.pem`);
|
||||
- in each repo section, keep the origin as the second URL:
|
||||
`revert [DIR]` restores byte-identical files (verified on AL9 and AL10) and
|
||||
removes the anchor. `status` shows the current state.
|
||||
|
||||
```ini
|
||||
[remi-safe]
|
||||
baseurl=https://<cache>/remi/enterprise/$releasever_major/safe/$basearch/ https://rpms.remirepo.net/enterprise/$releasever_major/safe/$basearch/
|
||||
#mirrorlist=...
|
||||
sslcacert=/etc/pki/pkg-cache/bundle.pem
|
||||
gpgcheck=1
|
||||
repo_gpgcheck=1 # Remi + AlmaLinux sign repomd; EPEL does not
|
||||
```
|
||||
**Why not "cache first, mirrorlist as fallback":** measured on AL10, with
|
||||
mirrorlist/metalink left in place dnf **ignores `baseurl` completely** (0 cache
|
||||
requests). The origin as second baseurl is the fallback dnf really performs.
|
||||
|
||||
Hosts have no php.net gate. On a security-release day, run `pkgcache-refresh` on the cache and check `dnf info php-cli` against php.net.
|
||||
**Measured on 192.168.1.148** (AlmaLinux 10.2), 2026-10-21:
|
||||
|
||||
| test | result |
|
||||
|---|---|
|
||||
| baseline `dnf makecache --refresh` (original mirrorlists) | rc=0, **1209 s** |
|
||||
| via cache `makecache --refresh` | rc=0, **22 s** (2nd run, non-interactive: 2 s) |
|
||||
| via cache `dnf download php-cli` | rc=0, 1 s |
|
||||
| `dnf -y --downloadonly update php-cli` | "Nothing to do" (already 8.5.12) |
|
||||
| cache **down** (nginx stopped): makecache --refresh / download | rc=0 **16 s** / rc=0 24 s, served by origin |
|
||||
| cache **silently dropped** (iptables DROP): makecache --refresh / download | rc=0 **225 s** / rc=0 59 s (dnf's 30 s connect timeout per attempt) |
|
||||
|
||||
**Datacenter hosts** (whp01, whp02, sdbees, TrueSelfCA) cannot reach
|
||||
192.168.1.40 and are **not configured**. That needs CT130 on NetBird (see the
|
||||
report: setup key / route pending).
|
||||
|
||||
## Operating the cache (CT130)
|
||||
|
||||
|
||||
Reference in new issue
Block a user