coingate
All checks were successful
Deploy / build-deploy (push) Successful in 3m10s

This commit is contained in:
Jorijn van der Graaf 2026-08-13 23:34:19 +02:00
commit 70668af8f5
20 changed files with 2354 additions and 1048 deletions

View file

@ -18,6 +18,46 @@ catcrafts.net {
Referrer-Policy "strict-origin-when-cross-origin"
X-Content-Type-Options "nosniff"
-Server
# HSTS. Caddy redirects http->https but does NOT send this header on
# its own, so without it a first visit over http is still interceptable
# and every later one is only as safe as the redirect. A shop taking
# card payments should not be relying on a redirect.
#
# includeSubDomains commits EVERY catcrafts.net subdomain to HTTPS —
# www and forgejo are both on TLS here, so it holds. Drop that token if
# a subdomain ever has to serve plaintext. `preload` is deliberately
# NOT set: submission to the browser preload list is months to undo,
# and it should be a decision, not a side effect of this file.
Strict-Transport-Security "max-age=31536000; includeSubDomains"
# CSP. Defence in depth rather than the primary control — markup is
# built through Catcrafts.Shared:Html, where escaping is enforced by
# the type system and forgetting is a compile error. What this adds is
# the damage limitation that escaping cannot provide:
#
# form-action 'self' the checkout form cannot be retargeted at
# another origin — the directive that matters
# most on a page that collects an address
# frame-ancestors no clickjacking the buy button
# base-uri 'none' an injected <base> cannot re-point every
# relative script src on the page
# object-src 'none' no plugin content, ever
#
# 'unsafe-inline' in script-src is a known and bounded compromise: the
# geo price hint (kGeoPriceHintScript) must run before first paint to
# set a class on <html> without a flash, so it is inline by design.
# Removing it means hashing that constant here and re-hashing on every
# edit — silently breaking the hint when someone forgets. To tighten
# this properly, move the script to a real file and give it a nonce.
# 'wasm-unsafe-eval' is what the WASM runtime needs to compile the
# module; it does not enable eval() for JavaScript.
#
# img-src and media-src allow https: because a post whose media mirror
# failed still points at the source instance's URL (see Media::Describe)
# — locking those to 'self' would blank exactly the pictures a post is
# talking about.
Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; media-src 'self' https:; font-src 'self'; connect-src 'self'; form-action 'self'; frame-ancestors 'none'; base-uri 'none'; object-src 'none'"
}
# ── cross-origin isolation, scoped ────────────────────────────────────
@ -110,6 +150,18 @@ catcrafts.net {
# Pages, /feed.xml, /sitemap.xml and later /api/*. The backend sets its own
# Cache-Control and returns real status codes — a 404 for an unknown path and
# a 301 for the retired /blog URLs, which a client-side router cannot do.
# Checkout rate limiting is per-peer, and this block is what makes that
# possible: reverse_proxy APPENDS the real client address to
# X-Forwarded-For, and the backend reads the rightmost entry (see
# ClientAddressFromForwarded — the leftmost is whatever the client claimed).
# That is only sound while nothing but Caddy can reach 8081, which is why
# the backend binds loopback and why the warning at the top of this file
# says not to expose the port.
#
# Caddy's own rate_limit directive is a third-party module and is NOT in a
# standard build — adding it here without rebuilding Caddy stops the server
# from starting. If volume ever justifies limiting at the edge, build Caddy
# with github.com/mholt/caddy-ratelimit first.
handle {
reverse_proxy 127.0.0.1:8081 {
health_uri /api/healthz