82 lines
3.0 KiB
Caddyfile
82 lines
3.0 KiB
Caddyfile
# Clusev reverse proxy — the panel domain is changeable FROM THE DASHBOARD.
|
|
#
|
|
# TLS is issued ON DEMAND: on the first HTTPS handshake for a host, Caddy asks the app
|
|
# (/_caddy/ask) whether to issue a certificate, and the app approves ONLY the domain
|
|
# currently configured in the dashboard (DB) / .env. So the operator changes the domain
|
|
# in the UI, points DNS at this host, restarts the stack, and Caddy gets the cert for the
|
|
# new domain automatically — no Caddyfile edit, no SITE_ADDRESS.
|
|
#
|
|
# HTTP is ALWAYS served (bare-IP access + the operator's recovery path); the app issues
|
|
# the HTTPS redirect for the configured domain (PanelScheme), and never for the raw IP.
|
|
# Reverb (Pusher protocol) rides the same address over /app/* (WS) and /apps/* (event
|
|
# API), proxied to reverb:8080 — no extra exposed port, no mixed content.
|
|
{
|
|
# ACME contact for Let's Encrypt (set by install.sh; defaults to admin@localhost).
|
|
email {$ACME_EMAIL}
|
|
|
|
# Only mint a certificate for the host the app approves — prevents cert-issuance
|
|
# abuse from arbitrary domains pointed at this IP.
|
|
on_demand_tls {
|
|
ask http://app:80/_caddy/ask
|
|
}
|
|
|
|
# Keep serving plain HTTP (still issue certs on demand): the APP decides the
|
|
# HTTP->HTTPS redirect — only for the configured domain, never for the raw IP —
|
|
# so bare-IP HTTP always stays reachable as the operator's recovery path.
|
|
auto_https disable_redirects
|
|
|
|
# Honor X-Forwarded-* from a trusted upstream proxy (external-TLS mode). Defaults to
|
|
# 127.0.0.1/32 (trust nothing external); set TRUSTED_PROXY_CIDR to the proxy's address.
|
|
servers {
|
|
trusted_proxies static {$TRUSTED_PROXY_CIDR:127.0.0.1/32}
|
|
}
|
|
}
|
|
|
|
# Snippet: the shared proxy + hardening, reused by both the HTTP and HTTPS sites.
|
|
(clusev_proxy) {
|
|
@reverb path /app/* /apps/*
|
|
reverse_proxy @reverb reverb:8080
|
|
|
|
reverse_proxy app:80
|
|
|
|
encode zstd gzip
|
|
|
|
header {
|
|
# conservative hardening; HSTS is asserted by the app only over HTTPS
|
|
X-Content-Type-Options nosniff
|
|
X-Frame-Options DENY
|
|
Referrer-Policy strict-origin-when-cross-origin
|
|
-Server
|
|
}
|
|
}
|
|
|
|
# HTTPS for any host the app approves (the configured panel domain); cert obtained
|
|
# on demand on the first handshake.
|
|
https:// {
|
|
tls {
|
|
on_demand
|
|
}
|
|
import clusev_proxy
|
|
}
|
|
|
|
# Plain HTTP: always served — bare-IP access and the recovery path. Also carries the
|
|
# ACME HTTP-01 challenge. The app redirects regular requests to HTTPS for the configured
|
|
# domain (PanelScheme); the raw IP stays on HTTP.
|
|
http:// {
|
|
# Reverb (/app WS, /apps events) bypasses the app, so enforce transport here: on a
|
|
# HOSTNAME force the WS/event paths onto HTTPS, so broadcasts (incl. the public metrics
|
|
# channel) are never carried in cleartext and a stale/wrong host is refused by on-demand
|
|
# TLS. On a bare IP there is no TLS, so plaintext there is expected and left alone.
|
|
@reverb_cleartext {
|
|
path /app/* /apps/*
|
|
not header_regexp Host ^(\d{1,3}\.){3}\d{1,3}(:\d+)?$
|
|
}
|
|
handle @reverb_cleartext {
|
|
redir https://{host}{uri}
|
|
}
|
|
|
|
handle {
|
|
import clusev_proxy
|
|
}
|
|
}
|