fix: use Caddy internal CA when ACME_EMAIL is not set

DOMAIN_SUFFIX=local (or any non-localhost LAN name) caused a TLS handshake
failure because Caddy attempted an ACME challenge that can never succeed for
private domains.

- Caddyfile: tls {$ACME_EMAIL:internal} — falls back to Caddy's built-in CA
  when ACME_EMAIL is absent, uses Let's Encrypt when it is set.
- start.sh: ACME_EMAIL is now optional; missing it prints a warning instead
  of aborting, so local/LAN setups work without an email address.

To trust the self-signed cert in a browser run: caddy trust

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
This commit is contained in:
Claude 2026-03-25 22:09:00 +00:00
parent 60f5df52f7
commit 73ea7320fd
No known key found for this signature in database
2 changed files with 6 additions and 37 deletions

View file

@ -20,45 +20,10 @@ if [ -z "$DOMAIN_SUFFIX" ] || [ "$DOMAIN_SUFFIX" = "localhost" ]; then
fi
if [ -z "$ACME_EMAIL" ]; then
echo "ERROR: Set ACME_EMAIL in infra/.env (required for Let's Encrypt)"
exit 1
echo "[hiy] ACME_EMAIL not set — Caddy will use its internal CA (self-signed)."
echo "[hiy] For a public domain with Let's Encrypt, set ACME_EMAIL in infra/.env"
fi
# ── Generate production caddy.json ─────────────────────────────────────────────
# Writes TLS-enabled config using Let's Encrypt (no Cloudflare required).
# Caddy will use the HTTP-01 challenge (port 80) or TLS-ALPN-01 (port 443).
cat > "$SCRIPT_DIR/../proxy/caddy.json" <<EOF
{
"admin": { "listen": "0.0.0.0:2019" },
"apps": {
"tls": {
"automation": {
"policies": [{
"subjects": ["${DOMAIN_SUFFIX}"],
"issuers": [{"module": "acme", "email": "${ACME_EMAIL}"}]
}]
}
},
"http": {
"servers": {
"hiy": {
"listen": [":80", ":443"],
"automatic_https": {},
"routes": [
{
"match": [{"host": ["${DOMAIN_SUFFIX}"]}],
"handle": [{"handler": "reverse_proxy", "upstreams": [{"dial": "server:3000"}]}]
}
]
}
}
}
}
}
EOF
echo "[hiy] Generated proxy/caddy.json for ${DOMAIN_SUFFIX}"
# ── Ensure cgroup swap accounting is enabled (required by runc/Podman) ────────
# runc always writes memory.swap.max when the memory cgroup controller is
# present. On Raspberry Pi OS swap accounting is disabled by default, so that

View file

@ -23,7 +23,11 @@
}
# HIY dashboard — served at your root domain.
# TLS behaviour:
# ACME_EMAIL set → Caddy requests a Let's Encrypt cert (production)
# ACME_EMAIL unset → Caddy uses its built-in internal CA (local / LAN domains)
{$DOMAIN_SUFFIX:localhost} {
tls {$ACME_EMAIL:internal}
reverse_proxy server:3000
}