homelab-blueprint/docker-compose-files/caddy/Caddyfile_template
Brian Pooe fef521909b feat(forgejo): expose publicly at git.brianpooe.com with SSO-free access
Public repos are now browsable/clonable anonymously (HTTPS) and over SSH,
proxied through the Hetzner VPS. Unlike other public apps this is
deliberately not behind the oauth2-proxy/Zitadel gate, since Forgejo
already enforces its own per-repo visibility. Root redirects to the
.profile landing page. Documents the REQUIRE_SIGNIN_VIEW gotcha and the
Zitadel OIDC redirect_uri fix needed whenever FORGEJO_DOMAIN changes.
2026-07-19 18:34:13 +02:00

270 lines
8.4 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
admin localhost:2019
}
#### Shared Snippets
(common) {
tls {{CADDY_TLS_EMAIL:-admin@example.com}} {
dns cloudflare {env.CLOUDFLARE_DNS_TOKEN}
# DNS-01 must see the public Cloudflare zone, not the internal Technitium split-DNS zone.
# Exclude the Caddy DMZ host from DNS DNAT so these queries are not redirected to Technitium.
resolvers 1.1.1.1 1.0.0.1
}
header {
# Enable HSTS
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-XSS-Protection 0
# Prevent browsers from incorrectly detecting non-scripts as scripts and MIME type sniffing
X-Content-Type-Options nosniff
Referrer-Policy same-origin
-Server
X-Frame-Options "SAMEORIGIN"
Permissions-Policy "geolocation=(self), microphone=(), camera=(), interest-cohort=()"
import content-security-general
}
}
#### Content Security Snippets
(content-security-general) { # Default setup
# Disable unsafe inline/eval and plugins, only load scripts and stylesheets from same origin, fonts from google,
# and images from same origin and imgur. Sites should aim for policies like this.
Content-Security-Policy "frame-ancestors 'self' https://*.{{CADDY_BASE_DOMAIN:-home.example.com}}"
#"default-src 'self'; style-src 'self'; script-src 'self'; font-src 'self'; img-src 'self'; form-action 'self'; connect-src 'self'; frame-ancestors 'none';"
#default-src 'none'; font-src https://fonts.gstatic.com; img-src 'self' https://i.imgur.com; object-src 'none'; script-src 'self'; style-src 'self'
}
(content-security-basic) { # Most basic setup
# Disable unsafe inline/eval, only load resources from same origin except also allow images from imgur
# Also disables the execution of plugins
Content-Security-Policy default-src 'self'; img-src 'self' https://i.imgur.com; object-src 'none'
}
(content-security-api) { # API
# Disable the loading of any resources and disable framing, recommended for APIs to use
Content-Security-Policy default-src 'none'; frame-ancestors 'none'
}
# Shared block for office admin apps with self-signed upstream TLS.
(office-admin-upstream) {
transport http {
tls_insecure_skip_verify
}
header_up Host {upstream_hostport}
}
# Like office-admin-upstream but also strips the Origin header.
# UniFi devices (UDM, UNAS) validate Origin on WebSocket upgrades and reject
# connections where Origin doesn't match their own hostname, causing real-time
# dashboard stats to show N/A. Stripping Origin bypasses that check.
(unifi-upstream) {
transport http {
tls_insecure_skip_verify
}
header_up Host {upstream_hostport}
header_up -Origin
}
# Shared security headers for office admin apps.
(office-admin-security) {
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
Referrer-Policy "no-referrer"
}
encode gzip
}
#### VLAN 1 — LAN (TRUSTED zone, 10.0.1.x)
udm.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_LAN_PREFIX:-10.0.1}}.1:443 {
import unifi-upstream
}
import office-admin-security
}
switchlite8poe.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_LAN_PREFIX:-10.0.1}}.6
}
#### VLAN 10 — Office (TRUSTED zone, 10.0.10.x)
## Physical / static devices
dns01.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{DNS_BIND_IP:-10.0.10.5}}:5380
}
pbs.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.8:8007 {
import office-admin-upstream
}
import office-admin-security
}
proxmox.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.10:8006 {
import office-admin-upstream
}
import office-admin-security
}
nas.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.24:5000
}
unas.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.25:443 {
import unifi-upstream
}
import office-admin-security
}
## LXC Apps (.6.19; all in use)
bento-pdf.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.6:8080
}
it-tools.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.7:80
}
paperless-ngx.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.11:8000 {
header_up Host {host}
}
encode gzip
}
beszel.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.13:8090
}
ytd.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.14:3087
}
immich.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.15:2283
}
vaultwarden.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.16:8800
}
gramps.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.17:80
}
koffan.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.12:8080
}
# Forgejo (LXC 114, .18) -- publicly exposed at git.brianpooe.com (proxied via
# the VPS over NetBird for outside visitors) as well as reachable directly on
# the LAN via split-horizon DNS. One canonical hostname everywhere, so this
# is NOT under CADDY_BASE_DOMAIN like other internal-only apps.
# Git-over-SSH is not proxyable through Caddy -- forgejo.brianpooe.com
# resolves straight to the LXC (10.0.10.18) instead, bypassing Caddy entirely.
# Root redirects to the .profile landing page instead of the repo explore
# list -- see the Forgejo README's "Public Exposure" section.
git.brianpooe.com {
import common
redir / /brianpooe 302
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.18:3000
}
# Hermes Agent web dashboard (LXC 113, not docker — systemd service)
hermes.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
# Upstream bug: with a single password-only auth provider the gate
# auto-redirects to the OAuth-only /auth/login endpoint, which 500s.
# Send browsers to the server-rendered /login form instead.
redir /auth/login* /login
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.19:9119
}
## Arr Stack — LXC 101 (.20)
bazarr.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.20:6767
}
emby.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.20:8096
}
flaresolverr.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.20:8191
}
gluetun.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.20:8000
}
prowlarr.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.20:9696
}
qbittorrent.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.20:8080
}
radarr.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.20:7878
}
sabnzbd.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.20:8282
}
seerr.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.20:5055
}
sonarr.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_OFFICE_PREFIX:-10.0.10}}.20:8989
}
#### VLAN 30 — IoT (IoT zone, 10.0.30.x)
zigbee.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_IOT_PREFIX:-10.0.30}}.5
}
# Requires in HA configuration.yaml:
# http:
# use_x_forwarded_for: true
# trusted_proxies:
# - <caddy-host-ip>
ha.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_IOT_PREFIX:-10.0.30}}.6:8123
}
z2m.{{CADDY_BASE_DOMAIN:-home.example.com}} {
import common
reverse_proxy {{CADDY_IOT_PREFIX:-10.0.30}}.6:8080
}