Entitlements fail open: group-less/JIT owners get unlimited quota (quota.default = -1) #120

Stängd
öppnade 2026-07-19 06:56:05 +00:00 av supernaut · 1 kommentar
Ägare

Severity: CRITICAL — surfaced by the pre-onboarding infra audit (2026-07-19).

#85 was closed intending to set [quota.default] TOTAL = 0 to close the fail-open window, but the fix could not land: the in-file comment (ansible/roles/forgejo/templates/app.ini.j2:183-197) records that on Forgejo 16 [quota.default] is composed restrictively for everyone (not just group-less users as on v15), so 0 broke all users — even members of unlimited groups couldn't push/merge. So TOTAL is back to -1 (unlimited) and the window #85 was meant to close is still open.

How it fails open

[quota.default] governs any owner in no quota group. It is -1 (unlimited). The reconciler (roles/reconciler, timer *:0/15) is the only thing that puts a user into a quota group.

  • A brand-new JIT account (first OIDC login) is unlimited until the next reconciler tick (~15 min, documented).
  • If the reconciler timer is dead / its token is wrong / a Kanidm read returns empty, new users stay group-less and therefore unlimited indefinitely. The system fails open — exactly wrong for open registration.

Ask

Close the JIT/reconciler-down window with a mechanism that survives the v16 restrictive-compose behaviour, e.g.:

  • assign the Forgejo quota group synchronously at signup (bitborg-web) or at JIT-provision, not on a 15-min lag; and/or
  • a Forgejo-side default that blocks uploads for group-less owners without blocking existing group members (needs a v16-correct knob — investigate [quota] group precedence).
  • MUST be paired with the reconciler deadman alert (separate issue) so a dead reconciler is detected, not silently fail-open.

Refs closed #85, ADR 0017/0029.

**Severity: CRITICAL** — surfaced by the pre-onboarding infra audit (2026-07-19). `#85` was closed intending to set `[quota.default] TOTAL = 0` to close the fail-open window, but the fix could not land: the in-file comment (`ansible/roles/forgejo/templates/app.ini.j2:183-197`) records that on **Forgejo 16** `[quota.default]` is composed restrictively for **everyone** (not just group-less users as on v15), so `0` broke all users — even members of unlimited groups couldn't push/merge. So `TOTAL` is back to **`-1` (unlimited)** and the window `#85` was meant to close is still open. ### How it fails open `[quota.default]` governs any owner in **no** quota group. It is `-1` (unlimited). The reconciler (`roles/reconciler`, timer `*:0/15`) is the only thing that puts a user into a quota group. - A brand-new JIT account (first OIDC login) is **unlimited** until the next reconciler tick (~15 min, documented). - If the reconciler timer is dead / its token is wrong / a Kanidm read returns empty, new users stay group-less and therefore **unlimited indefinitely**. The system fails open — exactly wrong for open registration. ### Ask Close the JIT/reconciler-down window with a mechanism that survives the v16 restrictive-compose behaviour, e.g.: - assign the Forgejo quota group **synchronously** at signup (bitborg-web) or at JIT-provision, not on a 15-min lag; and/or - a Forgejo-side default that blocks *uploads* for group-less owners without blocking existing group members (needs a v16-correct knob — investigate `[quota]` group precedence). - MUST be paired with the reconciler deadman alert (separate issue) so a dead reconciler is detected, not silently fail-open. Refs closed #85, ADR 0017/0029.
Upphovsperson
Ägare

✅ Resolved + verified live (2026-07-19, PRs #150 + #152).

Fail-closed default: [quota].DEFAULT_GROUPS = participant is live in the running Forgejo config. Verified via the admin quota API that it did NOT break anyone (the [quota.default]=0 incident is not reproduced):

  • Tier users unaffected — supernaut → lfs-basic (base = size:repos:all/assets:all -1, unlimited repos + tier LFS cap); kofish → lfs-xl (base -1 + 50 GiB LFS). Grouped users show ONLY their explicit group, so DEFAULT_GROUPS acts as a fallback for group-less owners only.
  • Platform org safe — gitborg → org-unlimited (size:all -1).
  • Group-less / new JIT owners now inherit the zero-quota participant group (browse + issues yes, uploads no) until the reconciler assigns their real tier — the intended fail-closed behaviour.

Defense-in-depth (PR #150): ReconcilerStale deadman alert (a dead reconciler that would silently stop entitlement sync is now detected) + reconcile interval 15 → 5 min (shrinks the group-less window). Forgejo healthy after the app.ini restart.

Rollback if ever needed: forgejo_quota_default_groups: "" + apply.

✅ **Resolved + verified live** (2026-07-19, PRs #150 + #152). **Fail-closed default:** `[quota].DEFAULT_GROUPS = participant` is live in the running Forgejo config. Verified via the admin quota API that it did NOT break anyone (the `[quota.default]=0` incident is not reproduced): - **Tier users unaffected** — `supernaut` → `lfs-basic` (`base` = `size:repos:all/assets:all -1`, unlimited repos + tier LFS cap); `kofish` → `lfs-xl` (`base -1` + 50 GiB LFS). Grouped users show ONLY their explicit group, so `DEFAULT_GROUPS` acts as a fallback for **group-less owners only**. - **Platform org safe** — `gitborg` → `org-unlimited` (`size:all -1`). - **Group-less / new JIT owners** now inherit the zero-quota `participant` group (browse + issues yes, uploads no) until the reconciler assigns their real tier — the intended fail-closed behaviour. **Defense-in-depth (PR #150):** `ReconcilerStale` deadman alert (a dead reconciler that would silently stop entitlement sync is now detected) + reconcile interval 15 → 5 min (shrinks the group-less window). Forgejo healthy after the app.ini restart. Rollback if ever needed: `forgejo_quota_default_groups: ""` + apply.
Logga in för att delta i denna konversation.
Ingen milstolpe
Inga tilldelade
1 deltagare
Notiser
Förfallodatum
Förfallodatumet är ogiltigt eller utanför gränserna. Använd formatet "åååå-mm-dd".

Inget förfallodatum satt.

Beroenden

Inga beroenden satta

Referens
bitborg/bitborg-infra#120
Ingen beskrivning angiven.