diff --git a/README.md b/README.md index 511ef5e..9de1d1a 100644 --- a/README.md +++ b/README.md @@ -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 ```