8.9 KiB
status, date, decision-makers, consulted, informed
| status | date | decision-makers | consulted | informed | ||
|---|---|---|---|---|---|---|
| accepted | 2026-10-11 |
|
ADR 0005: Personal Data Isolation, Authentik Authentication Boundaries, and Hosting Segregation
Context and Problem Statement
On 2026-10-11, during the development of mobile-first PWA dashboards for baby monitoring (Mila Graf) and personal nutrition/Garmin energy balance (Moritz Graf), personal health, sleep, diaper, and nutrition data was temporarily made available at unauthenticated root URLs:
https://hosting.haumdaucher.de/mila.htmlhttps://hosting.haumdaucher.de/moritz.html
While ADR 0004 established the conceptual routing design (/family/* protected by Traefik ForwardAuth, public recipes at /recipes/*), a gap analysis revealed critical systemic vulnerabilities:
- Unapplied Kubernetes Ingress: The ForwardAuth Ingress (
minio-hosting-family) declared ink8s/minio/hosting-ingress.yamlwas never applied to the Kubernetes cluster. - Missing Authentik Identity Provider: Authentik had no Proxy Provider or Application configured for
hosting.haumdaucher.de, returning HTTP 404 on ForwardAuth checks. - Dual-Publishing Anti-Pattern: Dashboard generator scripts (
update_website.py,update_moritz_website.py) mirrored files to root (/mila.html,/moritz.html) for convenience, completely bypassing the planned/family/path boundary. - Lack of Automated Ingress & Storage Auditing: No automated test or CI/Cron audit prevented personal data from being uploaded to public prefixes.
How do we establish a permanent, verifiable, and automated security boundary that guarantees personal and family data can never be exposed publicly without authentication?
Decision Drivers
- Zero Public Exposure for Personal Data: Health, sleep, nutrition, body, and family data must NEVER be accessible without authentication.
- Defense-in-Depth: Security must not rely on a single layer (e.g. secret URLs or developer memory). Protection must be enforced at identity provider, reverse proxy, storage bucket, and generator script levels.
- Declarative Infrastructure as Code (ADR 0003): All Authentik providers, policies, groups, and Kubernetes Ingresses must be versioned in Git and applied via Terraform/Kubectl.
- Automated Guardrails & Fast Feedback: Continuous audits must detect and quarantine any unauthorized files outside protected paths.
- Seamless PWA Experience: The PWA capabilities (manifest, service worker, offline shell) must function within the authenticated cookie session without breaking ForwardAuth.
Decision Outcome
Chosen Strategy: Multi-Layer Boundary Enforcement with ForwardAuth, Strict Storage Segregation, and Continuous Auditing.
1. Storage & URL Path Segmentation Architecture
Internet Request to https://hosting.haumdaucher.de
│
▼
┌───────────────────┐
│ Traefik Ingress │
└─────────┬─────────┘
│
Path Inspection & Priority
│
┌────────────────┴────────────────┐
│ │
Path: /family/* Path: /* (Fallback)
Priority: 100 Priority: 10
│ │
▼ ▼
┌─────────────────────────┐ ┌─────────────────────────┐
│ Authentik ForwardAuth │ │ Public Ingress │
│ • Host: hosting.haumd...│ │ • ONLY /recipes/* │
│ • Allowed: 'family' & │ │ • ONLY /showcase/* │
│ 'infra-admins' groups │ │ • NO personal files │
└────────────┬────────────┘ └────────────┬────────────┘
│ │
HTTP 200 (Authed) │
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ MinIO Bucket │ │ MinIO Bucket │
│ key: family/* │ │ key: public/* │
│ (Protected Data) │ │ (Public Static) │
└──────────────────┘ └──────────────────┘
Rule 1: Canonical Protected Prefix /family/
- All dashboards containing personal, familial, or physiological metrics MUST reside under
/family/:https://hosting.haumdaucher.de/family/mila.html(Baby sleep, feeds, diapers)https://hosting.haumdaucher.de/family/moritz.html(Personal nutrition, Garmin deficits, health)https://hosting.haumdaucher.de/family/nutrition.html(Family nutrition overview)https://hosting.haumdaucher.de/family/sleep.html(Sleep analytics)
- Associated PWA assets (manifests, service workers) MUST also be scoped within
/family/(e.g./family/mila.webmanifest,/family/sw.js).
Rule 2: Root Prefix / Strictly Restricted
- The root of
hosting.haumdaucher.deis restricted to public static assets (/recipes/*,/showcase/*). - Publishing scripts MUST NEVER write personal dashboards to the bucket root.
2. Declarative Authentik ForwardAuth Configuration
Authentik configuration is codified in terraform/authentik.tf:
- Proxy Provider (
authentik_provider_proxy.hosting_family):- External Host:
https://hosting.haumdaucher.de - Mode:
forward_single - Bound to
authentik_outpost.embedded - Session duration: 30 days (
access_token_validity = "days=30")
- External Host:
- Application (
authentik_application.hosting_family):- Slug:
hosting-family - Protocol Provider:
authentik_provider_proxy.hosting_family.id
- Slug:
- Role-Based Policy Bindings:
- Group
family(Moritz & Amelie Graf): Granted access - Group
infra-admins: Granted access - Unauthenticated requests are redirected (HTTP 302) to
https://auth.haumdaucher.de/application/o/authorize/...
- Group
3. Kubernetes Ingress Deployment
k8s/minio/hosting-ingress.yaml defines the two ingress tiers:
minio-hosting-family(Priority 100):- Path:
/family(Prefix) - Middlewares:
auth-authentik-forwardauth@kubernetescrd,minio-hosting-add-prefix@kubernetescrd
- Path:
minio-hosting(Priority 10):- Path:
/(Prefix) - Middlewares:
minio-hosting-add-prefix@kubernetescrd
- Path:
4. Generator Scripts & Codebase Hardening
- Elimination of Root Duplication:
skills/datastore/scripts/update_website.py: Removed all S3 upload and local sync calls to rootmila.html. Target is strictlyfamily/mila.html.skills/datastore/scripts/update_moritz_website.py: Removed S3 upload and local sync calls to rootmoritz.html. Target is strictlyfamily/moritz.html(andfamily/nutrition.html).
- PWA Scope Alignment:
- Web App Manifests configure
start_url: "/family/mila.html"andscope: "/family/". - Service workers are registered at
/family/sw.js.
- Web App Manifests configure
- Telegram Link Consistency:
- Agent prompts for
mila(Topic 20) andkoch(Topic 21) only broadcast the secure URL:https://hosting.haumdaucher.de/family/....
- Agent prompts for
5. Automated Continuous Leak Audit Guard
An automated audit check runs continuously (via OpenClaw periodic audit and datastore maintenance):
- Inspects the MinIO
hostingbucket root (prefix=""). - Detects any forbidden files matching patterns:
*mila*,*moritz*,*nutrition*,*sleep*,*schlaf*,*health*,*diaper*. - If detected:
- Immediately deletes/quarantines the offending object from the public bucket root.
- Raises a critical notification/event to the system log.
Consequences
Positive
- Guaranteed Confidentiality: Family and personal health data is physically impossible to access without passing Authentik ForwardAuth.
- Reproducible Infrastructure: The entire authentication chain is codified in Terraform and Kubernetes manifests.
- Persistent Mobile PWA: Authentik's 30-day session lifetime ensures seamless standalone mobile app operation without repetitive login prompts.
- Automated Safety Net: Even if a developer or agent script mistakenly attempts to mirror a file to root, the automated audit deletes it.
Negative & Mitigations
- Authentication Step on First Mobile Launch:
- Mitigation: When installing the PWA on iOS/Android, the user authenticates once via Authentik. The persistent session cookie maintains login across app launches.