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:
shadowdaoandClaude Opus 5.5 committed 2026-10-21 16:46:03 -07:00
1 parent 18f65c6d84
commit 2b9e7f302c
3 files changed
+301 -51

No files matched your search

+107
View File
@@ -0,0 +1,107 @@
#!/bin/bash
## pkgcache-host-repos.sh — point a WHP HOST's dnf repos (AlmaLinux 9/10 BaseOS/
## AppStream/CRB/extras/..., EPEL, Remi) at the internal package cache, with the
## ORIGIN as automatic fallback. Not used by image builds (see pkg-cache-repos.sh).
##
## pkgcache-host-repos.sh apply https://192.168.1.40 # backs up, then rewrites
## pkgcache-host-repos.sh status
## pkgcache-host-repos.sh revert [BACKUP_DIR] # default: newest backup
##
## Fallback design (measured 2026-10-21 on AlmaLinux 10.2):
## * each rewritten section gets baseurl=<cache>/<prefix>/<path> <origin>/<path>
## — dnf/librepo treats the second URL as a failover mirror.
## * the section's mirrorlist=/metalink= is COMMENTED OUT. Measured: with a
## mirrorlist/metalink present dnf ignores baseurl entirely (0 cache requests),
## so "cache first, mirrorlist as fallback" is not something dnf can do.
## * gpgcheck=1 everywhere; repo_gpgcheck=1 for AlmaLinux + Remi (both sign
## repomd.xml); EPEL has no repomd signature and relies on HTTPS end to end.
## * trust: the name-constrained pkg-cache CA is installed as a system anchor
## (/etc/pki/ca-trust/source/anchors/pkg-cache-ca.crt). Its nameConstraints
## (IP 192.168.1.40, pkg-cache.* DNS) are enforced by OpenSSL — a cert it
## signs for any other name fails ("permitted subtree violation").
## Every changed file is copied to /root/pkgcache-repo-backup-<UTC>/ first.
set -euo pipefail
MODE="${1:-status}"
CA_SRC="$(dirname "$0")/pkg-cache-ca.crt"
ANCHOR=/etc/pki/ca-trust/source/anchors/pkg-cache-ca.crt
die() { echo "FATAL(pkgcache-host-repos): $*" >&2; exit 1; }
same() { [ "$(sha256sum < "$1")" = "$(sha256sum < "$2")" ]; } # cmp/diffutils is not on minimal hosts
rewrite() { # $1 file $2 cache url
python3 - "$1" "$2" <<'PY'
import re, sys
f, C = sys.argv[1], sys.argv[2].rstrip('/')
maps = [ # regex on a (commented or live) baseurl, cache prefix, origin base, repomd signed?
(r'^#?\s*baseurl\s*=\s*https?://repo\.almalinux\.org/almalinux/(\S+)', 'almalinux', 'https://repo.almalinux.org/almalinux/', True),
(r'^#?\s*baseurl\s*=\s*https?://download\.example/pub/epel/(\S+)', 'epel', 'https://dl.fedoraproject.org/pub/epel/', False),
(r'^#?\s*baseurl\s*=\s*https?://rpms\.remirepo\.net/(\S+)', 'remi', 'https://rpms.remirepo.net/', True),
]
out, sec = [], []
state = {'hit': False, 'sig': False}
def flush():
for x in sec:
if state['hit']:
if re.match(r'^(mirrorlist|metalink)\s*=', x):
x = '#' + x
elif re.match(r'^(gpgcheck|repo_gpgcheck)\s*=', x):
continue
out.append(x)
if state['hit']:
# keep blank-line layout: insert before trailing blanks
tail = []
while out and out[-1].strip() == '':
tail.append(out.pop())
out.append('gpgcheck=1')
if state['sig']:
out.append('repo_gpgcheck=1')
out.extend(tail)
sec.clear(); state['hit'] = state['sig'] = False
for line in open(f).read().split('\n'):
if line.startswith('['):
flush()
for rx, prefix, origin, sig in maps:
m = re.match(rx, line)
if m:
p = m.group(1)
line = f'baseurl={C}/{prefix}/{p} {origin}{p}'
state['hit'] = True; state['sig'] = state['sig'] or sig
break
sec.append(line)
flush()
open(f, 'w').write('\n'.join(out))
PY
}
case "$MODE" in
apply)
C="${2:?usage: apply https://<cache>}"; C="${C%/}"
case "$C" in https://*) ;; *) die "cache URL must be https://" ;; esac
[ -s "$CA_SRC" ] || die "missing $CA_SRC (copy keys/pkg-cache/ca.crt next to this script)"
B=/root/pkgcache-repo-backup-$(date -u +%Y%m%dT%H%M%SZ)
mkdir -p "$B/yum.repos.d"
cp -a /etc/yum.repos.d/*.repo "$B/yum.repos.d/"
[ -e "$ANCHOR" ] && cp -a "$ANCHOR" "$B/" || touch "$B/.no-anchor-before"
install -m 0644 "$CA_SRC" "$ANCHOR" && update-ca-trust
for f in /etc/yum.repos.d/almalinux*.repo /etc/yum.repos.d/epel*.repo /etc/yum.repos.d/remi*.repo; do
[ -f "$f" ] || continue
grep -q -E '^#?\s*baseurl\s*=\s*https?://(repo\.almalinux\.org|download\.example|rpms\.remirepo\.net)/' "$f" && rewrite "$f" "$C"
done
echo "backup: $B"
echo "changed files:"; for f in "$B"/yum.repos.d/*.repo; do same "$f" "/etc/yum.repos.d/$(basename "$f")" || echo " /etc/yum.repos.d/$(basename "$f")"; done
;;
revert)
B="${2:-$(ls -d /root/pkgcache-repo-backup-* 2>/dev/null | sort | tail -1)}"
[ -d "$B/yum.repos.d" ] || die "no backup dir ($B)"
for f in "$B"/yum.repos.d/*.repo; do same "$f" "/etc/yum.repos.d/$(basename "$f")" || { cp -a "$f" /etc/yum.repos.d/; echo "restored /etc/yum.repos.d/$(basename "$f")"; }; done
if [ -e "$B/.no-anchor-before" ]; then rm -f "$ANCHOR"; update-ca-trust; echo "removed $ANCHOR"; fi
dnf clean metadata -q || true
echo "reverted from $B"
;;
status)
grep -l -E '^baseurl=https://[^ ]+/(almalinux|epel|remi)/' /etc/yum.repos.d/*.repo 2>/dev/null | sed 's/^/via cache: /' || echo "no repo uses the cache"
ls -d /root/pkgcache-repo-backup-* 2>/dev/null | sed 's/^/backup: /' || true
[ -e "$ANCHOR" ] && echo "CA anchor: $ANCHOR" || echo "CA anchor: absent"
;;
*) die "usage: apply URL | status | revert [DIR]" ;;
esac