diff --git a/app.py b/app.py index 19990a3..fd86e38 100644 --- a/app.py +++ b/app.py @@ -15,7 +15,7 @@ from uuid import UUID, uuid4 import psycopg from psycopg.types.json import Jsonb -from fastapi import FastAPI, Header, Request +from fastapi import FastAPI, Header, HTTPException, Request from pydantic import BaseModel from fastapi.responses import FileResponse @@ -54,6 +54,12 @@ MINT_ISO_PATH = str(Path(os.environ.get("TUXFLOTTE_MINT_ISO_PATH", "data/upstrea # zum Subprocess-cwd (TUXFLOTTE_INSTALLER_DIR) interpretiert, nicht relativ # zum eigenen Arbeitsverzeichnis. ISO_BUILD_OUTPUT_DIR = Path(os.environ.get("TUXFLOTTE_ISO_BUILD_OUTPUT_DIR", "data/iso-builds")).resolve() +# Golden Images (ADR-0024): bewusst unauthentifiziert, analog zu +# /installers/fedora-workstation/ks.cfg - werden von backend_launch() +# per curl geladen, bevor das Zielgeraet irgendein Token besitzt. Kein +# personalisierter/kundenspezifischer Inhalt (anders als ISO-Builds oben), +# nur ein generisches Basis-Root-Dateisystem pro Distribution. +GOLDEN_IMAGES_DIR = Path(os.environ.get("TUXFLOTTE_GOLDEN_IMAGES_DIR", "data/golden-images")).resolve() ISO_BUILD_TIMEOUT_SECONDS = 1800 # ISO-Ablauf nach 7 Tagen (Migration 0021): ergaenzt die bestehende # "neuer Build ersetzt alten"-Aufraeumung um eine reine Zeitdimension. @@ -2577,6 +2583,22 @@ def get_fedora_kickstart(): ) +@app.get("/golden-images/{filename}") +def get_golden_image(filename: str): + # Path-Traversal-Schutz: Path(...).name kappt jeden "../"-Anteil, der + # Vergleich mit dem Original-Parameter erkennt den Versuch und lehnt ihn + # ab, statt ihn still auf einen anderen Dateinamen umzubiegen. + safe_name = Path(filename).name + if safe_name != filename: + raise HTTPException(status_code=400, detail="invalid_filename") + + file_path = GOLDEN_IMAGES_DIR / safe_name + if not file_path.is_file(): + raise HTTPException(status_code=404, detail="not_found") + + return FileResponse(file_path, media_type="application/zstd") + + @app.post("/api/v1/agent/bootstrap") def agent_bootstrap(payload: AgentBootstrapRequest): agent_secret = secrets.token_urlsafe(32)