Add CA download to dashboard: GET /gateway/ca.crt + Gateway-panel button

When the gateway serves internal-CA HTTPS (gateway.tls=internal), other devices
must trust Caddy's root CA to accept *.lan HTTPS. Surface that from the dashboard
instead of hand-copying root.crt around.

- API: GET /gateway/ca.crt returns the public root cert (PEM) as a download,
  sourced from Caddy's admin API (/pki/ca/local — matches the running gateway)
  with an on-disk root.crt fallback. 404 unless tls=internal. GatewayInfo gains
  `tls` and `ca_fingerprint` (SHA-256, for out-of-band verification). The CA
  private key is never exposed — only the public root.
- UI: a "CA cert" button in the Gateway panel, shown only when tls=internal,
  linking to the download with the fingerprint in its tooltip.
- Tests for the tls=off defaults and the 404-without-internal-tls path; docs note
  the button + endpoint.
This commit is contained in:
2026-06-29 08:26:15 -07:00
parent 0d9c1ae6c2
commit fc5802d823
6 changed files with 116 additions and 5 deletions

View File

@@ -355,9 +355,13 @@ Two operational requirements:
lower the floor once: `net.ipv4.ip_unprivileged_port_start=80` (persist in
`/etc/sysctl.d/`). This beats `setcap`, which `NoNewPrivileges=true` would void.
- **Trust the local CA.** Run `caddy trust` on the gateway host, then distribute
`~/.local/share/caddy/pki/authorities/local/root.crt` to every other box's
system/browser trust store — `.lan` can't get a public cert, so clients trust
Caddy's root instead. (Firefox uses its own store; import it there too.)
the root CA to every other box's system/browser trust store — `.lan` can't get
a public cert, so clients trust Caddy's root instead. (Firefox uses its own
store; import it there too.) The dashboard's Gateway panel has a **CA cert**
download button (only shown when `tls: internal`), backed by
`GET /gateway/ca.crt` — the public root cert, sourced from Caddy's admin API,
with its SHA-256 shown for out-of-band verification. The on-disk copy is at
`~/.local/share/caddy/pki/authorities/local/root.crt`.
Routing only moves bytes — it does **not** supply the proxied app's own auth.
If a backend requires a token/credential (e.g. in the URL or a header), that