# ============================================================= # GLOBAL OPTIONS # ============================================================= { acme_dns cloudflare {env.CF_API_TOKEN} } # ============================================================= # SNIPPETS # ============================================================= # Reusable matcher — blocks anything not on the LAN or tailnet. # Usage: import internal_only inside any site block. # 100.64.0.0/10 is the Tailscale CGNAT range — allows tailnet clients that # reach Caddy without subnet-route SNAT (i.e. --snat-subnet-routes=false). (internal_only) { @blocked not remote_ip 192.168.1.0/24 172.16.0.0/12 100.64.0.0/10 127.0.0.1 respond @blocked "Access denied" 403 } # TB-006 — Authentik forward auth (embedded outpost on authentik-server:9000). # Wrap protected sites in `route { ... }` so bypass handles run before forward_auth. (authentik_outpost) { reverse_proxy /outpost.goauthentik.io/* authentik-server:9000 } (authentik_forward_auth) { forward_auth authentik-server:9000 { uri /outpost.goauthentik.io/auth/caddy copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Entitlements X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version trusted_proxies private_ranges } } # Clients on LAN, Docker, or tailnet (same ranges as internal_only). (remote_internal) { remote_ip 192.168.1.0/24 172.16.0.0/12 100.64.0.0/10 127.0.0.1 } # ============================================================= # FOUNDRY VTT — public # ============================================================= foundry.ginnoir.com { reverse_proxy foundry:30000 } foundry2.ginnoir.com { reverse_proxy foundry2:30000 } # ============================================================= # TABLETOP TOOLS — public # ============================================================= 5etools.ginnoir.com { reverse_proxy 5etools:80 } pf2e.ginnoir.com { root * /srv/aon file_server } # ============================================================= # FILE STORAGE — public (Authentik on admin UI; shares + WebDAV bypass) # ============================================================= files.ginnoir.com { handle /.well-known/openid-configuration { rewrite * /index.php/apps/openidconnect/config reverse_proxy owncloud:8080 } reverse_proxy owncloud:8080 } # ============================================================= # STATIC SITES — public # ============================================================= ffttsystems.ginnoir.com { root * /srv/ffttsystems file_server } # ============================================================= # MEDIA REQUESTS — public # ============================================================= requests.ginnoir.com { reverse_proxy overseerr:5055 } # ============================================================= # MEDIA MANAGEMENT — internal only # ============================================================= sonarr.ginnoir.com { import internal_only route { import authentik_outpost handle /api/* { reverse_proxy sonarr:8989 } handle { import authentik_forward_auth reverse_proxy sonarr:8989 } } } radarr.ginnoir.com { import internal_only route { import authentik_outpost handle /api/* { reverse_proxy radarr:7878 } handle { import authentik_forward_auth reverse_proxy radarr:7878 } } } bazarr.ginnoir.com { import internal_only route { import authentik_outpost handle /api/* { reverse_proxy bazarr:6767 } handle { import authentik_forward_auth reverse_proxy bazarr:6767 } } } prowlarr.ginnoir.com { import internal_only route { import authentik_outpost handle /api/* { reverse_proxy prowlarr:9696 } handle { import authentik_forward_auth reverse_proxy prowlarr:9696 } } } tautulli.ginnoir.com { import internal_only route { import authentik_outpost handle { import authentik_forward_auth reverse_proxy tautulli:8181 } } } # ============================================================= # DOWNLOAD CLIENTS — internal only # ============================================================= qbittorrent.ginnoir.com { import internal_only route { import authentik_outpost handle { import authentik_forward_auth reverse_proxy qbittorrent:3232 } } } deluge.ginnoir.com { import internal_only route { import authentik_outpost handle { import authentik_forward_auth reverse_proxy deluge:8112 } } } nzbget.ginnoir.com { import internal_only route { import authentik_outpost handle { import authentik_forward_auth reverse_proxy nzbget:6789 } } } whisparr.ginnoir.com { import internal_only route { import authentik_outpost handle /api/* { reverse_proxy whisparr:6969 } handle { import authentik_forward_auth reverse_proxy whisparr:6969 } } } stash.ginnoir.com { import internal_only route { import authentik_outpost handle { import authentik_forward_auth reverse_proxy stash:6970 } } } # ============================================================= # GAMES / ROMS — internal only # External access for a trusted friend is via the `share` stack's Tailscale # serve node (roms-share..ts.net), NOT public exposure here. # ============================================================= roms.ginnoir.com { import internal_only reverse_proxy romm:8080 } romhacks.ginnoir.com { import internal_only root * /srv/romhacks-wiki file_server } jd.ginnoir.com { import internal_only reverse_proxy jdownloader:5800 } idlegame.ginnoir.com { import internal_only root * /srv/idlegame try_files {path} /index.html file_server } # Pokémon ROM-hack library files (box art, guides, spreadsheets) for the # Obsidian catalog notes. Internal-only — these downloads are private. # Root is the read-only /storage1/labdata/romhacks/library bind from the proxy # stack; notes embed art via https://romhacks-files.ginnoir.com//. romhacks-files.ginnoir.com { import internal_only handle_path /_roms/* { root * /srv/roms file_server } handle { root * /srv/romhacks file_server browse } } # ============================================================= # NOTIFICATIONS & RSS — public (ntfy native auth; FreshRSS HTTP auth via Authentik) # ============================================================= ntfy.ginnoir.com, http://ntfy.ginnoir.com { reverse_proxy ntfy:80 @httpget { protocol http method GET path_regexp ^/([-_a-z0-9]{0,64}$|docs/|static/) } redir @httpget https://{host}{uri} } freshrss.ginnoir.com { reverse_proxy freshrss:80 } # ============================================================= # RESUME / PORTFOLIO — public (native OIDC via Authentik; no forward_auth) # ============================================================= resume.ginnoir.com { reverse_proxy app:3000 } j-costa.com, https://j-costa.com { tls { issuer acme { disable_tlsalpn_challenge } } redir * https://resume.ginnoir.com/ginnoir/resume permanent } storage.j-costa.com, https://storage.j-costa.com { tls { issuer acme { disable_tlsalpn_challenge } } reverse_proxy resume-minio:9000 } # ============================================================= # MINIO CONSOLE — internal only # ============================================================= minio.ginnoir.com { import internal_only reverse_proxy resume-minio:9001 } # ============================================================= # FAMAPP & AUTH — public # ============================================================= fam.ginnoir.com { reverse_proxy famapp:3000 } auth.ginnoir.com { reverse_proxy authentik-server:9000 } dev.ginnoir.com { reverse_proxy 192.168.1.74:3000 } # ============================================================= # MANAGEMENT — internal only # ============================================================= portainer.ginnoir.com { @webhook { method POST path /api/stacks/webhooks/* } handle @webhook { reverse_proxy portainer:9000 } handle { reverse_proxy portainer:9000 } } uptime.ginnoir.com { route { import authentik_outpost @uptime_mcp { path /api/* /socket.io/* import remote_internal } handle @uptime_mcp { reverse_proxy uptime-kuma:3001 } handle { import authentik_forward_auth reverse_proxy uptime-kuma:3001 } } } homarr.ginnoir.com { import internal_only reverse_proxy homarr:7575 } backup.ginnoir.com { route { import authentik_outpost handle { import authentik_forward_auth reverse_proxy kopia:51515 } } } router.ginnoir.com { import internal_only reverse_proxy 192.168.1.1 } # ============================================================= # DEV STACK — internal only # ============================================================= gitea.ginnoir.com { @api path /api/* @git path_regexp (?i)\.git(/|$) handle @api { reverse_proxy gitea:3000 } handle @git { reverse_proxy gitea:3000 } handle { reverse_proxy gitea:3000 } } code.ginnoir.com { route { import authentik_outpost handle { import authentik_forward_auth reverse_proxy code_server:8443 } } } registry.ginnoir.com { import internal_only reverse_proxy registry:5000 } registry-ui.ginnoir.com { route { import authentik_outpost handle { import authentik_forward_auth reverse_proxy registry_ui:80 } } } dbx.ginnoir.com { route { import authentik_outpost handle { import authentik_forward_auth reverse_proxy dbx:4224 } } } vault.ginnoir.com { route { import authentik_outpost @vaultapi path /v1/* handle @vaultapi { reverse_proxy vault:8200 } handle { import authentik_forward_auth reverse_proxy vault:8200 } } } docs.ginnoir.com { import internal_only reverse_proxy bookstack:80 } plane.ginnoir.com { route { import authentik_outpost @plane_api { path /api/* header X-API-Key * } handle @plane_api { reverse_proxy plane_api:8000 } handle /api/* { import authentik_forward_auth reverse_proxy plane_api:8000 } handle /auth/* { import authentik_forward_auth reverse_proxy plane_api:8000 } handle /god-mode/* { import authentik_forward_auth reverse_proxy plane_admin:3000 } handle { import authentik_forward_auth reverse_proxy plane_web:3000 } } } # ============================================================= # NOTES / PKM — internal only (LiveSync over LAN or tailnet) # ============================================================= obsidian.ginnoir.com { import internal_only reverse_proxy couchdb:5984 }