Lockouts (R-752): decisions 58-60 (decided by CC unattended), the one address behind the tunnel (R-753), the registry answered (R-750); STATUS, report, evidence
gates / gates (push) Successful in 27s
gates / gates (push) Successful in 27s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
# REPORT — strangers and lockouts (R-752), the one address behind the tunnel (R-753), the registry (R-750) — 2026-10-01 afternoon
|
||||
|
||||
Evidence: `documentation/audits/lockouts-2026-10-01/` (A, B, C, T, tools).
|
||||
Architecture read: `01-topology-and-trust.md` §5, §7; `09` §3 decisions 45–47, 57; `06` (the tunnel is not described
|
||||
there). Baselines (live Gitea ~10:55 CEST): controller `c1b123c64955`, agent `d766666ff8cf`, felhom.eu `a6a9f0b2458e`,
|
||||
catalog `83636352ea10` — all matched. Register 387 rows; highest R-752; last decision 57.
|
||||
|
||||
## The Part table
|
||||
|
||||
| Part | done / not done / changed | why |
|
||||
|---|---|---|
|
||||
| Operator note (decision 57 kept) | **done** — `09` §3 + CONTEXT | first |
|
||||
| **A — the client address** | **done — measured; no box-wide fix** (R-753) | trusting cloudflared would pass a client-written leftmost address; a single-address rewrite needs a plugin |
|
||||
| A1 two outside addresses | **changed — one** (DooPlex 37.191.56.193; no IPv6 here) | the "same address for everyone" result does not depend on a second one |
|
||||
| A1 demo-hp | **done, read only** — two GETs of a 404 path, then the logs | — |
|
||||
| **B1 calibre-web** | **measured; not fixed — operator decision** (STATUS) | no knob for the daily lock; both fixes cost the household |
|
||||
| **B2 wger** | **done — decision 58**, catalog `82fff32`; control + two fix runs on 9202 | the first fix (15 min) proved every try during a lock restarts it; changed to 5 min |
|
||||
| **B3 Grafana** | **done — decision 60: no change** (5.0 min measured; trickle measured) | already short |
|
||||
| **B4 BookStack** | **done — decision 59: no change** (1.0 min measured) | already short; `APP_PROXIES` would not help through the tunnel |
|
||||
| B installed apps | **done** — measured on 9202 | see below |
|
||||
| **C — the registry** | **done, read only** — cause found (R-750 answered) | — |
|
||||
| **D — release / golden** | **not done — not needed** | Part A built nothing |
|
||||
|
||||
## Claims in the brief that turned out wrong (or right)
|
||||
|
||||
1. **"Apps see traefik's address for every client"** — half right. The app's TCP peer is traefik, but `X-Forwarded-For`
|
||||
carries cloudflared's container address through the tunnel (the same for everyone) and the REAL address from the LAN.
|
||||
Apps that read it (calibre-web's ProxyFix, `TRUSTED_PROXY_COUNT` 1) still see one address for every tunnel visitor.
|
||||
2. **"calibre-web has no env switch"** — right for the limiter (a database setting, `config_ratelimiter`); it has an env
|
||||
`TRUSTED_PROXY_COUNT`, irrelevant here (the login limit is keyed on the user name).
|
||||
3. **"BookStack's 60 s is hard-coded"** — right (`ThrottlesLogins.php:82` 5 tries, `:90` 1 minute).
|
||||
4. **"A Gitea cleanup rule removed the old versions"** — wrong. No rule exists; a manual prune script did (HM-024).
|
||||
5. R-752's own claims: calibre-web "up to a day" — **right** (measured: still locked 2 min after the minute window; only a
|
||||
restart cleared it). My own earlier guess that calibre-web's OPDS door had no limit — **wrong**: 3/minute per name.
|
||||
Grafana "a slow trickle keeps it closed indefinitely" — **not as measured**: the household got in once the burst aged
|
||||
out, and a success resets the count. wger "everyone at once" — **right** (measured).
|
||||
6. `01` §7 "cloudflared runs on the host" — **the build differs**: it runs in the guest (R-754).
|
||||
|
||||
## Part A — the answer
|
||||
|
||||
| path | the app's TCP peer | X-Forwarded-For / X-Real-Ip | the real client is in | forgeable? |
|
||||
|---|---|---|---|---|
|
||||
| tunnel | traefik | cloudflared's container — same for every visitor | `CF-Connecting-IP` only | XFF no (traefik drops it); `CF-Connecting-IP` not through the tunnel, **yes from the LAN** |
|
||||
| LAN | traefik | the real LAN address | XFF / X-Real-Ip | no |
|
||||
|
||||
## Part B — per app (9202, the public name, a stranger through traefik)
|
||||
|
||||
| app | setting (pinned tag) | measured before | fix | after |
|
||||
|---|---|---|---|---|
|
||||
| wger 2.7 | `settings/main.py:268-272` (`AXES_*` env), `settings_global.py:485` reset-on-failure True | 10 wrong → the second member locked too | username, 5 min, DB handler (decision 58) | other member fine; admin in at 7.5 min with one retry; wrong still refused |
|
||||
| BookStack 26.09.1 | `ThrottlesLogins.php:66,82,90` | locked 1.0 min | none (59) | — |
|
||||
| Grafana 13.2.3 | `login_attempt.go:14,65-85`, `defaults.ini:498-507` | locked 5.0 min; trickle: in after the burst aged | none (60) | — |
|
||||
| calibre-web-automated v4.0.8 | `cps/web.py:2218-2219` (3/min, 40/day per name), `cps/main.py:75` (OPDS 3/min) | form 1.2 min; 40 wrong in 14 min → refused 2+ min later; restart cleared | **operator** | — |
|
||||
|
||||
**What an installed app gets, and when (measured with wger):** a settings-only change reaches the app's stack file at the
|
||||
next catalog sync (when its images equal the catalog's; ≤ 15 min); the RUNNING app keeps the old value until the next
|
||||
`compose up -d` — the app page's Restart or Start (measured: the env changed exactly at Restart), an Update, or a
|
||||
backup's restart of the app (`backup.go:972`, read, not measured). An app pinned to an older version than the catalog
|
||||
gets nothing until its Update (the frozen render, `09` §5.4).
|
||||
|
||||
## Part C — the registry (read only)
|
||||
|
||||
`package_cleanup_rule` empty; Gitea logs only to the console and the pod started 2026-08-23, so August logs are gone. The
|
||||
cause is recorded in homelab-manifests HM-024: `gitea-image-prune.sh --all --keep 7 --apply --reclaim` the night of
|
||||
2026-08-22/23 (the Gitea volume was full). Nothing schedules it. STATUS carries the decision (keep / a written rule, pick: a rule).
|
||||
|
||||
## Rows
|
||||
|
||||
**387 → 390.** Opened R-753 (one address behind the tunnel), R-754 (`01` §7 vs the build), R-755 (wger on runserver).
|
||||
Narrowed R-752. Answered R-750 (waiting on the operator). Closed none.
|
||||
|
||||
## Teardown
|
||||
|
||||
- **Machine:** 9202 back on the live catalog (`repo_url` read back), the same six containers as at the start; the apps
|
||||
this session installed (bookstack, grafana, calibre-web, wger ×5) removed through the product — calibre-web's drive
|
||||
data kept because the remove refused the drive path (R-442's fail-closed rule; its folder predates today); the echo
|
||||
container and both probe images removed. Drill catalog reset to live (`82fff32`).
|
||||
- **Host:** demo-hp untouched except two read-only GETs through its tunnel and log reads; `pct list` unchanged.
|
||||
- **Hub:** nothing. **Gitea / DooPlex:** read only (one READ ONLY database transaction, config and log reads).
|
||||
Reference in New Issue
Block a user