Authentik SSO + homepage naked domain
Auth layer (Authentik 2026.8.0): - compose: 3-service stack (authentik-postgres, authentik-server, authentik-worker), postgres health-gated, bind mounts under /home/dank/authentik - Caddyfile: auth.franksdank.cloud vhost; forward_auth on the no-auth vhosts (homepage, SearXNG, Hermes, Prometheus, RomM, Copyparty); outpost path proxied first in a route block - .env.example: AUTHENTIK_PG_PASS + AUTHENTIK_SECRET_KEY - deploy.sh: render only CADDY_BASICAUTH_HASH (the homepage per-user hashes are retired; grafana + pihole keep basicauth) - tests/authentik.bats (new) + updated homepage/caddyfile/pihole seams - docs: Authentik.md + per-service pages; Caddy.md + Homepage.md updated Homepage (naked domain): - Caddyfile: dankclaw.franksdank.cloud -> franksdank.cloud www.franksdank.cloud; dankclaw + dankserver redirect to it; bare-IP catch-all updated - addn-hosts: A records for the naked domain, www, and auth (all -> 143) - tests/pihole.bats: vhost_names handles multi-address blocks; TLD check allows the naked domain All 146 tests pass.
This commit is contained in:
1 parent
7ba1acdbc5
commit
3cc99d81cc
17 files changed
+634
-137
No files matched your search
@@ -2,13 +2,15 @@
|
||||
|
||||
A password-protected web site on Dankclaw lists the services that are up, with a short description of each and a link to both the service and its usage documentation.
|
||||
|
||||
**Served by:** Caddy, `file_server` + `basicauth` — **Docs:** this page
|
||||
**Served by:** Caddy, `file_server` + `forward_auth` (Authentik SSO) — **Docs:** this page
|
||||
|
||||
## Status — 2026-08-23: deployed to production
|
||||
|
||||
The homepage is live on Dankclaw. `./scripts/deploy.sh dankclaw` copies the static assets to `/srv/homepage/`, installs the status checker (systemd timer, every 3 min, writes `status.json`), writes `deployed.js` (the last master deployment datetime), renders the Caddyfile (both basicauth hashes from the host `.env`), reloads Caddy, and checks health. 116 tests pass. The Caddyfile auth uses `handle` blocks (see [Auth](#auth)); the caddy container mounts the host's `/srv/homepage` (not the repo source) so the generated files serve.
|
||||
The homepage is live on Dankclaw. `./scripts/deploy.sh dankclaw` copies the static assets to `/srv/homepage/`, installs the status checker (systemd timer, every 3 min, writes `status.json`), writes `deployed.js` (the last master deployment datetime), renders the Caddyfile (the shared basicauth hash from the host `.env`), reloads Caddy, and checks health. 146 tests pass. The Caddyfile auth uses a `route` block with `forward_auth` (see [Auth](#auth)); the caddy container mounts the host's `/srv/homepage` (not the repo source) so the generated files serve.
|
||||
|
||||
To change the operator password: regenerate `CADDY_ADMIN_HASH` (`caddy hash-password`) in the host `.env`, then redeploy. The per-service `/docs/` pages, the *arr stack, and the optional `/how/` page remain separate threads.
|
||||
**Moved to the naked domain (2026-08-24):** the homepage now serves `franksdank.cloud` and `www.franksdank.cloud`. The old names `dankclaw.franksdank.cloud` and `dankserver.franksdank.cloud` redirect to it. Authentication moved from per-page basicauth to Authentik `forward_auth` (see [Authentik](Authentik.md)).
|
||||
|
||||
The per-service `/docs/` pages, the *arr stack, and the optional `/how/` page remain separate threads.
|
||||
|
||||
## Design decision — 2026-08-22
|
||||
|
||||
@@ -16,9 +18,9 @@ The live up/down check runs server-side. A checker on Dankclaw probes each backe
|
||||
|
||||
The site has two pages. The root lists user-facing services only, with no host labels. An `/admin/` subpage lists the admin and infrastructure services behind a separate password for the operator. Gitea is a user service. Hermes is an admin service. A "how it is built" page for visitors is deferred (Q6).
|
||||
|
||||
The client-side probe design is blocked in this topology. The page origin is `https://dankclaw.franksdank.cloud`. The service URLs are `http://<name>.franksdank.cloud`. That is cross-origin, mixed content, and auth-gated (HTTP 401). All three fail a browser `fetch`.
|
||||
The client-side probe design is blocked in this topology. The page origin is `https://franksdank.cloud`. The service URLs are `https://<name>.franksdank.cloud`. That is cross-origin and auth-gated. Both fail a browser `fetch`.
|
||||
|
||||
Intended direction: move to client-side probing. Serve every service as a same-origin `https` path under `dankclaw.franksdank.cloud`. Add a health endpoint and CORS headers to each backend. Then the page probes each service directly. Keep this note until that shift is complete.
|
||||
Intended direction: move to client-side probing. Serve every service as a same-origin `https` path under `franksdank.cloud`. Add a health endpoint and CORS headers to each backend. Then the page probes each service directly. Keep this note until that shift is complete.
|
||||
|
||||
## Structure
|
||||
|
||||
@@ -110,42 +112,55 @@ Styling is **toggleable** (Q27). The default is the full terminal theme. A toggl
|
||||
|
||||
## Auth
|
||||
|
||||
Caddy wraps the site with HTTP basic auth. The root page uses the shared password. The `/admin/` page uses a second, operator-only password. The shared data + assets accept **both** (the browser reuses the page's credential for the data fetches). The hashes come from the host `.env` (git-ignored). Do not commit the hash or the password.
|
||||
Caddy wraps the site with `forward_auth` (Authentik SSO). A single Authentik
|
||||
session covers the whole site: the user page, the `/admin/` page, and the
|
||||
shared data + assets. The browser's Authentik cookie is reused for the data
|
||||
fetches, so no per-page credential is needed. See [Authentik](Authentik.md) for
|
||||
the provider, the outpost, and the first-time setup.
|
||||
|
||||
The pages are served from `handle` blocks, not a top-level `rewrite`. Caddy **sorts** top-level directives, so a `rewrite` runs before a `basicauth`; the rewrite would change `/admin/` to `/admin.html` and the path-based auth matchers would then see the rewritten path and apply the wrong block. A `handle` block preserves file order, so the auth is checked on the original path.
|
||||
The Caddyfile pattern (one of the forward_auth vhosts):
|
||||
|
||||
```
|
||||
dankclaw.franksdank.cloud {
|
||||
franksdank.cloud www.franksdank.cloud {
|
||||
root * /srv/homepage
|
||||
|
||||
handle /admin* {
|
||||
basicauth {
|
||||
frank REPLACE_WITH_ADMIN_HASH
|
||||
}
|
||||
try_files /admin.html
|
||||
file_server
|
||||
}
|
||||
route {
|
||||
# 1. The outpost path is proxied to Authentik (login UI + session).
|
||||
reverse_proxy /outpost.goauthentik.io/* authentik-server:9000
|
||||
|
||||
@data path /services.json /status.json /app.js /style.css /deployed.js
|
||||
handle @data {
|
||||
basicauth {
|
||||
dank REPLACE_WITH_HASH
|
||||
frank REPLACE_WITH_ADMIN_HASH
|
||||
# 2. forward_auth checks the session before serving the page.
|
||||
forward_auth authentik-server:9000 {
|
||||
uri /outpost.goauthentik.io/auth/caddy
|
||||
copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email X-Authentik-Name
|
||||
trusted_proxies private_ranges
|
||||
}
|
||||
file_server
|
||||
}
|
||||
|
||||
handle {
|
||||
basicauth {
|
||||
dank REPLACE_WITH_HASH
|
||||
# 3. The pages, in file order. 'handle' preserves order; a top-level
|
||||
# 'rewrite' would be sorted first and break the path matchers.
|
||||
handle /admin* {
|
||||
try_files /admin.html
|
||||
file_server
|
||||
}
|
||||
|
||||
@data path /services.json /status.json /app.js /style.css /deployed.js
|
||||
handle @data {
|
||||
file_server
|
||||
}
|
||||
|
||||
handle {
|
||||
try_files /index.html
|
||||
file_server
|
||||
}
|
||||
try_files /index.html
|
||||
file_server
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A `handle` inline matcher is a single token; the multi-path `@data` match is a named matcher. The caddy container mounts the host's `/srv/homepage` (where the deploy writes the static files and the checker writes `status.json`), not the repo's `shared/homepage`. A compose change needs a container recreate (`podman compose up -d caddy`), not a reload. The Caddyfile syntax is documented in the agent knowledge base (`~/.pi/agent/docs/caddy/caddyfile.md`).
|
||||
A `handle` inline matcher is a single token; the multi-path `@data` match is a
|
||||
named matcher. The caddy container mounts the host's `/srv/homepage` (where the
|
||||
deploy writes the static files and the checker writes `status.json`), not the
|
||||
repo's `shared/homepage`. A compose change needs a container recreate (`podman
|
||||
compose up -d caddy`), not a reload. The Caddyfile syntax is documented in the
|
||||
agent knowledge base (`~/.pi/agent/docs/caddy/caddyfile.md`).
|
||||
|
||||
## Service entry format
|
||||
|
||||
@@ -175,7 +190,7 @@ A `handle` inline matcher is a single token; the multi-path `@data` match is a n
|
||||
|
||||
The deploy is **git-driven**. Master always reflects the currently deployed state (Q33). `deploy.sh` copies the assets to `/srv/homepage/`, installs the checker timer, renders the Caddyfile (both hashes), and reloads Caddy. It stamps the deployment datetime the footer shows (Q43).
|
||||
|
||||
The build is **full TDD** (Q36–Q38). Tests exist before the code: the checker (a fake up port, a down port, a WIP service, and the JSON schema), the pages and config (the `services.json` schema, `caddy validate`, the `status.json` reference), and the security check (two distinct basicauth hashes).
|
||||
The build is **full TDD** (Q36–Q38). Tests exist before the code: the checker (a fake up port, a down port, a WIP service, and the JSON schema), the pages and config (the `services.json` schema, `caddy validate`, the `status.json` reference), and the auth check (the forward_auth block and the renderable basicauth placeholder).
|
||||
|
||||
## Per-service docs (separate workstream)
|
||||
|
||||
|
||||
Reference in new issue
Block a user