feat(nh3-svos): SVOS front door uses HTTP Basic auth (login Vuong); WhoIs gate retired

Prime's ruling: basic auth saved in the browser, mesh-only access. basic_auth argon2id with the account hash in a root:caddy file outside git (plaintext vaulted). After auth, Tailscale-User-Login is set from the authenticated user and Authorization is deleted, since the kernel's human path refuses requests that carry it. Removed svos-whois-auth (every mesh node is login pfi). Verified from a mesh peer against a temporary header echo: 401 without/with wrong credentials, 200 with exactly one Tailscale-User-Login: Vuong and no forged or Authorization headers upstream.
This commit is contained in:
vh
2026-10-05 19:47:19 -07:00
parent 454b689a54
commit c523806566
6 changed files with 37 additions and 157 deletions
+23 -15
View File
@@ -33,7 +33,7 @@ scripts/elway vh@10.100.50.43 --playbook playbooks/bootstrap-infra-ops-us
scripts/elway infra-ops@10.100.50.43 --playbook playbooks/nh3-svos-host.yaml
```
## Front door: built 2026-10-05, mesh-only `https://svos.phasefinal.com` (Prime). Fail-closed until the owner identity is set
## Front door: mesh-only `https://svos.phasefinal.com`, HTTP Basic auth (Prime, 2026-10-05)
- **Mesh:** tailscaled joined headscale as `nh3-svos`, **100.64.0.9**, `--accept-dns=false --accept-routes=false`.
- **Name:** `svos.phasefinal.com` exists ONLY as a headscale MagicDNS extra record (`/var/lib/headscale/extra-records.json`
@@ -42,25 +42,33 @@ scripts/elway infra-ops@10.100.50.43 --playbook playbooks/nh3-svos-host.yaml
`caddy-svos.service`, config `/etc/caddy/Caddyfile` (canonical `configs/svos-front/Caddyfile`). It listens **only on
100.64.0.9:443**, with no `:80`. The LE cert for `svos.phasefinal.com` comes via DNS-01, token `nh3-svos-caddy-dns01`
(phasefinal.com only), in `/etc/caddy/env` (root:caddy 0640), vaulted `nh3-svos/cloudflare-caddy-dns01-token`.
- **Identity:** `svos-whois-auth.service` (`configs/svos-front/svos-whois-auth.py`, DynamicUser, 127.0.0.1:8405). Caddy's
`forward_auth` passes the SOCKET peer; the responder asks tailscaled WhoIs and answers 200 + `Tailscale-User-Login` only
for logins in **`/etc/svos-front/allowed-logins`** (empty = nobody). Client identity/forwarding headers are deleted
first, as whole families by prefix (`Tailscale-User-*`, `X-Forwarded-*`, svos-dev's ask) plus `Tailscale-Headers-Info`,
`X-Real-IP`, `Forwarded` and `X-SVOS-Peer`. The wildcard was tested on a scratch Caddy first: case-insensitive, and
look-alikes outside the family (`X-Forwardedness`, `Tailscale-Other`) survive.
- **Identity: HTTP Basic auth, one account `Vuong`** (Prime: "Basic auth is fine, saved in browser is fine. Access is
via mesh because we don't host externally." SVOS addresses him as Prime; the login is `Vuong`). The argon2id hash is in
**`/etc/caddy/svos-basic-auth.accounts`** (root:caddy 0640, not in git), imported into `basic_auth argon2id SVOS`.
The plaintext is vaulted at **`nh3-svos/basic-auth-vuong`**. To rotate: pipe the new password to
`caddy hash-password --algorithm argon2id` on stdin (never `--plaintext`), rewrite the accounts file, `secret put`,
then `systemctl reload caddy-svos`.
- **Headers:** client copies of `Tailscale-User-*`, `X-Forwarded-*`, `Tailscale-Headers-Info`, `X-Real-IP`, `Forwarded`
and `X-SVOS-Peer` are deleted before auth. After auth, `Tailscale-User-Login` is SET to the authenticated user, and
**`Authorization` is deleted**. That deletion is load-bearing: the kernel's human path (svoperatingsystem
`u1-principals` INV-U1-06) refuses any request carrying `Authorization`. As a result the front door is human-only; a
bearer client cannot come through it.
- **Retired 2026-10-05:** the WhoIs `forward_auth` gate (`svos-whois-auth`, 127.0.0.1:8405). Every mesh node has WhoIs
login `pfi`, so it could not tell Prime's devices from the servers without restructuring the mesh, and Prime declined
that. The unit, script, `/etc/svos-front/` and the repo files are removed; git history has them.
- **Config changes:** validate WITHOUT putting the token on a command line, then reload:
`sudo systemd-run --wait --pipe --collect -p User=caddy -p Group=caddy -p EnvironmentFile=/etc/caddy/env
-p "Environment=XDG_DATA_HOME=/var/lib/caddy XDG_CONFIG_HOME=/var/lib/caddy" /usr/local/bin/caddy validate --config
<file> --adapter caddyfile`, then `systemctl reload caddy-svos`. (`env $(cat /etc/caddy/env) caddy …` leaked the first
token into sudo's argv log on 2026-10-05; it was revoked.)
- **Tested 2026-10-05:** LAN → no connection; mesh peer fv-ml1 (login `pfi`) → 403; forged `Tailscale-User-Login`,
`Tailscale-User-Whatever` and `X-Forwarded-For` → 403; the responder directly with a bogus, garbage or empty peer → 403.
- **Still to run (handoff to svos-dev's acceptance):** once a login is allowed, an allowed device gets 200 and its forged
`Tailscale-User-*` / `X-Forwarded-*` headers (arbitrary family members included) are replaced or gone at the app; a
stopped tailscaled gives 403; and the live-service vzdump + restore-mount drill once the SVOS services run.
- ⚠ **Every mesh node, servers included, has WhoIs LoginName `pfi`** (headscale has one user). `pfi` must never go in
the allow-list: it would admit every server and LAN traffic SNAT'd through a subnet router. Prime's decision pending:
a separate headscale user for his devices (recommended) or a device allow-list.
- **Tested 2026-10-05 ~1950, from mesh peer fv-ml1, against a temporary header echo on 127.0.0.1:8400 (removed after):**
- no credentials → 401 `Basic realm="SVOS"`; wrong password → 401; username `vuong` (wrong case) → 401;
- right credentials with forged `Tailscale-User-Login`/`-Whatever`, `X-Forwarded-For/Proto/Zzz`, `Forwarded` and
`X-Real-IP` → 200. Upstream saw exactly one `Tailscale-User-Login: Vuong`, no `Authorization`, none of the forged
headers, `X-Forwarded-Proto: https`, `Host: svos.phasefinal.com`, and path `/health` (the `/api` prefix stripped);
- connector route with right credentials → 502 (auth passed; the connector was not running); cert verifies; LAN
10.100.50.43:443 → connection refused.
- **Still to run:** the live-service vzdump + restore-mount drill, once svos-dev runs the services.
### Original requirements (svos-dev)