Skip to content

fix(app): ensureHTTPApp must not strict-decode interface-typed routes - #157

Merged
hsinatfootprintai merged 1 commit into
mainfrom
fix/caddy-ensure-http-app
May 13, 2026
Merged

fix(app): ensureHTTPApp must not strict-decode interface-typed routes#157
hsinatfootprintai merged 1 commit into
mainfrom
fix/caddy-ensure-http-app

Conversation

@hsinatfootprintai

Copy link
Copy Markdown
Contributor

Summary

  • ensureHTTPApp strict-decoded the Caddy /config/apps/http response into the typed CaddyHTTPApp. That type transitively contains Handle []CaddyHandler — an interface slice — which encoding/json cannot unmarshal into. On every daemon startup where Caddy already had a non-empty http app, the decode errored out, the code fell through to createHTTPApp's PUT, and Caddy returned 409 key already exists: http.
  • The daemon logged a warning and continued, but every subsequent Caddy update that depended on EnsureServerConfig having succeeded was silently lost.
  • Symptom observed live: a tunnel-promoted pool primary registered fine in the backend pool (tunnel-lab-primary-1 healthy in /v1/backends) but the new pool's TLS subject + srv0 route never landed, so its HTTPS endpoint failed the TLS handshake. Restarting the daemon did not recover, because the bug fires on every startup.

Fix: probe with map[string]json.RawMessage on servers rather than the strict typed decode. The path that actually mutates Caddy already uses untyped maps via getFullConfig / loadConfig, so we're just bringing the read path into alignment.

Added a regression test TestProxyManager_EnsureHTTPApp_AcceptsExistingConfigWithHandlers that fails on the old code (asserts no PUT is issued to /config/apps/http when the response contains a typed reverse_proxy handler).

Test plan

  • go test ./internal/app/ -run TestProxyManager -count=1 -v — all proxy tests pass including the new regression
  • Reproduces the live Failed to ensure Caddy server config: ... status 409 warning in observed prod logs
  • After merge + release, redeploy containarium daemon on containarium-jump-usw1-sentinel and verify containarium-lab.kafeido.app HTTPS returns the expected 404 (daemon's passthrough) instead of TLS handshake failure

ensureHTTPApp used to decode the GET /config/apps/http response into a
typed CaddyHTTPApp. That type transitively contains []CaddyHandler — an
interface slice — which encoding/json cannot unmarshal into. On every
daemon startup where Caddy already had a non-empty http app, the decode
errored out, the code fell through to createHTTPApp, the PUT 409'd
because the http app already existed, and the daemon logged a warning
and silently lost every subsequent Caddy update that depended on
EnsureServerConfig having succeeded.

Symptom in the wild: registering a tunnel-promoted pool primary added
the backend to /v1/backends but the corresponding TLS subject + srv0
route were never installed, so the new pool's HTTPS endpoint failed
the TLS handshake. Restarting the daemon didn't recover, because the
bug fires on every startup.

Fix: probe with map[string]json.RawMessage on `servers` instead of the
strict typed decode. Adds a regression test that asserts no spurious
PUT is issued when an existing http app has handler-shaped routes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hsinatfootprintai
hsinatfootprintai merged commit 3e6f0fb into main May 13, 2026
6 checks passed
pull Bot referenced this pull request in Spencerx/Containarium May 13, 2026
Ships the ensureHTTPApp interface-decode fix (#157) so the daemon can
reapply Caddy state on every startup — required for tunnel-promoted
pool primaries to register their TLS subject + srv0 route.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pull Bot referenced this pull request in Spencerx/Containarium May 13, 2026
…e failures

Captures two failure modes discovered while bringing up the `lab` pool
against the prod sentinel on 2026-05-13:

1. PROXY v2 framing mismatch when the primary daemon isn't started with
   `--proxy-protocol`. The sentinel writes a PROXY v2 frame before the
   TLS bytes (when its own `--proxy-protocol` is on); the primary's
   Caddy interprets the frame as TLS garbage and the handshake fails
   silently — neither side logs anything useful, and probing Caddy
   locally at 127.0.0.1:443 succeeds, which is what makes the trap
   confusing. Fix is the matching `--proxy-protocol
   --proxy-protocol-trusted=127.0.0.0/8` on the primary.

2. `ensureHTTPApp` interface-decode bug pre-v0.16.6 (#157), which
   showed up as a `Failed to ensure Caddy server config: ... 409 key
   already exists: http` warning on every daemon startup with an
   existing Caddy http app, and silently lost subsequent Caddy
   updates. Fixed by upgrade.

Also adds a cross-reference in `MULTI-POOL.md`'s "adding a new pool"
workflow so the matching `--proxy-protocol` flag lands in the canonical
runbook, not just in this troubleshooting section.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hsinatfootprintai
hsinatfootprintai deleted the fix/caddy-ensure-http-app branch May 14, 2026 03:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants