feat(caddy): serve Caddy root CA at /ca.crt for guest trust

Air-gap mirror uses tls internal; guests must trust Caddy's local CA. Serve it
at http://<domain>/ca.crt from a world-readable mount (the live pki dir is
root-only 0600). build-stack --up mirror|full exports the CA after bring-up;
landing page links it. Pre-creates the mount target to avoid a dir bind.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-24 05:32:31 +02:00
parent b277fc6268
commit 390f8c78af
6 changed files with 44 additions and 0 deletions

View File

@@ -109,7 +109,32 @@ up() {
log "PHASE up (mode=$mode)"
have_script render-config.sh
tooling/render-config.sh # cheap; ensures configs match current .env
# Static mode mounts ./caddy/caddy-root-ca.crt as a file at /srv/ca-pub/ca.crt.
# Pre-create it (empty) so Docker doesn't make a directory at that path; the
# real cert is exported below once Caddy's local CA exists.
case "$mode" in
static|full) [ -e caddy/caddy-root-ca.crt ] || : > caddy/caddy-root-ca.crt ;;
esac
"${COMPOSE[@]}" "${files[@]}" up -d
# Mirror/full provision Caddy's local CA — export it so guests can trust the
# HTTPS asset mirror (served at http://${BASE_DOMAIN}/ca.crt in static/full).
case "$mode" in
mirror|full)
local i
for i in 1 2 3 4 5; do
docker exec caddy cat /data/caddy/pki/authorities/local/root.crt \
> caddy/caddy-root-ca.crt 2>/dev/null && break
sleep 1
done
[ -s caddy/caddy-root-ca.crt ] \
&& echo " exported Caddy CA → caddy/caddy-root-ca.crt (served at /ca.crt)" \
|| warn "Caddy CA not ready; export later: docker exec caddy cat /data/caddy/pki/authorities/local/root.crt > caddy/caddy-root-ca.crt"
;;
esac
log "stack up (mode=$mode). apex: http://${BASE_DOMAIN} auth: http://auth.${BASE_DOMAIN}"
}