feat(2-Schritt3): nginx-Cutover — App per Token-Link, Admin hinter Authelia
Sicherheits-Architektur umgestellt (live verifiziert): - App-Routen (/, /ws/) ohne Authelia -> Auth via Capability-Token-Cookie (Senioren: Ein-Klick-Link, kein 2FA). Remote-User wird in nginx HART geleert (Anti-Spoofing: App vertraut Remote-User von der Proxy-IP). - /api/admin/* (und /api/admin/log) zusätzlich hinter Authelia (Doppelschutz). - gate_web_ui: statt SSO-Redirect (würde nach Cutover schleifen) eine freundliche "Persönlicher Zugang nötig"-Hinweisseite (401). Endabnahme durch nginx: Link->303+Cookie, /api/me->200 (kein 2FA), Admin->302 Authelia, Header-Spoofing->401. DEPLOYMENT.md 2.8 aktualisiert. Test auf neue Hinweisseite angepasst. 219 passed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
bd55807a1c
commit
6e824d8f9b
3 changed files with 43 additions and 22 deletions
22
app/main.py
22
app/main.py
|
|
@ -5,7 +5,7 @@ from pathlib import Path
|
|||
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from starlette.responses import RedirectResponse, JSONResponse
|
||||
from starlette.responses import RedirectResponse, JSONResponse, HTMLResponse
|
||||
|
||||
from app.config import settings
|
||||
from urllib.parse import urlencode
|
||||
|
|
@ -55,6 +55,19 @@ async def record_metrics(request: Request, call_next):
|
|||
|
||||
|
||||
# Pfade mit eigener Auth, Health-Checks und Favicons: nicht gaten.
|
||||
_NO_ACCESS_PAGE = """<!doctype html><html lang="de"><head><meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"><title>Zugang</title>
|
||||
<style>body{font-family:system-ui,-apple-system,sans-serif;background:#f8fafc;color:#1e293b;
|
||||
display:grid;place-items:center;min-height:100vh;margin:0;padding:1.5rem}
|
||||
.box{max-width:30rem;text-align:center}h1{font-size:1.3rem;margin:0 0 .75rem}
|
||||
p{color:#475569;line-height:1.6;margin:.5rem 0}.emoji{font-size:2.5rem}</style></head>
|
||||
<body><div class="box"><div class="emoji">🔒</div>
|
||||
<h1>Persönlicher Zugang nötig</h1>
|
||||
<p>Bitte öffne die App über deinen persönlichen Zugangslink.</p>
|
||||
<p>Wenn du keinen Link hast, wende dich an deine Betreuungsperson.</p>
|
||||
</div></body></html>"""
|
||||
|
||||
|
||||
_PUBLIC_PREFIXES = ("/api", "/ws", "/health", "/favicon", "/apple-touch-icon")
|
||||
|
||||
|
||||
|
|
@ -83,10 +96,9 @@ async def gate_web_ui(request: Request, call_next):
|
|||
token = (_bearer_token(request.headers.get("authorization"))
|
||||
or k or request.cookies.get(CAPABILITY_COOKIE))
|
||||
if authenticate(request.headers, client_host, token) is None:
|
||||
login = settings.sso_login_url.strip()
|
||||
if login:
|
||||
return RedirectResponse(login, status_code=302)
|
||||
return JSONResponse({"detail": "Authentication required"}, status_code=401)
|
||||
# Kein gültiger Zugang -> freundliche Hinweisseite (KEIN SSO-Redirect, der
|
||||
# nach dem nginx-Cutover in eine Schleife liefe). Senioren nutzen ihren Link.
|
||||
return HTMLResponse(_NO_ACCESS_PAGE, status_code=401)
|
||||
return await call_next(request)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue