fix(cache): install CA anchors where every update-ca-trust family reads them

The Fedora and CentOS Stream workspace jobs failed TLS to pypi.org,
registry.npmjs.org and repo.packagist.org (CERTIFICATE_VERIFY_FAILED,
curl error 7). package-frontend/ca.sh put the frontend CA into the Arch
anchor dir /etc/ca-certificates/trust-source/anchors, which p11-kit on
the RedHat family never reads.

Probing for the anchor dir does not fix it: with-ca-trust.sh creates
both /etc/pki/ca-trust/source/anchors and
/etc/ca-certificates/trust-source/anchors on every update-ca-trust host,
so the probe in registry-ca.sh already picks the RedHat dir on a reused
Arch runner. Both scripts now install into both dirs, rebuild only when
one of them changed, and take CA_ROOT so unit tests run them against a
fake root.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5.5 committed 2026-10-15 23:45:53 +02:00
1 parent ec2a4b96f0
commit 2756fd56f1
7 files changed
+277 -23

No files matched your search

+21 -7
View File
@@ -1,15 +1,22 @@
#!/usr/bin/env bash
# Install the frontend CA into the runner's trust store. Idempotent.
# See docs/contributing/environment/cache.md.
# Param: CA_ROOT prefix for every absolute path below; empty in production.
set -eu
CA_SRC="/opt/package-frontend-ca.crt"
CA_ROOT="${CA_ROOT:-}"
CA_SRC="${CA_ROOT}/opt/package-frontend-ca.crt"
CA_NAME="infinito-package-frontend.crt"
if command -v update-ca-certificates >/dev/null 2>&1; then
CA_DST="/usr/local/share/ca-certificates/infinito-package-frontend.crt"
CA_DSTS=("${CA_ROOT}/usr/local/share/ca-certificates/${CA_NAME}")
CA_REBUILD=(update-ca-certificates)
elif command -v update-ca-trust >/dev/null 2>&1; then
CA_DST="/etc/ca-certificates/trust-source/anchors/infinito-package-frontend.crt"
# Exception: Arch and the RedHat family both run update-ca-trust but read different anchor dirs, and with-ca-trust.sh creates both dirs on both families, so the anchor goes into each instead of a probed one.
CA_DSTS=(
"${CA_ROOT}/etc/pki/ca-trust/source/anchors/${CA_NAME}"
"${CA_ROOT}/etc/ca-certificates/trust-source/anchors/${CA_NAME}"
)
CA_REBUILD=(update-ca-trust extract)
else
echo "[package-frontend-ca] no supported CA trust tool" >&2
@@ -21,11 +28,18 @@ if [ ! -s "${CA_SRC}" ]; then
exit 0
fi
if cmp -s "${CA_SRC}" "${CA_DST}" 2>/dev/null; then
changed=0
for dst in "${CA_DSTS[@]}"; do
if ! cmp -s "${CA_SRC}" "${dst}" 2>/dev/null; then
install -d -m 0755 "$(dirname "${dst}")"
install -m 0644 "${CA_SRC}" "${dst}"
changed=1
fi
done
if [ "${changed}" -eq 0 ]; then
exit 0
fi
install -d -m 0755 "$(dirname "${CA_DST}")"
install -m 0644 "${CA_SRC}" "${CA_DST}"
"${CA_REBUILD[@]}" >/dev/null 2>&1
echo "[package-frontend-ca] installed ${CA_DST}" >&2
echo "[package-frontend-ca] installed ${CA_DSTS[*]}" >&2
+22 -16
View File
@@ -19,25 +19,24 @@
# * Debian/Ubuntu: /usr/local/share/ca-certificates (name must end in .crt),
# rebuilt by `update-ca-certificates`.
# * Arch and Fedora/CentOS both rebuild with `update-ca-trust extract` but
# read different anchor dirs -- Arch has no /etc/pki, the RedHat family
# has no /etc/ca-certificates -- so the dir is probed, not assumed.
# read different anchor dirs, and with-ca-trust.sh creates both dirs on
# both families, so the anchor goes into each.
# Param: CA_ROOT prefix for every absolute path below; empty in production.
set -eu
CA_DIR="/opt/registry-cache-ca"
CA_ROOT="${CA_ROOT:-}"
CA_DIR="${CA_ROOT}/opt/registry-cache-ca"
CA_SRC="${CA_DIR}/ca.crt"
CA_NAME="infinito-registry-cache.crt"
if command -v update-ca-certificates >/dev/null 2>&1; then
CA_DST="/usr/local/share/ca-certificates/infinito-registry-cache.crt"
CA_DSTS=("${CA_ROOT}/usr/local/share/ca-certificates/${CA_NAME}")
CA_REBUILD=(update-ca-certificates)
elif command -v update-ca-trust >/dev/null 2>&1; then
if [ -d /etc/pki/ca-trust/source/anchors ]; then
CA_DST="/etc/pki/ca-trust/source/anchors/infinito-registry-cache.crt"
elif [ -d /etc/ca-certificates/trust-source/anchors ]; then
CA_DST="/etc/ca-certificates/trust-source/anchors/infinito-registry-cache.crt"
else
echo "[registry-cache-ca] update-ca-trust present but no known anchor dir (/etc/pki/ca-trust/source/anchors, /etc/ca-certificates/trust-source/anchors)" >&2
exit 1
fi
CA_DSTS=(
"${CA_ROOT}/etc/pki/ca-trust/source/anchors/${CA_NAME}"
"${CA_ROOT}/etc/ca-certificates/trust-source/anchors/${CA_NAME}"
)
CA_REBUILD=(update-ca-trust extract)
else
echo "[registry-cache-ca] no supported CA trust tool (update-ca-certificates or update-ca-trust)" >&2
@@ -56,11 +55,18 @@ if [ ! -s "${CA_SRC}" ]; then
exit 1
fi
if cmp -s "${CA_SRC}" "${CA_DST}" 2>/dev/null; then
changed=0
for dst in "${CA_DSTS[@]}"; do
if ! cmp -s "${CA_SRC}" "${dst}" 2>/dev/null; then
install -d -m 0755 "$(dirname "${dst}")"
install -m 0644 "${CA_SRC}" "${dst}"
changed=1
fi
done
if [ "${changed}" -eq 0 ]; then
exit 0
fi
install -d -m 0755 "$(dirname "${CA_DST}")"
install -m 0644 "${CA_SRC}" "${CA_DST}"
"${CA_REBUILD[@]}" >/dev/null
echo "[registry-cache-ca] installed ${CA_DST}" >&2
echo "[registry-cache-ca] installed ${CA_DSTS[*]}" >&2
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
@@ -0,0 +1,115 @@
from __future__ import annotations
import os
import shutil
import subprocess
import tempfile
import unittest
from pathlib import Path
from utils.cache.files import PROJECT_ROOT
SCRIPT = PROJECT_ROOT / "scripts" / "docker" / "cache" / "package-frontend" / "ca.sh"
NAME = "infinito-package-frontend.crt"
STUB = '#!/usr/bin/env bash\nprintf "%s\\n" "$0 $*" >>"${STUB_LOG}"\n'
PKI = "etc/pki/ca-trust/source/anchors"
ARCH = "etc/ca-certificates/trust-source/anchors"
TOOLS = ("bash", "cmp", "dirname", "install")
class TestPackageFrontendCa(unittest.TestCase):
def fake_root(self, tool: str, anchor_dirs: tuple[str, ...]) -> Path:
"""Build a fake root that offers one trust tool and a frontend CA.
Args:
tool: the trust rebuild command the fake system provides.
anchor_dirs: anchor directories that already exist in the fake root.
Returns:
The fake root.
"""
root = Path(self.enterContext(tempfile.TemporaryDirectory()))
(root / "opt").mkdir()
(root / "opt" / "package-frontend-ca.crt").write_text("CA\n", encoding="utf-8")
for anchor in anchor_dirs:
(root / anchor).mkdir(parents=True)
(root / "bin").mkdir()
for needed in TOOLS:
(root / "bin" / needed).symlink_to(shutil.which(needed))
(root / "bin" / tool).write_text(STUB, encoding="utf-8")
(root / "bin" / tool).chmod(0o755)
return root
def run_script(self, root: Path) -> subprocess.CompletedProcess:
"""Run ca.sh against a fake root.
Args:
root: fake root from ``fake_root``.
Returns:
The finished process.
"""
env = {k: v for k, v in os.environ.items() if k not in ("BASH_ENV", "CA_ROOT")}
env.update(
CA_ROOT=str(root),
PATH=str(root / "bin"),
STUB_LOG=str(root / "stub.log"),
)
return subprocess.run(
[str(root / "bin" / "bash"), str(SCRIPT)],
env=env,
capture_output=True,
text=True,
check=False,
)
def rebuilds(self, root: Path) -> int:
log = root / "stub.log"
if not log.exists():
return 0
text = log.read_text(encoding="utf-8") # nocheck: cache-read per-run tempdir
return len(text.splitlines())
def test_the_redhat_family_gets_the_pki_anchor(self) -> None:
root = self.fake_root("update-ca-trust", (PKI,))
done = self.run_script(root)
self.assertEqual(done.returncode, 0, done.stderr)
self.assertTrue((root / PKI / NAME).is_file())
self.assertEqual(self.rebuilds(root), 1)
def test_arch_with_both_anchor_dirs_gets_its_trust_source_anchor(self) -> None:
root = self.fake_root("update-ca-trust", (PKI, ARCH))
done = self.run_script(root)
self.assertEqual(done.returncode, 0, done.stderr)
self.assertTrue((root / ARCH / NAME).is_file())
def test_a_fresh_update_ca_trust_root_gets_both_anchors(self) -> None:
root = self.fake_root("update-ca-trust", ())
done = self.run_script(root)
self.assertEqual(done.returncode, 0, done.stderr)
self.assertTrue((root / PKI / NAME).is_file())
self.assertTrue((root / ARCH / NAME).is_file())
def test_debian_uses_update_ca_certificates(self) -> None:
root = self.fake_root("update-ca-certificates", ())
done = self.run_script(root)
self.assertEqual(done.returncode, 0, done.stderr)
self.assertTrue((root / "usr/local/share/ca-certificates" / NAME).is_file())
self.assertFalse((root / "etc").exists())
def test_a_second_run_does_not_rebuild(self) -> None:
root = self.fake_root("update-ca-trust", (PKI,))
self.run_script(root)
done = self.run_script(root)
self.assertEqual(done.returncode, 0, done.stderr)
self.assertEqual(self.rebuilds(root), 1)
def test_no_trust_tool_fails_loudly(self) -> None:
root = self.fake_root("unrelated-tool", ())
done = self.run_script(root)
self.assertNotEqual(done.returncode, 0)
self.assertFalse(any(root.glob(f"**/{NAME}")))
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,119 @@
from __future__ import annotations
import os
import shutil
import subprocess
import tempfile
import unittest
from pathlib import Path
from utils.cache.files import PROJECT_ROOT
SCRIPT = PROJECT_ROOT / "scripts" / "docker" / "cache" / "registry-ca.sh"
NAME = "infinito-registry-cache.crt"
STUB = '#!/usr/bin/env bash\nprintf "%s\\n" "$0 $*" >>"${STUB_LOG}"\n'
PKI = "etc/pki/ca-trust/source/anchors"
ARCH = "etc/ca-certificates/trust-source/anchors"
TOOLS = ("bash", "cmp", "dirname", "install")
class TestRegistryCacheCa(unittest.TestCase):
def fake_root(
self, tool: str, anchor_dirs: tuple[str, ...], ca: str | None
) -> Path:
"""Build a fake root that offers one trust tool.
Args:
tool: the trust rebuild command the fake system provides.
anchor_dirs: anchor directories that already exist in the fake root.
ca: content of the mounted ``ca.crt``; ``None`` leaves the CA dir unmounted.
Returns:
The fake root.
"""
root = Path(self.enterContext(tempfile.TemporaryDirectory()))
if ca is not None:
(root / "opt" / "registry-cache-ca").mkdir(parents=True)
(root / "opt" / "registry-cache-ca" / "ca.crt").write_text(
ca, encoding="utf-8"
)
for anchor in anchor_dirs:
(root / anchor).mkdir(parents=True)
(root / "bin").mkdir()
for needed in TOOLS:
(root / "bin" / needed).symlink_to(shutil.which(needed))
(root / "bin" / tool).write_text(STUB, encoding="utf-8")
(root / "bin" / tool).chmod(0o755)
return root
def run_script(self, root: Path) -> subprocess.CompletedProcess:
"""Run registry-ca.sh against a fake root.
Args:
root: fake root from ``fake_root``.
Returns:
The finished process.
"""
env = {k: v for k, v in os.environ.items() if k not in ("BASH_ENV", "CA_ROOT")}
env.update(
CA_ROOT=str(root),
PATH=str(root / "bin"),
STUB_LOG=str(root / "stub.log"),
)
return subprocess.run(
[str(root / "bin" / "bash"), str(SCRIPT)],
env=env,
capture_output=True,
text=True,
check=False,
)
def rebuilds(self, root: Path) -> int:
log = root / "stub.log"
if not log.exists():
return 0
text = log.read_text(encoding="utf-8") # nocheck: cache-read per-run tempdir
return len(text.splitlines())
def test_the_redhat_family_gets_the_pki_anchor(self) -> None:
root = self.fake_root("update-ca-trust", (PKI,), "CA\n")
done = self.run_script(root)
self.assertEqual(done.returncode, 0, done.stderr)
self.assertTrue((root / PKI / NAME).is_file())
self.assertEqual(self.rebuilds(root), 1)
def test_arch_with_both_anchor_dirs_gets_its_trust_source_anchor(self) -> None:
root = self.fake_root("update-ca-trust", (PKI, ARCH), "CA\n")
done = self.run_script(root)
self.assertEqual(done.returncode, 0, done.stderr)
self.assertTrue((root / ARCH / NAME).is_file())
def test_debian_uses_update_ca_certificates(self) -> None:
root = self.fake_root("update-ca-certificates", (), "CA\n")
done = self.run_script(root)
self.assertEqual(done.returncode, 0, done.stderr)
self.assertTrue((root / "usr/local/share/ca-certificates" / NAME).is_file())
def test_a_second_run_does_not_rebuild(self) -> None:
root = self.fake_root("update-ca-trust", (ARCH,), "CA\n")
self.run_script(root)
done = self.run_script(root)
self.assertEqual(done.returncode, 0, done.stderr)
self.assertEqual(self.rebuilds(root), 1)
def test_an_unmounted_ca_dir_is_skipped(self) -> None:
root = self.fake_root("update-ca-trust", (PKI,), None)
done = self.run_script(root)
self.assertEqual(done.returncode, 0, done.stderr)
self.assertEqual(self.rebuilds(root), 0)
def test_a_mounted_dir_without_ca_is_refused(self) -> None:
root = self.fake_root("update-ca-trust", (PKI,), "")
done = self.run_script(root)
self.assertNotEqual(done.returncode, 0)
self.assertFalse(any(root.glob(f"**/{NAME}")))
if __name__ == "__main__":
unittest.main()