# ============================================================================= # Caddyfile -- Authelia + fail2ban integration # # Copy this file into your Caddy setup (or merge the relevant blocks into # your existing Caddyfile), edit all placeholders, then reload: # # # System Caddy: # sudo caddy validate --config /etc/caddy/Caddyfile # sudo systemctl reload caddy # # # Dockerized Caddy: # docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile # # Placeholders to replace: # example.com -> your real root domain # 192.168.x.x -> real upstream LAN IPs # # Requirements: # - Caddy v2.5.1+ (for `forward_auth` directive; tested on v2.11.2) # - Caddy must be on the `caddy_net` Docker network so it can resolve # `authelia` by container name. In your Caddy compose: # networks: [caddy_net] # and at the bottom: # networks: # caddy_net: # external: true # # ============================================================================= # DECISION TREE -- which sites go behind Authelia? # # CASE 1 -- App has NO built-in auth (e.g. Pi doorbell PTT page). # `import authelia` + rule in authelia/configuration.yml. # Authelia is the ONLY login. Use two_factor for hardware-control pages. # # CASE 2 -- App has built-in auth AND supports trusted-header proxy auth # (Frigate 0.14+, Grafana, Gitea, Nextcloud, Home Assistant, ...). # `import authelia` + rule in Authelia + disable the app's own login form. # Single Authelia login: Authelia authenticates, app reads Remote-User header. # # CASE 3 -- App has built-in auth and CANNOT switch to proxy auth, but you # want a 2FA gate in front anyway (router admin, legacy apps, etc.). # `import authelia` + rule in Authelia. App auth is untouched. # User logs into Authelia (2FA), then the app's own login form appears. # # CASE 4 -- App handles its own auth; Authelia not involved. # Plain `reverse_proxy`, no `import authelia`, no Authelia rule. # Traffic skips Authelia entirely. # # fail2ban coverage: import (accesslog) in EVERY site block -- gated or not. # The caddy-4xx jail watches /var/log/caddy/access.log and bans scanners # spraying all your subdomains, not just the Authelia-gated ones. # ============================================================================= # ----------------------------------------------------------------------------- # (authelia) -- forward_auth gate. # Import into any site block you want gated (cases 1, 2, 3). # On success Authelia sets Remote-User, Remote-Groups, Remote-Email, # Remote-Name headers that the upstream app can consume for role mapping. # ----------------------------------------------------------------------------- (authelia) { forward_auth authelia:9091 { uri /api/authz/forward-auth copy_headers Remote-User Remote-Groups Remote-Email Remote-Name } } # ----------------------------------------------------------------------------- # (accesslog) -- structured JSON access log consumed by fail2ban's caddy-4xx # jail. Import into EVERY site block so fail2ban covers your whole stack. # # Pre-create the log directory before starting Caddy: # sudo mkdir -p /var/log/caddy # sudo chown caddy:caddy /var/log/caddy # system Caddy # # Dockerized Caddy: add volumes: ["/var/log/caddy:/var/log/caddy"] to compose # ----------------------------------------------------------------------------- (accesslog) { log { output file /var/log/caddy/access.log { roll_size 10MiB roll_keep 5 roll_keep_for 720h } format json } } # ============================================================================= # Authelia login portal # Never add `import authelia` here -- the `bypass` rule in # access_control.rules handles the portal itself. Adding forward_auth here # would cause a redirect loop. # ============================================================================= auth.example.com { # CHANGE import accesslog reverse_proxy authelia:9091 } # ============================================================================= # CASE 1: Pi doorbell PTT page -- Authelia is the ONLY auth. # # The Pi's Flask server has no built-in authentication. Authelia gates it. # two_factor is appropriate -- this URL controls a speaker in your house. # Comment out until the Pi is deployed. # Also add (or uncomment) the doorbell.example.com rule in configuration.yml. # ============================================================================= # doorbell.example.com { # CHANGE # import accesslog # import authelia # # # Same-origin proxy to Frigate so WebRTC fetch works without CORS. # handle_path /frigate/* { # reverse_proxy 192.168.x.x:8971 { # transport http { # read_timeout 60s # write_timeout 60s # } # } # } # # handle { # reverse_proxy 192.168.x.x:5555 # } # } # ============================================================================= # CASE 2: Frigate UI -- Authelia replaces Frigate's own login form. # # Frigate 0.14+ supports trusted-header proxy auth. Authelia authenticates # the user (optionally with TOTP 2FA), then passes Remote-User and # Remote-Groups headers to Frigate which maps them to admin/viewer roles. # # To enable proxy auth in Frigate, edit frigate_config/config.yml: # # auth: # enabled: False # trusted_proxies: # - 172.18.0.0/16 # caddy_net subnet; find it with: # # docker network inspect caddy_net # proxy: # header_map: # user: remote-user # matches copy_headers in (authelia) snippet # role: remote-groups # default_role: viewer # separator: '|' # # Optional shared secret -- prevents LAN header spoofing. # # Generate: openssl rand -hex 32 # # Set the same value as header_up X-Proxy-Secret below. # # auth_secret: 'your-32-byte-hex' # # Then uncomment the cam.example.com rule in authelia/configuration.yml # and restart: docker compose restart authelia (in the authelia stack) # docker compose restart frigate (in the camera stack) # ============================================================================= cam.example.com { # CHANGE import accesslog import authelia reverse_proxy 192.168.x.x:8971 { # CHANGE IP transport http { read_timeout 60s write_timeout 60s } # Uncomment if you set auth_secret: in Frigate's proxy: block. # header_up X-Proxy-Secret "your-32-byte-hex-here" } } # ============================================================================= # CASE 3: App keeps its own login; Authelia adds a 2FA gate in front. # # Use when an app can't do proxy auth but you still want 2FA before it. # The user authenticates with Authelia (2FA), then the app's own login # form appears. Two separate logins -- the app's auth is untouched. # # Also add a rule in authelia/configuration.yml: # - domain: 'nas.example.com' # policy: 'two_factor' # ============================================================================= # nas.example.com { # CHANGE/REMOVE example # import accesslog # import authelia # # reverse_proxy 192.168.x.x:PORT { # CHANGE # transport http { # tls_insecure_skip_verify # only if self-signed TLS # } # } # } # ============================================================================= # CASE 4: App handles its own auth; Authelia not involved. # # No `import authelia`. No access_control rule in Authelia. # Still import accesslog so fail2ban's caddy-4xx jail covers this site. # ============================================================================= # router.example.com { # CHANGE/REMOVE example # import accesslog # reverse_proxy 192.168.x.x:PORT { # CHANGE # transport http { # tls_insecure_skip_verify # } # } # }