Add Caddy custom-domains deploy destination - #95
Conversation
Add a "caddy" destination alongside "custom" for the new Caddy-based custom-domains setup (elastic-infra ansible/vars/apps/custom-domains.yml). It uploads the same files as the old external nginx server, but rewrites the nginx SSI directives to Caddy template placeholders on upload, since Caddy's templates directive injects the request hostname and the proxy-product query parameters at serve time. The caddy-ssi transform fails the deploy if a build contains stray Go template delimiters or an SSI directive without a known replacement, so a bad build cannot turn into per-request 500s. Uploads go via a temporary file so the transformed bytes are what lands on the server, and the post-upload verification compares against the transformed content. A --caddy-only flag deploys exclusively to the new setup, leaving the CDN and the old external server untouched during the migration. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ApznmYeffWY6YojKXhEJDW
| ['<!--# echo var="proxy_hostname" default="" -->', '{{.Req.URL.Query.Get "hostname"}}'], | ||
| ['<!--# echo var="proxy_path" default="/simple" -->', '{{.Req.URL.Query.Get "path"}}'], |
There was a problem hiding this comment.
Caddy's templates uses text/template (no escaping) and Query.Get percent-decodes, whereas nginx's $arg_* does not — so these placeholders sit unescaped inside the JS string literal }(window,{},"…") in proxy.js, and ?hostname=%22%2balert(document.domain)%2b%22 renders as }(window,{},""+alert(document.domain)+""), executing attacker script on the customer's proxy domain (verified against Go's text/template). Wrapping both in Go's built-in js escaper keeps the decoding while making the value safe; the caddy-ssi unit test expectations need the same update.
| ['<!--# echo var="proxy_hostname" default="" -->', '{{.Req.URL.Query.Get "hostname"}}'], | |
| ['<!--# echo var="proxy_path" default="/simple" -->', '{{.Req.URL.Query.Get "path"}}'], | |
| ['', '{{js (.Req.URL.Query.Get "hostname")}}'], | |
| ['', '{{js (.Req.URL.Query.Get "path")}}'], |
|
SDLC label: |
Claude review checkpointReviewed commit |
Summary
Closes #96
Adds a
caddydeploy destination for the new Caddy-based custom-domains setup (see simpleanalytics/infrastructure#231,ansible/vars/apps/custom-domains.yml).customdestination): latest/events/latest.dev, SRIv<n>/files, light, proxy and auto-events, plus source maps.templatesdirective substitutes the request hostname ({{.Req.Host}}) and the proxy-producthostname/pathquery parameters at serve time.caddy-ssitransform fails the deploy if a build contains stray{{delimiters or an SSI directive without a known replacement, so a bad build cannot turn into per-request 500s.pnpm deploy --caddy-onlydeploys exclusively to the new setup, leaving the CDN and the old external server untouched during the migration.Follow-up
customdestination is selected, since Caddy needs no nginx config change for a new SRI version. The docs update still applies to Caddy-only SRI deploys; worth splitting the reminder once the old server is gone.Test plan
pnpm typecheckpnpm test:deploy(17 pass, 5 new)pnpm deploy --caddy-onlyand compare the preview against esapp05--caddy-onlydeploy, then runverify-custom-domains-parity.sh/verify-proxy-parity.shfrom elastic-infra🤖 Generated with Claude Code
https://claude.ai/code/session_01ApznmYeffWY6YojKXhEJDW
Security implications
Has security impact - described as: This PR is labeled
change: needs review, but Claude did not provide specific security implications. Reviewers should confirm the actual impact on security, privacy, customer/user data, system stability, or critical functionality.Testing
Not run by Claude.
Checklist