docs: break first-run into individual copyable steps; clarify two-sided gate

- First-run setup: replace one big bash comment block with numbered
  sub-sections, each step getting its own prose explanation and separate
  code block(s) so individual commands can be copied without selecting
  around surrounding lines
- Step 4: add explicit table showing what happens when each side of the
  Caddy/Authelia gate is missing; add policy comparison table
- Frigate section: move subnet discovery command out of YAML comment
  into its own code block; separate restart commands into own blocks
- Troubleshooting "access denied": show the exact rule YAML to add
  and the restart command, each in their own block
- Troubleshooting "Caddy can't resolve authelia": show full compose
  YAML snippet rather than describing it in prose
- SMTP section: separate each action into its own code block

https://claude.ai/code/session_012eTokAaGiZo7aGt1T2W9BC
This commit is contained in:
Claude
2026-04-26 16:43:16 +00:00
parent 62fb5c75f4
commit 628d77563c
+185 -51
View File
@@ -281,68 +281,157 @@ gh auth login
## First-run setup
### 0. Clone the repo
The Frigate stack lives on `main`. This auth stack is on the `authelia` branch -- clone it separately into its own directory.
```bash
git clone -b authelia \
https://github.com/outis1one/frigate_w_audio.git \
~/docker/authelia
```
```bash
# 0) Clone the auth stack onto the server.
# (The Frigate stack lives on the `main` branch and is cloned separately.)
gh repo clone outis1one/frigate_w_audio -- \
--branch authelia ~/docker/authelia
cd ~/docker/authelia
```
# 1) Create the external docker network (Caddy must also be on this).
### 1. Create the external Docker network
Caddy must join this same network so it can reach Authelia by container name. Skip if `caddy_net` already exists.
```bash
docker network create caddy_net 2>/dev/null || true
```
# 2) Bootstrap the secrets directory.
### 2. Generate secrets
Authelia loads these from files so they never appear in `docker inspect` or process listings.
```bash
mkdir -p authelia/secrets
openssl rand -hex 32 > authelia/secrets/JWT_SECRET
openssl rand -hex 32 > authelia/secrets/SESSION_SECRET
openssl rand -hex 32 > authelia/secrets/STORAGE_ENCRYPTION_KEY
chmod 600 authelia/secrets/*
```
# 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.
### 3. Set your domain
`DOMAIN` is the only value you set here. It flows into `authelia/configuration.yml` via Go template substitution and into your Caddyfile via `{env.DOMAIN}` -- no find-and-replace needed anywhere else.
```bash
cp .env.example .env
$EDITOR .env # set DOMAIN, TZ; pin image versions if you want
```
# 4) For each site you added `import authelia` to in Caddy, uncomment the
# matching rule in authelia/configuration.yml under access_control.rules.
#
# WHY: default_policy is 'deny'. If a domain reaches Authelia with no
# matching rule, Authelia returns 403 -- even to a logged-in user. The
# rule tells Authelia the domain is valid and what auth level to require.
#
# Each rule is two lines -- just uncomment and pick one_factor or two_factor:
# - domain: 'cam.{{ env "DOMAIN" }}'
# policy: 'two_factor'
```bash
$EDITOR .env
```
Set `DOMAIN=yourdomain.com` and `TZ=Your/Timezone`. Save and close.
### 4. Add access control rules
**This step and step 8 (Caddy wiring) must be done together for every site you want to gate. Both are required -- neither alone is enough.**
```bash
$EDITOR authelia/configuration.yml
```
# 5) Create your first user.
Scroll to `access_control.rules`. Uncomment the rule for each site you want to protect and choose a policy:
```yaml
- domain: 'cam.{{ env "DOMAIN" }}'
policy: 'two_factor'
```
#### Why both sides are required
Caddy and Authelia each control one half of the gate:
| What you configure | What it does |
|--------------------|-------------|
| `import authelia` in a Caddy site block | Sends that site's requests to Authelia for a decision |
| Rule in `access_control.rules` | Tells Authelia what decision to make |
The default policy is `deny`. If a request reaches Authelia with no matching rule, it gets a **403 Forbidden -- no login prompt, no redirect, just blocked**. This is true even for an already-logged-in user.
Miss either side and here is what happens:
| Caddy `import authelia` | Rule in `configuration.yml` | Result |
|------------------------|----------------------------|--------|
| Missing | Present | Site is open -- Authelia is never consulted |
| Present | Missing | 403 Forbidden, no login prompt |
| Both missing | | Site is open -- Authelia is never consulted |
| Both present | | Works correctly |
#### Which policy to use
| Policy | Requires |
|--------|---------|
| `bypass` | Nothing -- Authelia waves the request through. Used for the portal itself only. |
| `one_factor` | Password only |
| `two_factor` | Password + TOTP. Use this for everything. |
### 5. Create your first user
```bash
cp authelia/users_database.yml.example authelia/users_database.yml
$EDITOR authelia/users_database.yml # set username, email, displayname
```
# Generate the password hash:
```bash
$EDITOR authelia/users_database.yml
```
Fill in `username`, `email`, and `displayname`. Then generate the password hash:
```bash
docker compose run --rm authelia \
authelia crypto hash generate argon2 --password 'your-real-password'
# Paste the $argon2id$... output into the password: field.
```
# 6) Pre-create the Authelia log file.
# Docker creates a DIRECTORY at the bind-mount path if the file doesn't
# exist, which breaks fail2ban's mount. Create it as an empty file first.
Copy the `$argon2id$...` line and paste it as the `password:` value in `users_database.yml`.
### 6. Pre-create the Authelia log file
Docker creates a **directory** at a bind-mount path if the source file does not exist yet. That breaks fail2ban's read-only mount. Create it as an empty file first:
```bash
touch authelia/authelia.log
```
# 7) Validate config before starting.
### 7. Validate the config
```bash
docker compose run --rm authelia \
authelia validate-config --config /config/configuration.yml
# Expect: "Configuration: validation complete" with no errors.
# 8) Wire up Caddy (see "Wire Caddy into Authelia" below).
# 9) Bring it up.
docker compose up -d
docker compose logs -f authelia # expect "Authelia is listening on ..."
docker compose logs -f fail2ban # expect "Jail authelia is now active"
```
Expect: `Configuration: validation complete` with no errors. Fix any YAML issues before continuing.
### 8. Wire Caddy
See [Wire Caddy into Authelia](#wire-caddy-into-authelia) below. Add the Caddy site block for each site alongside the rule you added in step 4.
### 9. Bring it up
```bash
docker compose up -d
```
Confirm both services started cleanly:
```bash
docker compose logs -f authelia
```
Expect: `Authelia is listening on ...`
```bash
docker compose logs -f fail2ban
```
Expect: `Jail authelia is now active`
## Wire Caddy into Authelia
Open `caddy/snippets.caddyfile`. It contains copy-paste blocks for your
@@ -396,12 +485,19 @@ sudo chown caddy:caddy /var/log/caddy # adjust to your Caddy UID
Edit `frigate_config/config.yml` in your Frigate stack:
First, find your `caddy_net` subnet -- you need this for `trusted_proxies`:
```bash
docker network inspect caddy_net | jq '.[0].IPAM.Config'
```
Then edit `frigate_config/config.yml`:
```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'
- 172.18.0.0/16 # replace with your caddy_net subnet from above
proxy:
header_map:
@@ -410,21 +506,28 @@ proxy:
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` in caddy/Caddyfile.
# auth_secret: 'your-32-byte-hex'
```
Then uncomment `cam.example.com` in `authelia/configuration.yml`, restart
both services:
To generate the optional `auth_secret`:
```bash
openssl rand -hex 32
```
Set the same value in the `header_up X-Proxy-Secret` line in your Caddy site block.
Add the rule to `authelia/configuration.yml` (step 4 of first-run), then restart both services:
```bash
docker compose restart authelia
```
```bash
docker compose restart frigate # in your Frigate stack
```
Verify: `https://cam.example.com` in a private window goes to Authelia and
back without a Frigate login screen.
Verify in a private browser window: `https://cam.yourdomain.com` should go to Authelia and back without a Frigate login screen.
## First login + TOTP enrollment
@@ -545,18 +648,26 @@ notifier:
# password loaded via AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE
```
Add the secret and wire it up:
Add the secret file:
```bash
echo 'your_smtp_password' > authelia/secrets/SMTP_PASSWORD
chmod 600 authelia/secrets/SMTP_PASSWORD
```
Add to the authelia service environment in `docker-compose.yml`:
Add to the `authelia` service `environment:` block in `docker-compose.yml`:
```yaml
- AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE=/secrets/SMTP_PASSWORD
```
Restart and look for `"Notifier SMTP startup check successful"` in logs.
Restart and check for a successful startup message:
```bash
docker compose restart authelia && docker compose logs -f authelia
```
Expect: `Notifier SMTP startup check successful`
## Security notes
@@ -581,8 +692,18 @@ of it served over HTTPS. Mixed HTTP/HTTPS won't work; the session cookie is
### "access denied" with no login prompt
`default_policy: deny` and no `access_control` rule for this domain. Add a
rule in `authelia/configuration.yml` and restart Authelia.
`default_policy: deny` -- a request reached Authelia with no matching rule for that domain. Add a rule under `access_control.rules` in `authelia/configuration.yml`:
```yaml
- domain: 'yoursite.{{ env "DOMAIN" }}'
policy: 'two_factor'
```
Then restart Authelia:
```bash
docker compose restart authelia
```
### Authelia container restarts forever
@@ -595,8 +716,21 @@ Most often: missing/empty secret files in `authelia/secrets/`, bad YAML in
### Caddy can't resolve `authelia`
Caddy isn't on `caddy_net`. Add `networks: [caddy_net]` to your Caddy
service and `caddy_net: external: true` at the bottom of its compose, then:
Caddy isn't on `caddy_net`. Add this to your Caddy service in its compose file:
```yaml
services:
caddy:
networks:
- caddy_net
networks:
caddy_net:
external: true
```
Then recreate the Caddy container:
```bash
docker compose up -d caddy
```