From 1f50fa7901c721356361fdcc42b1f089f6b6a6cb Mon Sep 17 00:00:00 2001 From: Garrett Allen <98648590+Gerrrt@users.noreply.github.com> Date: Fri, 4 Sep 2026 14:35:01 +0000 Subject: [PATCH] docs(adr): give ADR-0008's SSO deferral an expiry (#103) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ADR-0008 defers Authelia knowingly and records why, but records no condition for ending it. A deferral with no end condition and a decision never to do the thing are the same document six months later. ADR-0022 ends it on a state rather than a date: the first real secret, photo or document in the sensitive tier, any reachability from outside the house, or a third account holder — whichever comes first. Expiry means a decision gets recorded, not that Authelia gets deployed; re-accepting is allowed, drifting is what the trigger removes. Checking ADR-0008's stated substitute for SSO against the actual service list is most of what this was. Per-application TOTP exists on Vaultwarden, Paperless-ngx and Home Assistant, and on none of Grafana, Immich or AdGuard Home — Grafana OSS has no MFA in any edition, Immich's upstream has declined it and points at OAuth, and AdGuard has one password-only admin. Grafana is the only one of the six deployed, so the thing offered in place of SSO has never been available here, and for those three an identity provider is the only route to a second factor rather than a heavier alternative to one. ADR-0008 is not superseded and not amended — ADR-0001 keeps it immutable — so its SSO paragraph gets a forward pointer and nothing else. security.md's single MFA line is replaced with the per-service split, and the roadmap moves #103 into Security with the two things it puts on #102's build: TOTP enrolment on the three that can carry it, and the mini PC's disk encryption decided there rather than inherited from prometheus. Co-Authored-By: Claude Opus 5 --- docs/adr/0008-place-services-by-data-trust.md | 6 + ...-deferral-when-the-tier-holds-real-data.md | 142 ++++++++++++++++++ docs/roadmap.md | 33 +++- docs/security.md | 16 ++ 4 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 docs/adr/0022-expire-the-sso-deferral-when-the-tier-holds-real-data.md diff --git a/docs/adr/0008-place-services-by-data-trust.md b/docs/adr/0008-place-services-by-data-trust.md index 4ea3a24..423902e 100644 --- a/docs/adr/0008-place-services-by-data-trust.md +++ b/docs/adr/0008-place-services-by-data-trust.md @@ -72,6 +72,12 @@ TOTP is proportionate, and an SSO layer is operational weight that has not yet earned its place. This leaves the "no MFA on the internal services" gap in `security.md` open, knowingly. +> *The deferral above has an expiry as of +> [ADR-0022](0022-expire-the-sso-deferral-when-the-tier-holds-real-data.md), +> which also finds that per-application TOTP is not available on two of the +> services this paragraph assumes it for. The reasoning above is unchanged and +> this ADR is not superseded.* + ## Consequences - **Winterfell becomes busier and less special, and this is the real cost.** diff --git a/docs/adr/0022-expire-the-sso-deferral-when-the-tier-holds-real-data.md b/docs/adr/0022-expire-the-sso-deferral-when-the-tier-holds-real-data.md new file mode 100644 index 0000000..815a0cc --- /dev/null +++ b/docs/adr/0022-expire-the-sso-deferral-when-the-tier-holds-real-data.md @@ -0,0 +1,142 @@ +# ADR-0022: Expire the SSO deferral when the tier holds real data + +**Status:** Accepted · 2026-09 + +## Context + +[ADR-0008](0008-place-services-by-data-trust.md) defers Authelia knowingly and +records why: two users, no external exposure, and *"per-application +authentication with TOTP is proportionate."* What it does not record is when the +deferral stops being the right answer. A deferral with no end condition and a +decision never to do the thing are the same document six months later, and by +then nobody remembers which one was meant. + +The gap today is genuinely small, and it is worth being precise about why rather +than taking it on faith. **Grafana is the only authenticated service the estate +runs.** It has one account; anonymous access and sign-up are off and the password +comes from SOPS (`stacks/observability/compose.yaml`); and the Hicks tab reaches +`10.0.99.20` on `3000` and nothing else on that host, above a logged *Block +access to Winterfell*. Nothing untrusted reaches it at all. + +That stops being the shape of the problem the moment ADR-0008's sensitive tier +exists. [#102](https://github.com/Gerrrt/HomeLab/issues/102) puts Vaultwarden, +Immich, Paperless-ngx and Home Assistant on Winterfell — by ADR-0008's own trust +argument, the four things in the estate most worth a second factor, which is +exactly why the argument for deferring gets weaker as the build gets closer +rather than staying where it was. + +**The premise that does not survive checking is the third one.** ADR-0008 offered +per-application TOTP *in place of* SSO. Read against the services that actually +authenticate a person, that substitute exists for half of them: + +| Service | Its own second factor | +| --- | --- | +| Vaultwarden | TOTP and WebAuthn, built in | +| Paperless-ngx | TOTP and WebAuthn, through `django-allauth` | +| Home Assistant | TOTP, built in | +| Immich | **None.** Upstream has declined it repeatedly and points at OAuth — which in this house means Authelia or Authentik | +| Grafana OSS | **None, in any edition.** "Grafana and the Grafana Cloud portal currently do not include built-in support for multi-factor authentication"; the documented route is an external identity provider | +| AdGuard Home | **None.** One admin account, password only | + +ntfy and Homepage are left out of that table because neither authenticates a +household identity — ntfy has basic auth on a topic, Homepage has no login at +all. + +Two things follow, and both cut against the deferral as written. **For Grafana +and Immich an identity provider is not a heavier alternative to per-application +TOTP — it is the only route to a second factor there is.** And Grafana is the one +of the six that is deployed *today*, so the substitute ADR-0008 named has never +existed in this estate. The line it points at in `security.md` is not a gap +awaiting a decision; for the only service it currently describes, it is a +standing property. + +AdGuard Home is the third, and its data is easy to under-rate. Under +[ADR-0010](0010-keep-the-resolver-on-the-gateway.md) Unbound forwards to it, so +its query log is the whole household's browsing history — without per-client +attribution, which is the one mitigation, and which ADR-0010 arrived at for +unrelated reasons. + +Finally, `SECURITY.md` records the monitoring host's unencrypted disk and swap as +an accepted residual, measured on `prometheus`. **The mini PC is not bought**, so +full-disk encryption there is a build-time choice rather than a retrofit — and a +password vault behind one factor on an unencrypted disk is a different bet from a +metrics dashboard behind one factor on the same disk. The two decisions belong in +the same sitting, which is the observation +[#103](https://github.com/Gerrrt/HomeLab/issues/103) was filed on. + +## Decision + +**The deferral stands, and it now ends on a stated condition.** ADR-0008 is +neither superseded nor amended — [ADR-0001](0001-record-architecture-decisions.md) +keeps it immutable, and its SSO paragraph gets a forward pointer to this document +and nothing else. Everything it decided about placement holds. + +**The expiry is a state, not a date.** Nothing about this risk is driven by the +calendar, so a date would be arbitrary, and an arbitrary date is a deadline +everyone learns to move. Three triggers, whichever comes first: + +1. **The sensitive tier holds real data** — the first real credential in + Vaultwarden, the first real photo in Immich, or the first real document in + Paperless-ngx. Seeded test entries do not count. Deciding *before* the data + arrives rather than after is the whole point. +2. **Any of it becomes reachable from outside the house**, by any means, + including a VPN terminating on 99. This is ADR-0008's *no external exposure* + premise made testable. +3. **A third person gets an account on any of it.** ADR-0008's *two users* + premise, likewise. That ADR already says the household growing should force a + revisit; this makes it a trigger rather than an aspiration. + +**Expiry means a decision gets recorded, not that Authelia gets deployed.** At +the first trigger, a new ADR either stands an identity provider up or re-accepts +the deferral with its reasons. Re-accepting is a legitimate outcome — it is what +happened here once already. What this document removes is the third option, which +is arriving at the same place by never looking. + +**Three things are due before #102's tier holds anything.** They are the floor +the deferral rests on, and none of them is automatic: + +- **TOTP enrolled at first login on Vaultwarden, Paperless-ngx and Home + Assistant.** This is precisely what ADR-0008 claims is in place of SSO, and all + three ship with it off. +- **Immich and AdGuard Home named in `security.md` as unable to carry a factor + at all**, rather than folded into a single line about MFA that reads as uniform + and is not. +- **The mini PC's disk encryption decided at build time**, on #102, rather than + inherited from `prometheus` by default. Recorded here; not decided here. + +**Grafana does not wait for the tier and its gap does not close on this +timetable.** No edition of Grafana OSS can carry a second factor, so its only +path is the same identity provider, and until one exists "no MFA" is true of it +permanently rather than pending. `docs/security.md` now says that instead of +implying otherwise. + +## Consequences + +- **#102 cannot be finished without meeting the floor or explicitly declining + it.** A deferral with a trigger is something a reader can check the estate + against; one without is a sentence. +- **The decision now lands before hardware is bought rather than after data is + loaded.** Authelia in front of Immich is a compose-file change on a box with + nothing on it. The same change under a live photo library and a working vault + is a migration, a re-enrolment and a household outage. +- **"No MFA on the internal services" turns out not to be one gap.** Three + services can close it themselves today and three cannot close it at all. The + single line in `security.md` flattened the distinction that decides the answer, + and the flattening is what made the deferral look cheaper than it is. +- **The cost of Authelia is unchanged and still real.** It puts one container in + the authentication path of everything, and its failure is a house-wide login + outage that looks like every service breaking at once — the same shape of + failure ADR-0010 declined for DNS, for the same reason. That is why this ADR + sets a trigger rather than mandating an identity provider now. +- **The unencrypted disk and single-factor auth compound, and the compounding is + new.** `SECURITY.md` accepts plaintext at rest on `prometheus` on the strength + of a threat model that excludes physical access to the rack. That exclusion was + written when the most valuable thing on the disk was 30 days of metrics. The + mini PC is the last moment the disk half of that bet is cheap to change. +- **Some of the estate will re-accept the deferral, and should.** ntfy, Homepage + and the whole streaming tier hold nothing whose exposure costs anything, and an + identity provider in front of a page of links is exactly the operational weight + ADR-0008 was right about. +- Nothing here changes a rule, a container or a byte of configuration. It is a + condition written down, which is the smallest possible artefact and the one the + deferral was missing. diff --git a/docs/roadmap.md b/docs/roadmap.md index 4ace329..faf5e8a 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -69,6 +69,28 @@ issues intact. Nothing was summarised away. agent pushes to them and has no other path, which is why #70 could close Alertmanager and not these. Firewall default-deny is the whole control. Accepted residual, recorded in `SECURITY.md`. +- **[#103](https://github.com/Gerrrt/HomeLab/issues/103) Give ADR-0008's SSO + deferral an expiry.** Answered by + [ADR-0022](adr/0022-expire-the-sso-deferral-when-the-tier-holds-real-data.md): + the deferral ends on a state rather than a date — the first real secret, photo + or document in the sensitive tier, any reachability from outside the house, or + a third account holder, whichever comes first. At the first of those a + decision gets recorded. Re-accepting is allowed; arriving at the same place by + never looking is what the expiry removes. + + **Writing it turned up that ADR-0008's substitute for SSO does not exist for + half the tier.** Per-application TOTP is available on Vaultwarden, + Paperless-ngx and Home Assistant, and on none of Grafana, Immich or AdGuard + Home — Grafana OSS has no MFA in any edition, Immich's upstream has declined + it and points at OAuth, and AdGuard has one password-only admin. Grafana is + the only one of the six deployed, so the thing ADR-0008 offered *in place of* + SSO has never been available here, and for those three an identity provider is + the only route to a second factor rather than a heavier alternative to one. + What is outstanding belongs to #102: TOTP enrolled at first login on the three + that can carry it, and the mini PC's disk encryption decided at build time + rather than inherited from `prometheus` — a vault behind one factor on an + unencrypted disk is not the bet `SECURITY.md` accepted for a metrics + dashboard. - **[#235](https://github.com/Gerrrt/HomeLab/issues/235) Decide whether the iLO stays on the lab segment.** ADR-0014 puts `ifrit`'s attack VM on ImaginationLAN, so `shiva` — the BMC of the box being defended, on firmware @@ -259,7 +281,11 @@ what left this one unfireable for months. cannot host it — ADR-0015 measured 2549 MiB available behind a 5400 rpm disk and a 100 Mb/s NIC — and ADR-0007 keeps household services off the lab hypervisor. The SSO this box deliberately does not get is - [#103](https://github.com/Gerrrt/HomeLab/issues/103). + [#103](https://github.com/Gerrrt/HomeLab/issues/103), and as of + [ADR-0022](adr/0022-expire-the-sso-deferral-when-the-tier-holds-real-data.md) + it does not get it *until this box holds real data* rather than indefinitely — + which puts two things on this build: TOTP enrolled on the three services that + can carry it, and a disk encryption decision made here rather than inherited. - **[#95](https://github.com/Gerrrt/HomeLab/issues/95) Plan and build the NAS on VLAN 40.** Planned; [ADR-0016](adr/0016-open-casabonita-inward-and-keep-it-terminal-outward.md) @@ -400,7 +426,10 @@ months. **Infrastructure** above, because it has a shape now rather than only a decision. - **[#103](https://github.com/Gerrrt/HomeLab/issues/103)** The SSO deferral - ADR-0008 takes knowingly — give it an expiry. + ADR-0008 takes knowingly, given an expiry by + [ADR-0022](adr/0022-expire-the-sso-deferral-when-the-tier-holds-real-data.md). + Under **Security** above, because it has a condition now rather than only a + decision. - **[#105](https://github.com/Gerrrt/HomeLab/issues/105)** Confirm the unconfigured Snort package actually went. diff --git a/docs/security.md b/docs/security.md index 4800e0d..49eda09 100644 --- a/docs/security.md +++ b/docs/security.md @@ -28,6 +28,22 @@ domain or by port says why not for the lab, and the reason generalises), and no MFA on the internal services. +**The MFA gap has an expiry now, and it is not one gap.** +[ADR-0022](adr/0022-expire-the-sso-deferral-when-the-tier-holds-real-data.md) +ends [ADR-0008](adr/0008-place-services-by-data-trust.md)'s SSO deferral on a +state rather than leaving it open: the first real secret, photo or document in +the sensitive tier, any reachability from outside the house, or a third account +holder — whichever comes first. Until then the floor is per-application TOTP, +and it does not reach everything. Vaultwarden, Paperless-ngx and Home Assistant +can each carry a second factor; **Grafana, Immich and AdGuard Home cannot** — +Grafana OSS has no MFA in any edition, Immich's upstream has declined it and +points at OAuth, and AdGuard has one password-only admin account. For those +three an identity provider is the only route to a second factor rather than a +heavier alternative to one. **Grafana is the only one of the six deployed +today**, which makes "no MFA" a standing property of the estate rather than a +pending piece of work — it closes when an identity provider exists and not +before. + **Intrusion detection has been running** on **Skids (VLAN 20)** since 2026-08-21 and on **Degens (VLAN 10)** since 2026-09-02, one Suricata process per interface.