feat: drive domain from DOMAIN env var -- no manual find-and-replace

Previously every example.com had to be found and replaced manually.
Now a single DOMAIN=yourdomain.com in .env propagates everywhere:

- .env.example: add DOMAIN=example.com with explanation
- docker-compose.yml: pass DOMAIN into authelia container environment
- authelia/configuration.yml: use {{ env "DOMAIN" }} in totp.issuer,
  access_control.rules, and all four session.cookies[] fields
  (Authelia 4.38+ Go template substitution)
- caddy/Caddyfile: use {env.DOMAIN} in all site block addresses
  (Caddy native env substitution); update header comment explaining
  how to set DOMAIN for system vs dockerized Caddy
- README.md: update step 3 to explain DOMAIN is the only change needed;
  update step 4 to say just uncomment the right rule; update Caddy
  wiring section with DOMAIN env var instructions for both Caddy modes

https://claude.ai/code/session_012eTokAaGiZo7aGt1T2W9BC
This commit is contained in:
Claude
2026-04-26 14:39:56 +00:00
parent 711f892ada
commit cd78a3560d
5 changed files with 97 additions and 65 deletions
+8
View File
@@ -4,6 +4,14 @@
# so they can be mounted into the container without env-var leakage. This # so they can be mounted into the container without env-var leakage. This
# .env only holds non-secret tunables. # .env only holds non-secret tunables.
# ---------------------------------------------------------------------------
# Your root domain. This single value flows into authelia/configuration.yml
# (via Authelia's {{ env "DOMAIN" }} template substitution) and into
# caddy/Caddyfile (via Caddy's {env.DOMAIN} substitution).
# No manual find-and-replace needed -- just set this.
# ---------------------------------------------------------------------------
DOMAIN=example.com
# Pin your image versions. Bump to current stable when you upgrade -- # Pin your image versions. Bump to current stable when you upgrade --
# check https://github.com/authelia/authelia/releases and # check https://github.com/authelia/authelia/releases and
# https://github.com/crazy-max/docker-fail2ban/releases. # https://github.com/crazy-max/docker-fail2ban/releases.
+23 -11
View File
@@ -209,15 +209,15 @@ openssl rand -hex 32 > authelia/secrets/SESSION_SECRET
openssl rand -hex 32 > authelia/secrets/STORAGE_ENCRYPTION_KEY openssl rand -hex 32 > authelia/secrets/STORAGE_ENCRYPTION_KEY
chmod 600 authelia/secrets/* chmod 600 authelia/secrets/*
# 3) Copy and edit .env. # 3) Copy and edit .env -- this is the only place you set your domain.
# DOMAIN flows into authelia/configuration.yml and caddy/Caddyfile
# automatically via each tool's env-var substitution; no find-and-replace.
cp .env.example .env cp .env.example .env
$EDITOR .env # set TZ; pin AUTHELIA_VERSION if you want $EDITOR .env # set DOMAIN, TZ; pin image versions if you want
# 4) Edit authelia/configuration.yml. # 4) Uncomment access_control.rules in authelia/configuration.yml for the
# Replace every `example.com` with your real root domain. # sites you want to gate. The domain is already templated -- just
# Look for the four CHANGE comments: totp.issuer, session.cookies[].domain, # uncomment the right case block and choose one_factor or two_factor.
# session.cookies[].authelia_url, session.cookies[].default_redirection_url.
# Also uncomment access_control.rules entries for the sites you want to gate.
$EDITOR authelia/configuration.yml $EDITOR authelia/configuration.yml
# 5) Create your first user. # 5) Create your first user.
@@ -254,11 +254,23 @@ Open `caddy/Caddyfile`. It defines:
- `(authelia)` -- reusable snippet: add `import authelia` to any site block. - `(authelia)` -- reusable snippet: add `import authelia` to any site block.
- `(accesslog)` -- writes Caddy's JSON access log to `/var/log/caddy/access.log` - `(accesslog)` -- writes Caddy's JSON access log to `/var/log/caddy/access.log`
so fail2ban's `caddy-4xx` jail can watch it. so fail2ban's `caddy-4xx` jail can watch it.
- `auth.example.com` -- the Authelia portal. - `auth.{env.DOMAIN}` -- the Authelia portal.
- Example site blocks for all four cases (cases 1-3 active, case 4 commented). - Example site blocks for all four cases.
Copy the relevant blocks into your real Caddyfile, replace `example.com` with Copy the relevant blocks into your real Caddyfile and replace `192.168.x.x`
your domain and `192.168.x.x` with real upstream IPs, then reload Caddy. with real upstream IPs. The domain is already templated as `{env.DOMAIN}` --
just make sure the `DOMAIN` environment variable is available to Caddy:
```bash
# Dockerized Caddy -- add to its .env or compose environment:
DOMAIN=yourdomain.com
# System Caddy -- add to /etc/caddy/caddy.env (or wherever systemd reads env):
echo 'DOMAIN=yourdomain.com' | sudo tee -a /etc/caddy/caddy.env
# Then make sure the systemd unit loads it:
# EnvironmentFile=/etc/caddy/caddy.env (in the [Service] section)
sudo systemctl daemon-reload && sudo systemctl reload caddy
```
**Every** site block should have `import accesslog` -- even case 4 sites. **Every** site block should have `import accesslog` -- even case 4 sites.
fail2ban's caddy-4xx jail watches the one log file and covers all your fail2ban's caddy-4xx jail watches the one log file and covers all your
+17 -15
View File
@@ -11,6 +11,10 @@
# Secrets are NOT in this file. They are loaded from files mounted at # Secrets are NOT in this file. They are loaded from files mounted at
# /secrets via the AUTHELIA_*_FILE env vars in docker-compose.yml. # /secrets via the AUTHELIA_*_FILE env vars in docker-compose.yml.
# #
# Your domain comes from the DOMAIN variable in .env -- no manual
# find-and-replace needed. Authelia 4.38+ processes this file as a Go
# template, so {{ env "DOMAIN" }} is substituted at startup.
#
# After editing, validate before restarting: # After editing, validate before restarting:
# docker compose run --rm authelia authelia validate-config --config /config/configuration.yml # docker compose run --rm authelia authelia validate-config --config /config/configuration.yml
############################################################################### ###############################################################################
@@ -37,7 +41,7 @@ identity_validation:
totp: totp:
disable: false disable: false
issuer: 'example.com' # CHANGE: your root domain (shown in authenticator app) issuer: '{{ env "DOMAIN" }}' # shown in your authenticator app
algorithm: 'sha1' algorithm: 'sha1'
digits: 6 digits: 6
period: 30 period: 30
@@ -75,8 +79,6 @@ authentication_backend:
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# WHICH SITES NEED A RULE HERE? # WHICH SITES NEED A RULE HERE?
# #
# There are four ways a site can relate to Authelia:
#
# CASE 1 -- App has NO built-in auth (e.g. Pi doorbell PTT page). # CASE 1 -- App has NO built-in auth (e.g. Pi doorbell PTT page).
# -> Rule required + `import authelia` in Caddy. # -> Rule required + `import authelia` in Caddy.
# -> Authelia is the ONLY login. Use two_factor for hardware-control pages. # -> Authelia is the ONLY login. Use two_factor for hardware-control pages.
@@ -104,7 +106,7 @@ access_control:
rules: rules:
# The Authelia portal itself is always bypass. # The Authelia portal itself is always bypass.
- domain: 'auth.example.com' # CHANGE - domain: 'auth.{{ env "DOMAIN" }}'
policy: 'bypass' policy: 'bypass'
# ------------------------------------------------------------------- # -------------------------------------------------------------------
@@ -112,24 +114,24 @@ access_control:
# The Pi doorbell PTT page has no built-in authentication. # The Pi doorbell PTT page has no built-in authentication.
# two_factor is appropriate -- this URL controls a speaker in your house. # two_factor is appropriate -- this URL controls a speaker in your house.
# ------------------------------------------------------------------- # -------------------------------------------------------------------
# - domain: 'doorbell.example.com' # CHANGE # - domain: 'doorbell.{{ env "DOMAIN" }}'
# policy: 'two_factor' # policy: 'two_factor'
# ------------------------------------------------------------------- # -------------------------------------------------------------------
# CASE 2: App supports trusted-header proxy auth -- replace app login. # CASE 2: App supports trusted-header proxy auth -- replace app login.
# Frigate 0.14+: set `auth.enabled: False` and configure `proxy:` in # Frigate: set auth.enabled: False and configure proxy: in
# frigate_config/config.yml (see README.md "Switching Frigate to Authelia"). # frigate_config/config.yml (see README.md). Authelia handles login
# Single login: Authelia authenticates, Frigate reads Remote-User/Groups. # and passes Remote-User/Remote-Groups to Frigate for role mapping.
# ------------------------------------------------------------------- # -------------------------------------------------------------------
# - domain: 'cam.example.com' # CHANGE # - domain: 'cam.{{ env "DOMAIN" }}'
# policy: 'two_factor' # policy: 'two_factor'
# ------------------------------------------------------------------- # -------------------------------------------------------------------
# CASE 3: App keeps its own auth; Authelia adds a 2FA gate in front. # CASE 3: App keeps its own auth; Authelia adds a 2FA gate in front.
# The app's login form is still shown after Authelia passes the request. # User authenticates with Authelia (2FA) then with the app itself.
# User logs into Authelia (2FA) then into the app separately. # Add entries for any extra-gated domains here.
# ------------------------------------------------------------------- # -------------------------------------------------------------------
# - domain: 'nas.example.com' # CHANGE/REMOVE example # - domain: 'nas.{{ env "DOMAIN" }}'
# policy: 'two_factor' # policy: 'two_factor'
# CASE 4: No rule here, no `import authelia` in Caddy. App handles auth. # CASE 4: No rule here, no `import authelia` in Caddy. App handles auth.
@@ -138,9 +140,9 @@ session:
# secret loaded via AUTHELIA_SESSION_SECRET_FILE # secret loaded via AUTHELIA_SESSION_SECRET_FILE
cookies: cookies:
- name: 'authelia_session' - name: 'authelia_session'
domain: 'example.com' # CHANGE: your root domain domain: '{{ env "DOMAIN" }}'
authelia_url: 'https://auth.example.com' # CHANGE authelia_url: 'https://auth.{{ env "DOMAIN" }}'
default_redirection_url: 'https://example.com' # CHANGE default_redirection_url: 'https://{{ env "DOMAIN" }}'
expiration: '1 hour' expiration: '1 hour'
inactivity: '5 minutes' inactivity: '5 minutes'
remember_me: '1 month' remember_me: '1 month'
+46 -39
View File
@@ -2,7 +2,7 @@
# Caddyfile -- Authelia + fail2ban integration # Caddyfile -- Authelia + fail2ban integration
# #
# Copy this file into your Caddy setup (or merge the relevant blocks into # Copy this file into your Caddy setup (or merge the relevant blocks into
# your existing Caddyfile), edit all placeholders, then reload: # your existing Caddyfile), then reload:
# #
# # System Caddy: # # System Caddy:
# sudo caddy validate --config /etc/caddy/Caddyfile # sudo caddy validate --config /etc/caddy/Caddyfile
@@ -11,9 +11,16 @@
# # Dockerized Caddy: # # Dockerized Caddy:
# docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile # docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile
# #
# Placeholders to replace: # Domain is read from the DOMAIN environment variable -- no hardcoded
# example.com -> your real root domain # domains in this file. Set it in your Caddy environment before reloading:
# 192.168.x.x -> real upstream LAN IPs #
# # System Caddy (add to /etc/caddy/caddy.env or your systemd override):
# export DOMAIN=yourdomain.com
#
# # Dockerized Caddy (add to its .env or compose environment:):
# DOMAIN=yourdomain.com
#
# Only the upstream IPs need editing: replace 192.168.x.x with real LAN IPs.
# #
# Requirements: # Requirements:
# - Caddy v2.5.1+ (for `forward_auth` directive; tested on v2.11.2) # - Caddy v2.5.1+ (for `forward_auth` directive; tested on v2.11.2)
@@ -90,38 +97,11 @@
# access_control.rules handles the portal itself. Adding forward_auth here # access_control.rules handles the portal itself. Adding forward_auth here
# would cause a redirect loop. # would cause a redirect loop.
# ============================================================================= # =============================================================================
auth.example.com { # CHANGE auth.{env.DOMAIN} {
import accesslog import accesslog
reverse_proxy authelia:9091 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. # CASE 2: Frigate UI -- Authelia replaces Frigate's own login form.
# #
@@ -147,11 +127,11 @@ auth.example.com { # CHANGE
# # Set the same value as header_up X-Proxy-Secret below. # # Set the same value as header_up X-Proxy-Secret below.
# # auth_secret: 'your-32-byte-hex' # # auth_secret: 'your-32-byte-hex'
# #
# Then uncomment the cam.example.com rule in authelia/configuration.yml # Then uncomment the cam rule in authelia/configuration.yml and restart:
# and restart: docker compose restart authelia (in the authelia stack) # docker compose restart authelia (in the authelia stack)
# docker compose restart frigate (in the camera stack) # docker compose restart frigate (in the camera stack)
# ============================================================================= # =============================================================================
cam.example.com { # CHANGE cam.{env.DOMAIN} {
import accesslog import accesslog
import authelia import authelia
@@ -165,6 +145,33 @@ cam.example.com { # CHANGE
} }
} }
# =============================================================================
# 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 uncomment the doorbell rule in authelia/configuration.yml.
# =============================================================================
# doorbell.{env.DOMAIN} {
# 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 { # CHANGE IP
# transport http {
# read_timeout 60s
# write_timeout 60s
# }
# }
# }
#
# handle {
# reverse_proxy 192.168.x.x:5555 # CHANGE IP
# }
# }
# ============================================================================= # =============================================================================
# CASE 3: App keeps its own login; Authelia adds a 2FA gate in front. # CASE 3: App keeps its own login; Authelia adds a 2FA gate in front.
# #
@@ -173,10 +180,10 @@ cam.example.com { # CHANGE
# form appears. Two separate logins -- the app's auth is untouched. # form appears. Two separate logins -- the app's auth is untouched.
# #
# Also add a rule in authelia/configuration.yml: # Also add a rule in authelia/configuration.yml:
# - domain: 'nas.example.com' # - domain: 'nas.{{ env "DOMAIN" }}'
# policy: 'two_factor' # policy: 'two_factor'
# ============================================================================= # =============================================================================
# nas.example.com { # CHANGE/REMOVE example # nas.{env.DOMAIN} { # CHANGE/REMOVE example
# import accesslog # import accesslog
# import authelia # import authelia
# #
@@ -193,7 +200,7 @@ cam.example.com { # CHANGE
# No `import authelia`. No access_control rule in Authelia. # No `import authelia`. No access_control rule in Authelia.
# Still import accesslog so fail2ban's caddy-4xx jail covers this site. # Still import accesslog so fail2ban's caddy-4xx jail covers this site.
# ============================================================================= # =============================================================================
# router.example.com { # CHANGE/REMOVE example # router.{env.DOMAIN} { # CHANGE/REMOVE example
# import accesslog # import accesslog
# reverse_proxy 192.168.x.x:PORT { # CHANGE # reverse_proxy 192.168.x.x:PORT { # CHANGE
# transport http { # transport http {
+3
View File
@@ -34,6 +34,9 @@ services:
- AUTHELIA_SESSION_SECRET_FILE=/secrets/SESSION_SECRET - AUTHELIA_SESSION_SECRET_FILE=/secrets/SESSION_SECRET
- AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE=/secrets/STORAGE_ENCRYPTION_KEY - AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE=/secrets/STORAGE_ENCRYPTION_KEY
- TZ=${TZ:-UTC} - TZ=${TZ:-UTC}
# Passed through so authelia/configuration.yml can use {{ env "DOMAIN" }}
# instead of hardcoding your domain in the config file.
- DOMAIN=${DOMAIN}
volumes: volumes:
- ./authelia:/config - ./authelia:/config
- ./authelia/secrets:/secrets:ro - ./authelia/secrets:/secrets:ro