Pin verified versions; add decision tree for Authelia coverage
- Pin AUTHELIA_VERSION=4.39.19 (current stable, released 2026-04-12) and FAIL2BAN_VERSION=1.1.0-r0 in .env.example + docker-compose.yml. - Reshape access_control.rules and the Caddyfile snippet around a three-case decision tree: no app auth (Authelia is the gate), app with proxy-auth support (switch FROM app login TO Authelia headers), and apps that keep their own login (skip Authelia entirely). - Document Frigate 0.14+ proxy auth specifically: auth.enabled: False, proxy.header_map (remote-user / remote-groups), trusted_proxies for the caddy_net subnet, optional X-Proxy-Secret for cross-VLAN trust. https://claude.ai/code/session_013XZ1vmgk78k2PEQ5DmJhF3
This commit is contained in:
+2
-2
@@ -7,8 +7,8 @@
|
|||||||
# 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.
|
||||||
AUTHELIA_VERSION=4.39
|
AUTHELIA_VERSION=4.39.19
|
||||||
FAIL2BAN_VERSION=latest
|
FAIL2BAN_VERSION=1.1.0-r0
|
||||||
|
|
||||||
# Used by both containers for log timestamps. Set to your IANA zone.
|
# Used by both containers for log timestamps. Set to your IANA zone.
|
||||||
TZ=America/New_York
|
TZ=America/New_York
|
||||||
|
|||||||
@@ -120,16 +120,42 @@ docker compose up -d
|
|||||||
docker compose logs -f authelia # expect "Authelia is listening on ..."
|
docker compose logs -f authelia # expect "Authelia is listening on ..."
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Which sites go behind Authelia?
|
||||||
|
|
||||||
|
Authelia is opt-in per site. The goal is one login (Authelia, with 2FA)
|
||||||
|
across everything that *can* use it -- and zero double-prompts for things
|
||||||
|
that already authenticate themselves and can't be retrofitted.
|
||||||
|
|
||||||
|
| Case | App has built-in auth? | Switchable to proxy auth? | What to do |
|
||||||
|
|------|-----------------------|---------------------------|------------|
|
||||||
|
| 1 | No | n/a | Gate with Authelia. Use `two_factor` for anything that controls hardware. |
|
||||||
|
| 2 | Yes | Yes (Authelia, Authentik, oauth2_proxy headers) | Disable the app's login form, point it at Authelia headers, gate with Authelia. Single login. |
|
||||||
|
| 3 | Yes | No | Don't involve Authelia. Plain `reverse_proxy` in Caddy. The app handles its own login. |
|
||||||
|
|
||||||
|
Concretely, in this household:
|
||||||
|
|
||||||
|
- **`doorbell.example.com`** (Pi PTT page) -- case 1. No app auth. Authelia
|
||||||
|
is the only gate. `two_factor`.
|
||||||
|
- **`cam.example.com`** (Frigate UI) -- case 2. Frigate 0.14+ supports
|
||||||
|
proxy auth, so disable Frigate's login form and let Authelia drive both
|
||||||
|
the auth and the role mapping. Single login covers Frigate too.
|
||||||
|
- **router admin / NAS UI / odd one-offs** -- case 3 territory. Plain
|
||||||
|
`reverse_proxy`, no `import authelia`, no Authelia rule.
|
||||||
|
|
||||||
|
Default policy in `configuration.yml` is `deny`, so a domain with no rule
|
||||||
|
*and* no `import authelia` in Caddy never reaches Authelia at all -- the
|
||||||
|
deny doesn't apply.
|
||||||
|
|
||||||
## Wire Caddy into Authelia
|
## Wire Caddy into Authelia
|
||||||
|
|
||||||
Open `caddy/snippet.example.caddyfile`. It defines:
|
Open `caddy/snippet.example.caddyfile`. It defines:
|
||||||
|
|
||||||
- `(authelia)` -- a reusable snippet: `import authelia` in any site block to
|
- `(authelia)` -- reusable snippet: `import authelia` in any site block
|
||||||
gate it.
|
you want gated.
|
||||||
- `(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 can watch it.
|
so fail2ban can watch it.
|
||||||
- `auth.example.com` -- the Authelia portal subdomain.
|
- `auth.example.com` -- the Authelia portal subdomain.
|
||||||
- Example protected blocks for `cam.example.com` and `doorbell.example.com`.
|
- Example blocks for the three cases above.
|
||||||
|
|
||||||
Copy the relevant blocks into your real Caddyfile, replace `example.com`
|
Copy the relevant blocks into your real Caddyfile, replace `example.com`
|
||||||
with your domain and `192.168.x.x` with real upstream IPs, then reload Caddy:
|
with your domain and `192.168.x.x` with real upstream IPs, then reload Caddy:
|
||||||
@@ -139,14 +165,45 @@ docker compose -f ~/docker/caddy/docker-compose.yml exec caddy \
|
|||||||
caddy reload --config /etc/caddy/Caddyfile
|
caddy reload --config /etc/caddy/Caddyfile
|
||||||
```
|
```
|
||||||
|
|
||||||
For each protected domain, also add a rule under `access_control.rules` in
|
For each case-1 or case-2 domain, also add a rule under `access_control.rules`
|
||||||
`authelia/configuration.yml` (Authelia's default policy is `deny` -- a
|
in `authelia/configuration.yml`. Restart Authelia after editing:
|
||||||
domain with no rule will not authenticate). Restart Authelia after editing:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose restart authelia
|
docker compose restart authelia
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Switching Frigate to Authelia (case 2)
|
||||||
|
|
||||||
|
Frigate 0.14+ has a `proxy:` config block that consumes a username header
|
||||||
|
from the upstream and skips its own login form. Edit
|
||||||
|
`frigate_config/config.yml` in your Frigate repo:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
auth:
|
||||||
|
enabled: False
|
||||||
|
trusted_proxies:
|
||||||
|
- 172.18.0.0/16 # the caddy_net subnet -- find it with:
|
||||||
|
# docker network inspect caddy_net | jq '.[0].IPAM.Config'
|
||||||
|
|
||||||
|
proxy:
|
||||||
|
header_map:
|
||||||
|
user: remote-user # what Authelia sends; matches `copy_headers` in Caddy
|
||||||
|
role: remote-groups
|
||||||
|
default_role: viewer
|
||||||
|
separator: '|'
|
||||||
|
# Optional but recommended when Caddy crosses VLANs to reach Frigate.
|
||||||
|
# Generate with `openssl rand -hex 32`. Caddy must send the same value
|
||||||
|
# as `X-Proxy-Secret` -- see the cam.* block in caddy/snippet.example.caddyfile.
|
||||||
|
# auth_secret: 'paste-32-byte-hex-here'
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart Frigate (`docker compose restart frigate` in the Frigate repo).
|
||||||
|
Confirm the Frigate UI now jumps straight to Authelia and back without
|
||||||
|
a Frigate login screen.
|
||||||
|
|
||||||
|
If you want Authelia groups to drive Frigate roles (admin vs. viewer),
|
||||||
|
add a `role_map:` under `proxy:` (see Frigate docs).
|
||||||
|
|
||||||
### Caddy access log path
|
### Caddy access log path
|
||||||
|
|
||||||
fail2ban mounts `/var/log/caddy` from the host as read-only. Your Caddy
|
fail2ban mounts `/var/log/caddy` from the host as read-only. Your Caddy
|
||||||
|
|||||||
@@ -63,28 +63,61 @@ authentication_backend:
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Access control
|
# Access control
|
||||||
#
|
#
|
||||||
# default_policy: deny means every domain Authelia sees must have an
|
# default_policy: deny means every domain that Caddy forwards here must
|
||||||
# explicit allow rule. Caddy only sends a domain to Authelia when its site
|
# have an explicit allow rule. Caddy only forwards when a site block has
|
||||||
# block has `import authelia`, so domains you don't proxy through Authelia
|
# `import authelia`, so domains where you keep the app's own auth (no
|
||||||
# aren't affected.
|
# `import authelia` in Caddy) bypass Authelia entirely and don't need
|
||||||
|
# rules here.
|
||||||
#
|
#
|
||||||
# Policies:
|
# Policies:
|
||||||
# bypass no auth (Authelia portal itself)
|
# bypass Authelia waves the request through (used for the portal)
|
||||||
# one_factor password only
|
# one_factor password only
|
||||||
# two_factor password + TOTP
|
# two_factor password + TOTP
|
||||||
|
#
|
||||||
|
# Decision tree for whether a site needs an Authelia rule at all:
|
||||||
|
#
|
||||||
|
# - App has NO built-in auth (doorbell PTT page) -> rule here +
|
||||||
|
# `import authelia` in Caddy. Use two_factor for anything that
|
||||||
|
# controls hardware in the house.
|
||||||
|
#
|
||||||
|
# - App has built-in auth but supports trusted-header proxy auth
|
||||||
|
# (Frigate 0.14+, Grafana, Gitea, Jellyfin, ...) -> rule here +
|
||||||
|
# `import authelia` in Caddy AND switch the app to proxy auth in
|
||||||
|
# its own config. Single login (Authelia), 2FA, app keeps its own
|
||||||
|
# user/role mapping driven from Authelia headers.
|
||||||
|
#
|
||||||
|
# - App has built-in auth and CAN'T switch (router admin, legacy
|
||||||
|
# things) -> NO rule here, NO `import authelia` in Caddy. The
|
||||||
|
# traffic skips Authelia entirely; the app handles its own login.
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
access_control:
|
access_control:
|
||||||
default_policy: 'deny'
|
default_policy: 'deny'
|
||||||
rules:
|
rules:
|
||||||
- domain: 'auth.example.com' # CHANGE
|
# The Authelia portal itself is always bypass.
|
||||||
|
- domain: 'auth.example.com' # CHANGE
|
||||||
policy: 'bypass'
|
policy: 'bypass'
|
||||||
|
|
||||||
# Examples -- uncomment / change to your subdomains:
|
# ----- Apps with NO built-in auth: Authelia is the only gate -----
|
||||||
# - domain: 'cam.example.com'
|
# The Pi doorbell PTT page has no app-level auth, so Authelia is it.
|
||||||
# policy: 'one_factor'
|
# 2FA is appropriate -- this URL controls a speaker in your house.
|
||||||
# - domain: 'doorbell.example.com'
|
# - domain: 'doorbell.example.com'
|
||||||
# policy: 'two_factor'
|
# policy: 'two_factor'
|
||||||
|
|
||||||
|
# ----- Apps that switched FROM their own auth TO Authelia -----
|
||||||
|
# Frigate 0.14+ supports trusted-header proxy auth. To use this:
|
||||||
|
# 1. In frigate_config/config.yml, set `auth.enabled: False`
|
||||||
|
# and add a `proxy:` block with `header_map.user: remote-user`
|
||||||
|
# and `header_map.role: remote-groups` (see README).
|
||||||
|
# 2. `import authelia` in the cam.* Caddy block.
|
||||||
|
# 3. Add the rule below.
|
||||||
|
# - domain: 'cam.example.com'
|
||||||
|
# policy: 'two_factor'
|
||||||
|
|
||||||
|
# ----- Apps that KEEP their own auth -----
|
||||||
|
# Don't add a rule and don't `import authelia` in their Caddy block.
|
||||||
|
# Example: a router admin page on `router.example.com` -- no rule
|
||||||
|
# appears here, the request never reaches Authelia.
|
||||||
|
|
||||||
session:
|
session:
|
||||||
# secret loaded via AUTHELIA_SESSION_SECRET_FILE
|
# secret loaded via AUTHELIA_SESSION_SECRET_FILE
|
||||||
cookies:
|
cookies:
|
||||||
|
|||||||
@@ -11,13 +11,32 @@
|
|||||||
# - Caddy v2.5.1 or newer
|
# - Caddy v2.5.1 or newer
|
||||||
# - Caddy joined to the external `caddy_net` docker network so it can
|
# - Caddy joined to the external `caddy_net` docker network so it can
|
||||||
# resolve `authelia` by container name
|
# resolve `authelia` by container name
|
||||||
# - access_control.rules in authelia/configuration.yml have an entry
|
#
|
||||||
# for each protected domain (otherwise Authelia's default_policy of
|
# -----------------------------------------------------------------------------
|
||||||
# `deny` will refuse access)
|
# DECISION TREE: which sites go behind Authelia?
|
||||||
|
#
|
||||||
|
# 1. App has NO built-in auth (e.g. the Pi doorbell PTT page).
|
||||||
|
# -> `import authelia` here AND add a rule in
|
||||||
|
# authelia/configuration.yml access_control.rules. Use
|
||||||
|
# two_factor for anything that controls hardware.
|
||||||
|
#
|
||||||
|
# 2. App has built-in auth AND supports trusted-header proxy auth
|
||||||
|
# (Frigate 0.14+, Grafana, Gitea, Jellyfin, Portainer, ...).
|
||||||
|
# -> `import authelia` here, add a rule in Authelia, AND switch
|
||||||
|
# the app's own config to consume Remote-User from upstream
|
||||||
|
# (disable its built-in login form). One login, 2FA, app
|
||||||
|
# still owns its user/role mapping. See the cam.* example
|
||||||
|
# and the README for Frigate specifics.
|
||||||
|
#
|
||||||
|
# 3. App has built-in auth and CAN'T switch (router admin pages,
|
||||||
|
# odd legacy things).
|
||||||
|
# -> Plain `reverse_proxy` block. NO `import authelia`, NO
|
||||||
|
# Authelia rule. The traffic skips Authelia entirely.
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Reusable forward_auth snippet -- import into any site you want gated.
|
# Reusable forward_auth snippet for cases (1) and (2). Import into any site
|
||||||
|
# block you want gated by Authelia.
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
(authelia) {
|
(authelia) {
|
||||||
forward_auth authelia:9091 {
|
forward_auth authelia:9091 {
|
||||||
@@ -42,8 +61,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Site blocks
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Authelia login portal -- bypass policy in access_control.rules
|
# Authelia login portal -- always bypass in access_control.rules.
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
auth.example.com {
|
auth.example.com {
|
||||||
import accesslog
|
import accesslog
|
||||||
@@ -51,22 +74,8 @@ auth.example.com {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Example: gate cam.example.com (Frigate UI on a different VLAN/host)
|
# CASE 1: app has NO built-in auth.
|
||||||
# Per-domain policy lives in authelia/configuration.yml, NOT here.
|
# Pi doorbell PTT page -- Authelia is the only gate. two_factor in Authelia.
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
cam.example.com {
|
|
||||||
import accesslog
|
|
||||||
import authelia
|
|
||||||
reverse_proxy 192.168.x.x:8971 {
|
|
||||||
transport http {
|
|
||||||
read_timeout 60s
|
|
||||||
write_timeout 60s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
# Example: gate doorbell.example.com (Pi PTT page + same-origin Frigate proxy)
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
doorbell.example.com {
|
doorbell.example.com {
|
||||||
import accesslog
|
import accesslog
|
||||||
@@ -85,3 +94,51 @@ doorbell.example.com {
|
|||||||
reverse_proxy 192.168.x.x:5555
|
reverse_proxy 192.168.x.x:5555
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# CASE 2: app supports trusted-header proxy auth.
|
||||||
|
# Frigate UI -- Authelia gates access AND Frigate consumes Remote-User from
|
||||||
|
# upstream so its own role mapping (admin/viewer) still works. To use this,
|
||||||
|
# also edit frigate_config/config.yml:
|
||||||
|
#
|
||||||
|
# auth:
|
||||||
|
# enabled: False
|
||||||
|
# trusted_proxies:
|
||||||
|
# - 172.18.0.0/16 # your caddy_net subnet, see README
|
||||||
|
# proxy:
|
||||||
|
# header_map:
|
||||||
|
# user: remote-user
|
||||||
|
# role: remote-groups
|
||||||
|
# default_role: viewer
|
||||||
|
# separator: '|'
|
||||||
|
# # Optional but recommended when Caddy and Frigate are on different
|
||||||
|
# # hosts/VLANs. Generate with `openssl rand -hex 32` and add the
|
||||||
|
# # matching `header_up X-Proxy-Secret <value>` below.
|
||||||
|
# # auth_secret: 'paste-32-byte-hex-here'
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
cam.example.com {
|
||||||
|
import accesslog
|
||||||
|
import authelia
|
||||||
|
reverse_proxy 192.168.x.x:8971 {
|
||||||
|
transport http {
|
||||||
|
read_timeout 60s
|
||||||
|
write_timeout 60s
|
||||||
|
}
|
||||||
|
# Uncomment and match Frigate's auth_secret if you set one above.
|
||||||
|
# header_up X-Proxy-Secret "paste-same-32-byte-hex-here"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# CASE 3: app keeps its own auth (no Authelia involvement).
|
||||||
|
# Example: a router admin page or a service that can't do proxy auth.
|
||||||
|
# Do NOT add `import authelia` and do NOT add an access_control rule for it.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# router.example.com {
|
||||||
|
# import accesslog
|
||||||
|
# reverse_proxy 192.168.1.1:443 {
|
||||||
|
# transport http {
|
||||||
|
# tls_insecure_skip_verify
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|||||||
+2
-2
@@ -21,7 +21,7 @@ services:
|
|||||||
|
|
||||||
authelia:
|
authelia:
|
||||||
container_name: authelia
|
container_name: authelia
|
||||||
image: authelia/authelia:${AUTHELIA_VERSION:-4.39}
|
image: authelia/authelia:${AUTHELIA_VERSION:-4.39.19}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
- caddy_net
|
- caddy_net
|
||||||
@@ -46,7 +46,7 @@ services:
|
|||||||
|
|
||||||
fail2ban:
|
fail2ban:
|
||||||
container_name: fail2ban
|
container_name: fail2ban
|
||||||
image: crazymax/fail2ban:${FAIL2BAN_VERSION:-latest}
|
image: crazymax/fail2ban:${FAIL2BAN_VERSION:-1.1.0-r0}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
# Host networking so iptables bans take effect on the host's edge,
|
# Host networking so iptables bans take effect on the host's edge,
|
||||||
# including DOCKER-USER chain rules that gate traffic to containers.
|
# including DOCKER-USER chain rules that gate traffic to containers.
|
||||||
|
|||||||
Reference in New Issue
Block a user