cube, api and ai joined the HTTPS_UserAccess two_factor rule in one Authelia restart on 2026-08-27, backup configuration.yml.bak-ai-20260827. All four AI hostnames now return 302 to the auth portal, and POST /ask on ai.yokogawa.tech returns 303 to the portal rather than a 404 from ai-web, which is what proves the route exists. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
133 lines
5.9 KiB
Markdown
133 lines
5.9 KiB
Markdown
# Authelia access rules — the additions, as text
|
|
|
|
**This file is documentation, not configuration. Never commit the real
|
|
`~/authelia/configuration.yml`, and never generate a replacement for it.**
|
|
It contains the AD bind account, session secrets and the Duo integration for
|
|
every service on the host. The four hostnames below are the only part of it
|
|
this project touches.
|
|
|
|
## What has to change
|
|
|
|
Four hostnames join the existing `HTTPS_UserAccess` `two_factor` rule in
|
|
`~/authelia/configuration.yml`, under `access_control.rules`:
|
|
|
|
| Hostname | Phase | Serves |
|
|
|---|---|---|
|
|
| `lf.yokogawa.tech` | 2 | Langfuse — traces, prompts, eval runs |
|
|
| `cube.yokogawa.tech` | 5 | Cube semantic layer, playground and REST API |
|
|
| `api.yokogawa.tech` | 6 | `ai-api` FastAPI |
|
|
| `ai.yokogawa.tech` | 7 | `ai-web` operator UI |
|
|
|
|
Phase 9 adds no hostname. It adds one **path-scoped rule** on an existing one —
|
|
see "Phase 9: the document publisher rule" below.
|
|
|
|
The shape of the addition — the domain list on the existing trailing rule gains
|
|
these entries, the policy and subject stay exactly as they already are:
|
|
|
|
```yaml
|
|
access_control:
|
|
rules:
|
|
# ... existing rules unchanged ...
|
|
- domain:
|
|
# ... existing domains unchanged ...
|
|
- lf.yokogawa.tech # added <date>, AI PoC Phase 2
|
|
- cube.yokogawa.tech # added <date>, AI PoC Phase 5
|
|
- api.yokogawa.tech # added <date>, AI PoC Phase 6
|
|
- ai.yokogawa.tech # added <date>, AI PoC Phase 7
|
|
policy: two_factor
|
|
subject:
|
|
- group:HTTPS_UserAccess
|
|
```
|
|
|
|
Add each hostname at the phase that needs it. Every domain added here must also
|
|
have a Caddyfile block with `import authelia` (`caddy/ai-routes.caddy`), and
|
|
every Caddyfile block must have a rule here. One without the other is a hole.
|
|
|
|
**Applied on lin001:** `lf` on 2026-08-20; `cube`, `api` and `ai` on
|
|
2026-08-27, in one Authelia restart. Backup
|
|
`~/authelia/configuration.yml.bak-ai-20260827`. All four now return 302 to the
|
|
auth portal. Between the Caddy blocks going in and this rule landing, the three
|
|
new hostnames returned **403** — Caddy serving a hostname Authelia has no rule
|
|
for means `default_policy: deny`, which is the safe direction but is not
|
|
obvious from the status code alone. `verify.sh` names that case explicitly.
|
|
|
|
## Phase 9: the document publisher rule
|
|
|
|
Operator document upload splits the API in two. Anyone in `HTTPS_UserAccess`
|
|
may ask a question and upload a document for review. **Approving** a document —
|
|
which is what makes it citable, and what decides whether a superseded revision
|
|
stops being citable — needs a second group, `AI_DocPublishers`.
|
|
|
|
```yaml
|
|
access_control:
|
|
rules:
|
|
# ... existing rules unchanged ...
|
|
|
|
# MUST come BEFORE the general api.yokogawa.tech rule. Authelia applies the
|
|
# FIRST matching rule and stops. Below it, this rule is dead and every
|
|
# authenticated user can approve a procedure revision.
|
|
- domain: api.yokogawa.tech
|
|
resources:
|
|
- '^/docs/.*'
|
|
policy: two_factor
|
|
subject:
|
|
- group:AI_DocPublishers # added <date>, AI PoC Phase 9
|
|
|
|
# ... the existing trailing rule, unchanged, still carries
|
|
# api.yokogawa.tech for everyone in HTTPS_UserAccess ...
|
|
```
|
|
|
|
Three things about this rule specifically:
|
|
|
|
- **`AI_DocPublishers` must be created in AD with DIRECT membership.** The same
|
|
trap as `HTTPS_UserAccess`: a user inside a nested group is silently denied,
|
|
with no useful log line. `svc-authelia` is read-only and cannot fix it.
|
|
- **It is not the only check.** `ai-api` re-reads `Remote-Groups` and returns
|
|
403 on approve, reject and supersede. This rule protects the whole `/docs`
|
|
path; the API protects the three operations that matter, and survives this
|
|
rule being reordered or dropped in a future edit of a file nobody diffs.
|
|
- **Verify it by calling the API directly**, as a user who is authenticated but
|
|
not a publisher. The UI hides the buttons from that user, which proves
|
|
nothing at all:
|
|
|
|
```bash
|
|
curl -si https://api.yokogawa.tech/docs/uploads/<id>/approve -X POST -H 'Content-Type: application/json' -d '{}' # expect 403
|
|
```
|
|
|
|
## How to apply it
|
|
|
|
```bash
|
|
# 1. Back up first. There are plenty of .bak-* precedents on the host.
|
|
sudo cp ~/authelia/configuration.yml ~/authelia/configuration.yml.bak-ai-$(date +%Y%m%d)
|
|
|
|
# 2. Edit with sudo - the file is root-owned. ~/apply_rule.py rewrites the
|
|
# trailing rule if you prefer it to hand-editing.
|
|
sudo nano ~/authelia/configuration.yml
|
|
|
|
# 3. ANNOUNCE FIRST - this logs out every active user on every service.
|
|
docker compose -f ~/authelia-compose.yml restart authelia
|
|
|
|
# 4. Verify. "Up" is not proof.
|
|
curl -sI https://ai.yokogawa.tech # expect 302 -> auth portal
|
|
docker logs --tail 50 authelia
|
|
```
|
|
|
|
## Things that bite
|
|
|
|
- **Restarting Authelia logs out every active user on the host**, including
|
|
whoever is mid-demo on Grafana. Announce it, and batch the domain additions
|
|
so you restart once per phase rather than once per hostname.
|
|
- **AD group membership must be DIRECT.** Authelia resolves direct membership
|
|
only; a user inside a nested group silently gets denied with no useful log
|
|
line. Before Phase 7, confirm the demo operator account is a direct member of
|
|
`HTTPS_UserAccess` and is Duo-enrolled. `svc-authelia` is read-only and
|
|
cannot fix membership for you.
|
|
- **Rule order is a security control, not a style choice.** First match wins.
|
|
A path-scoped rule placed after the domain rule it narrows is inert, and
|
|
nothing warns you — the service keeps working, for everybody.
|
|
- **A missing rule fails open at the wrong layer.** Caddy will happily serve a
|
|
hostname that has `import authelia` before the rule exists — Authelia then
|
|
applies its default policy. Add the rule in the same change as the Caddyfile
|
|
block and verify the 302 before telling anyone the URL.
|
|
- Restarting Authelia is also the supported way to refresh someone's group
|
|
membership after an AD change.
|