diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..0416c17 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,60 @@ +# syntax=docker/dockerfile:1.6 + +FROM python:3.11-slim AS runtime + +ENV PYTHONUNBUFFERED=1 \ + PIP_NO_CACHE_DIR=1 \ + ANSIBLE_FORCE_COLOR=1 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + openssh-client \ + sshpass \ + git \ + curl \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /opt/ansible-ui + +RUN useradd --create-home --shell /bin/bash appuser \ + && mkdir -p \ + /opt/ansible-ui/app \ + /opt/ansible/playbooks \ + /opt/ansible/inventories \ + /opt/ansible/ssh-keys \ + /var/lib/ansible_ui \ + /var/log/ansible_ui \ + && chown -R appuser:appuser \ + /opt/ansible-ui \ + /opt/ansible/playbooks \ + /opt/ansible/inventories \ + /opt/ansible/ssh-keys \ + /var/lib/ansible_ui \ + /var/log/ansible_ui + +RUN pip install --no-cache-dir \ + ansible \ + fastapi \ + uvicorn[standard] \ + jinja2 \ + python-multipart \ + aiofiles + +COPY app /opt/ansible-ui/app + +USER appuser + +ENV DATA_ROOT=/opt/ansible-ui \ + PLAYBOOK_DIR=/opt/ansible/playbooks \ + INVENTORY_DIR=/opt/ansible/inventories \ + SSH_KEY_DIR=/opt/ansible/ssh-keys \ + DB_PATH=/var/lib/ansible_ui/ansible_ui.db \ + LOG_DIR=/var/log/ansible_ui \ + HTPASSWD_PATH=/opt/ansible-ui/.htpasswd + +VOLUME ["/opt/ansible/playbooks", "/opt/ansible/inventories", "/opt/ansible/ssh-keys", "/var/lib/ansible_ui", "/var/log/ansible_ui"] + +EXPOSE 8000 + +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..dec5d92 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,29 @@ +version: "3.9" + +services: + web: + build: + context: .. + dockerfile: docker/Dockerfile + container_name: ansible-webui + restart: unless-stopped + ports: + - "8000:8000" + environment: + DATA_ROOT: /opt/ansible-ui + PLAYBOOK_DIR: /opt/ansible/playbooks + INVENTORY_DIR: /opt/ansible/inventories + SSH_KEY_DIR: /opt/ansible/ssh-keys + DB_PATH: /var/lib/ansible_ui/ansible_ui.db + LOG_DIR: /var/log/ansible_ui + HTPASSWD_PATH: /opt/ansible-ui/.htpasswd + volumes: + - ../app:/opt/ansible-ui/app:ro + - ../data/playbooks:/opt/ansible/playbooks + - ../data/inventories:/opt/ansible/inventories + - ../data/ssh-keys:/opt/ansible/ssh-keys + - ../data/ansible_ui.db:/var/lib/ansible_ui/ansible_ui.db + - ../data/.htpasswd:/opt/ansible-ui/.htpasswd:ro + - ../logs:/var/log/ansible_ui + tty: true + stdin_open: false \ No newline at end of file diff --git a/entwicklung/api_endpoints.md b/entwicklung/api_endpoints.md new file mode 100644 index 0000000..b6ddcc2 --- /dev/null +++ b/entwicklung/api_endpoints.md @@ -0,0 +1,177 @@ +# API Endpoints — Web‑UI für Ansible‑TUI + +Hinweis zur Authentifizierung +- HTTP‑Basic wird extern via .htpasswd gepflegt; Webserver setzt Auth und ggf. REMOTE_USER. +- Die Applikation verifiziert keine internen Benutzer, erwartet ggf. REMOTE_USER zur Audit‑Zuordnung. + +Endpunkte (MVP) + +GET /health +- Beschreibung: Simple Health‑Check +- Antwort: 200 OK + +GET /playbooks +- Beschreibung: Liste aller hochgeladenen Playbooks (Metadaten) +- Query: ?limit=&offset= +- Antwort: JSON Array [{id,name,filename,uploaded_by,uploaded_at}] + +POST /uploads +- Beschreibung: Upload eines ZIP/TAR mit Playbook(s) +- Konsumiert: multipart/form-data file=... +- Validierung: MIME und Dateiendung (zip,tar,tgz), Größenlimit +- Antwort: 201 Created + Metadaten + +GET /playbooks/{id} +- Beschreibung: Metadaten zu einem Playbook +- Antwort: JSON + +POST /playbooks/{id}/run +- Beschreibung: Startet einen Run via systemweiter ansible-playbook CLI. Keine Nutzung von ansible-runner. +- Body: optional JSON {inventory: string, extra_vars: object, tags: [string], become: bool} +- Behaviour: Prozess wird vom Service gestartet; stdout/stderr werden in temporäre Dateien geschrieben. +- Antwort: 202 Accepted {run_id} + +GET /runs/{run_id}/status +- Beschreibung: Statusabfrage (queued,running,success,failed) +- Antwort: JSON {run_id,status,exit_code,started_at,finished_at} + +GET /runs/{run_id}/output +- Beschreibung: Holt vollständiges stdout/stderr des Runs (Text/stream) +- Antwort: text/plain (oder chunked) + +GET /keys +- Beschreibung: Listet referenzierbare SSH‑Keys (nur lesend) +- Antwort: JSON [{key_id,filename,description}] + +GET /logs?limit=&offset= +- Beschreibung: Zugriff auf Run‑Logs / Audit‑Einträge (keine sensiblen Daten) +- Antwort: JSON + +Sicherheits‑Hinweis +- Die Applikation führt keine Änderung an SSH‑Keys oder .htpasswd durch. +- Alle schreibenden Betriebsaufgaben (Permissions, Key‑Rollout, TLS) sind außerhalb des MVP‑Scopes. +## Erweiterung: Onboarding API — Endpoints für automatisches Host‑Onboarding + +Authentifizierung / Autorisierung +- Auth via vorhandene HTTP‑Basic/.htpasswd‑Integration; Webserver stellt REMOTE_USER zur Verfügung. +- Ops‑spezifische Endpoints (z. B. Approve) erfordern REMOTE_USER ∈ Ops‑Gruppe (Ops‑Policy). +- Transport: Produktion erfordert TLS (Ops‑Deployment‑Voraussetzung). + +Zusätzliche Endpoints (Übersicht) +- POST /onboard/requests +- GET /onboard/requests +- GET /onboard/requests/{id}/status +- POST /onboard/requests/{id}/approve +- POST /onboard/requests/{id}/cancel +- GET /onboard/jobs +- GET /onboard/keys +- GET /onboard/requests/{id}/logs + +POST /onboard/requests +- Zweck: Erstellt eine Onboarding‑Anfrage / startet ein Onboarding‑Job (sofern Pre‑Checks erfolgreich sind). +- Auth: REMOTE_USER (any authenticated user) +- Konsumiert: application/json +- Payload: + { + "host": "192.0.2.10", // host oder IP + "port": 22, // optional, default 22 + "login_user": "ubuntu", // Account zur initialen Login‑Phase (sudo‑fähig) + "login_password": "secret", // nur in‑transit; wird nicht persistiert + "desired_user": "ansible", // Benutzer, der angelegt/wenn nötig benutzt wird + "generate_key": true, // true => Server erzeugt Schlüsselpaar (Ed25519/RSA‑4096 fallback) + "public_key": null, // optional, wenn false und Nutzer eigenen Schlüssel liefert + "description": "Kurzinfo", + "options": { // optional: z. B. {"force_approve":false} + "force_approve": false + } + } +- Verhalten: + - Validierung von Pflichtfeldern. + - Erzeugt Onboard‑Request und Onboard‑Job, reply enthält { "job_id": "...", "status": "created" }. + - Wenn Fingerprint nicht mit Ops known_hosts übereinstimmt, wird status = "awaiting_approval". +- Antwort: 201 Created + { + "job_id": "uuid", + "status": "created" | "awaiting_approval" | "queued" + } +- Fehlercodes: + - 400 Bad Request (Validierungsfehler) + - 401 Unauthorized + - 429 Too Many Requests (Rate‑Limit pro User/Host) + +GET /onboard/requests +- Zweck: Listet Onboarding‑Requests (nur Metadaten) +- Query: ?limit=&offset=&status= +- Antwort: JSON Array [{job_id, host, requested_by, status, created_at, fingerprint_sha256?}] + +GET /onboard/requests/{id}/status +- Zweck: Statusabfrage für einen Request +- Antwort: + { + "job_id": "...", + "host": "...", + "status": "created|queued|running|awaiting_approval|approved|onboarded|failed|cancelled", + "retries": 0, + "started_at": "...", + "finished_at": "...", + "error_message": "kurze Fehlerdiagnose (sensitivitäts‑gefiltert)" + } + +POST /onboard/requests/{id}/approve +- Zweck: Ops‑Endpoint zur Freigabe eines abweichenden Host‑Fingerprints +- Auth: REMOTE_USER muss Ops sein (Server‑Side check) +- Payload: + { + "approve": true, + "operator_note": "Fingerprint validiert via call", + "operator_id": "ops-username" + } +- Verhalten: Wenn genehmigt, Job wird zur Fortsetzung freigegeben; Audit‑Eintrag mit operator_id erzeugt. +- Antwort: 200 OK { "job_id": "...", "status": "approved" } +- Fehler: 403 Forbidden (wenn nicht Ops), 404 Not Found + +POST /onboard/requests/{id}/cancel +- Zweck: Bricht laufenden/queued Onboarding‑Job ab (z. B. User Abbruch) +- Auth: Requester oder Ops +- Antwort: 200 OK { "job_id": "...", "status": "cancelled" } + +GET /onboard/jobs +- Zweck: Admin/Operator Sicht auf Onboard‑Jobs (Metadaten) +- Filters: status, requested_by, host +- Antwort: Array von job‑Metadaten (keine sensitiven Inhalte) + +GET /onboard/keys +- Zweck: Listet erzeugte Public‑Keys (Metadaten only) +- Antwort: [{ "key_id": "...", "host_id": "...", "public_key_fingerprint": "...", "created_at": "..." }] + +GET /onboard/requests/{id}/logs +- Zweck: Liefert redigierte Logs des Onboarding‑Jobs (Ausgaben, Schritte) +- Sicherheits‑Hinweis: Logs sind gesäubert; keine Passwörter oder private Key‑Material enthalten. +- Antwort: text/plain oder JSON { "lines":[ ... ] } + +Sicherheits‑Hinweise für API‑Design +- Login‑Password darf niemals geloggt werden; im API‑Server mittelbar nur in‑memory übergeben. +- API darf niemals privaten Schlüssel in Responses zurückliefern. +- Alle Endpoints, die sensible Ablaufsteuerung erlauben (approve, cancel), müssen REMOTE_USER‑Checks durchführen. +- Rate‑Limits: Schutz gegen bruteforce / DOS. +- Input‑Sanitization: host/username dürfen keine Shell‑Injection ermöglichen; SSH‑Interaktionen erfolgen kontrolliert. +- Audit: Jeder state‑wechsel (created, awaiting_approval, approved, onboarded, failed, cancelled) schreibt ein Metadaten‑Audit (siehe [`webapp/entwicklung/schema.sql`](webapp/entwicklung/schema.sql:1) Anpassungen). + +Mapping UI ↔ API +- UI‑Form "Host Onboarding" ↟ POST /onboard/requests +- UI‑Ops Dashboard ↟ GET /onboard/requests?status=awaiting_approval +- UI‑Ops Approve Button ↟ POST /onboard/requests/{id}/approve +- UI‑User Status Polling ↟ GET /onboard/requests/{id}/status +- UI‑Logs ↟ GET /onboard/requests/{id}/logs + +Beispiel‑Flow (Kurz) +1. User sendet POST /onboard/requests mit login_password. +2. API antwortet job_id. +3. Onboarding Service führt Pre‑Check aus; bei Fingerprint‑Mismatch → awaiting_approval. +4. Ops Approver ruft POST /onboard/requests/{id}/approve → Service fährt fort. +5. Job schreibt audit_logs und setzt Host Status auf onboarded oder failed. + +Referenzen zur weiteren Anpassung +- Architektur: [`webapp/entwicklung/architektur.md`](webapp/entwicklung/architektur.md:1) +- DB Schema: [`webapp/entwicklung/schema.sql`](webapp/entwicklung/schema.sql:1) +- Roadmap: [`webapp/entwicklung/roadmap.md`](webapp/entwicklung/roadmap.md:1) \ No newline at end of file diff --git a/entwicklung/architektur.md b/entwicklung/architektur.md new file mode 100644 index 0000000..507c768 --- /dev/null +++ b/entwicklung/architektur.md @@ -0,0 +1,149 @@ +# Architektur — Web‑UI für Ansible‑TUI + +Kurzer Überblick +Dieses Dokument beschreibt die finale Architektur für das MVP Web‑UI gemäß dem gewählten Profil: +Minimal — extern gepflegte .htpasswd, direkte ansible‑playbook‑CLI, ZIP/TAR‑Uploads, SSH‑Keys nur abgelegt. + +Authentifizierung +- Nur externe .htpasswd zur HTTP‑Basic‑Authentifizierung. +- Die Applikation validiert Anfragen gegen die Webserver‑auth (keine DB‑Sync, keine interne Benutzerverwaltung). +- Betrieb: Ops pflegt .htpasswd; die Applikation nimmt keine Änderungen an .htpasswd vor. + +Executor +- Playbooks werden durch Aufruf der systemweiten ansible‑playbook CLI ausgeführt. +- Kein Einsatz von ansible‑runner oder eines persistenten Hintergrund‑Executors im MVP. +- Die Applikation erstellt bei Bedarf temporäre Arbeitsverzeichnisse, schreibt erforderliche Metadaten und ruft ansible‑playbook mit den passenden Parametern. + +SSH‑Key‑Handling +- SSH‑Keys werden als Dateien im Dateisystem abgelegt (Uploads oder durch Ops bereitgestellt). +- Die Applikation ändert keine Besitz‑ oder Berechtigungsattribute; Berechtigungsmanagement liegt bei den Betriebsprozessen/Opsteam. +- Keys werden referenziert, aber nicht automatisch verteilt oder in externe Stores synchronisiert. + +Netzwerk / Deployment +- Kein integrierter Reverse‑Proxy oder TLS im MVP. +- Container/Service horcht direkt auf Port 8000 (HTTP). +- TLS, Reverse‑Proxy oder Ingress werden von der Ziel‑Betriebsumgebung bereitgestellt und in späteren Releases integriert. + +Uploads / Artefakte +- Unterstützte Uploads: ZIP und TAR. Eingangsprüfung auf erlaubte Dateitypen und Größenlimits. +- Entpackung erfolgt in sicheren, temporären Verzeichnissen; keine automatische Ausführung von hochgeladenem Code außer kontrollierten Ansible‑Playbooks. + +Persistenz / Datenbank +- Minimales MVP vermeidet eigene Benutzer‑DB. Falls Metadaten nötig sind, begrenzt auf leichtgewichtige lokale Speicherung (z. B. SQLite). +- Keine Speicherung sensitiver Geheimnisse in der Applikations‑DB; Secrets verbleiben unter Ops‑Verwaltung. + +Sicherheitshinweise +- Ops ist verantwortlich für sichere Aufbewahrung und Rollout von .htpasswd und SSH‑Keys. +- Applikationslogs vermeiden sensitive Inhalte; Playbook‑Outputs können ggf. redigiert werden. + +Offene Fragen +- Keine offenen Fragen — alle Entscheidungen sind gemäß dem Minimal‑Profil finalisiert. +## Erweiterung: Automatisches Host‑Onboarding (Feature‑Spec) + +Zweck +- Erlaubt automatisches Onboarding neuer Zielhosts mit wählbaren Optionen: + 1. Pro‑Host SSH‑Key: serverseitig erzeugtes Schlüsselpaar (Ed25519, Fallback RSA‑4096). + 2. Login per Username + temporärem Passwort (Sudo‑fähiger Account) für initiale Berechtigungsaufgaben. + 3. Passwort wird nur einmalig verwendet und danach unverzüglich verworfen. + +Kontext / Referenzen +- Diese Erweiterung baut auf bestehender Architektur auf; siehe Hauptdokumentation und API‑Liste: [`webapp/entwicklung/api_endpoints.md`](webapp/entwicklung/api_endpoints.md:1), DB‑Schema: [`webapp/entwicklung/schema.sql`](webapp/entwicklung/schema.sql:1) und Roadmap: [`webapp/entwicklung/roadmap.md`](webapp/entwicklung/roadmap.md:1). + +Design‑Prinzipien (Policy: Strict Ops‑First) +- Schlüssel: Ed25519 wird erzeugt. Falls Client/Host Ed25519 nicht unterstützt, Fallback RSA‑4096. +- Host‑Fingerprint: strikt — Fingerprint muss mit Ops' known_hosts übereinstimmen oder vorab durch Ops manuell freigegeben werden. Abweichende Fingerprints erzeugen eine Onboarding‑Sperre (Ops‑Review). +- Privater Schlüssel: niemals persistent auf Datenträger; nur im RAM des Onboarding‑Jobs verfügbar, nur für die Dauer des Jobs. +- Passwort: nur im RAM gehalten; nach erfolgreichem Onboarding sofort unwiederbringlich verworfen. Bei Fehlern sind maximal 3 unmittelbare Retries erlaubt, danach Ops‑Intervention. +- Audit: nur Metadaten (username, host, fingerprint‑SHA256, event=start|success|fail, timestamp, operator_id bei manueller Freigabe). Keine Passwörter oder Private‑Key‑Material in Logs oder DB. Audit‑Retention: 365 Tage. + +Komponenten (Erweiterung) +- Onboarding UI (neues Formular, siehe API‑Mapping). +- Onboarding API (neue Endpoints; Auth via HTTP‑Basic/REMOTE_USER). +- Onboarding Service / Job Runner (zuständig für Erzeugung Key, SSH‑Verbindung, sudo‑Aufgaben, Key‑Copy). +- Ops Approval Queue (falls Host‑Fingerprint nicht bekannt). +- DB Erweiterungen (Hosts‑Tabelle, Onboard‑Jobs, Key‑Referenzen, ephemeral markers) — siehe vorgeschlagene Schema‑Änderungen in [`webapp/entwicklung/schema.sql`](webapp/entwicklung/schema.sql:1). +- Audit‑Subsystem: schreibt streng limitierte Events in audit_logs mit Tagging. + +Prozessablauf (High‑Level) +- UI: Benutzer öffnet "Host Onboarding" Formular und gibt ein: hostname/ip, ssh_port (optional), username (für initiales Passwort‑Login), temporäres Passwort (nur wenn Option 2 gewählt), gewünschter Ziel‑Benutzername (z. B. ansible user) und evtl. Beschreibung. +- API: POST /onboard/requests erzeugt Onboard‑Request, validiert Felder, erstellt Onboard‑Job und antwortet mit job_id. +- Pre‑Checks: Onboarding Service prüft Erreichbarkeit (TCP), prüft Host‑Fingerprint gegen Ops known_hosts. + - Falls Fingerprint unbekannt/abweichend: Job wird in Ops‑Approval‑Queue gestellt; API gibt status=awaiting_approval zurück. + - Falls Fingerprint OK: Fortsetzung. +- Key Generation: Service erzeugt temporäres Schlüsselpaar (Ed25519 oder RSA‑4096); privater Schlüssel bleibt nur in‑memory. +- SSH Login: Service stellt SSH‑Sitzung mit provided username+password auf. Die Login‑Phase verwendet sichere in‑memory Credentials; Zeitüberschreitung/Fehler führen zu Retry (max 3). +- Privilegien: Service führt via sudo (password prompted, one‑time use) die notwendigen Befehle aus (Erstellen Zieluser, setfacl/chown, Erstellen ~/.ssh, Deploy public key). +- Key‑Copy: öffentlicher Schlüssel wird in das target user's authorized_keys geschrieben; Dateirechte gesetzt. +- Verifizieren: Service versucht Login per SSH‑Key (key auth) und optional überprüft sudo‑Ausführung ohne Passwort (wenn operator gewünscht). +- Abschluss: Bei Erfolg markiert Job als completed, privater Schlüssel wird verworfen, Onboard‑Result in DB/Audit geschrieben. Bei Fehlern: bis 3 Retries, danach markiert als failed und Ops benachrichtigt. +- Cleanup: temporäre Verzeichnisse/Handles werden gelöscht, keine sensible Persistenz. + +Mermaid: Ablaufdiagramm (vereinfachte Darstellung) +``` +graph TB + UI[User nutzt Onboarding‑Formular] + API[POST /onboard/requests] + JOB[Onboarding Service: Job erstellt] + PRECHK[Pre‑Checks: TCP + Fingerprint vs Ops known_hosts] + APPROVE[Ops Approval Queue] + KEYGEN[Key‑Erzeugung: Ed25519 / RSA‑4096] + SSHLOGIN[SSH Login mit User+Passwort (RAM)] + SUDO[Sudo‑Aufgaben: create user, setup ~/.ssh, copy pubkey] + VERIFY[Verify key auth] + SUCCESS[Onboarding success -> Audit + DB] + FAIL[Onboarding failed -> Audit + Ops Benachrichtigung] + + UI --> API --> JOB --> PRECHK + PRECHK -- match --> KEYGEN --> SSHLOGIN --> SUDO --> VERIFY --> SUCCESS + PRECHK -- mismatch --> APPROVE --> JOB + VERIFY -- ok --> SUCCESS + VERIFY -- nok --> FAIL +``` + +Schnittstellen / Integration (Kurz) +- Neue API Endpoints (voll spezifiziert in [`webapp/entwicklung/api_endpoints.md`](webapp/entwicklung/api_endpoints.md:1)): + - POST /onboard/requests — erstellt Onboarding‑Request, Payload enthält host, port, username, password (nur in‑transit), desired_user, options (generate_key: true/false), description. + - GET /onboard/requests/{id}/status — Jobstatus, ops approval indicator. + - POST /onboard/requests/{id}/approve — Ops endpoint zum Freigeben eines Fingerprints (Auth: REMOTE_USER muss Ops sein). + - GET /onboard/keys — listet public keys erzeugt (readonly metadata). + - GET /onboard/jobs — Liste der Onboarding Jobs (nur Metadaten). +- Sicherheit: Passwords ONLY accepted via HTTPS (Ops deployment requirement). App muss niemals persistieren Passwörter; API akzeptiert Passwort im Request Body, Job Runner liest ihn und löscht danach. + +DB‑Änderungen (high level; detail in schema.sql) +- Neue Tabelle hosts: + - id, hostname/ip, primary_key_id (FK), fingerprint_sha256, onboarded_at, onboarded_by, status (pending/awaiting_approval/onboarded/failed), created_at +- Neue Tabelle onboard_jobs: + - id, host_id, requested_by, desired_user, options_json, retries, status, started_at, finished_at, error_message +- ssh_keys erweitert / referenziert (public key metadata only; private key never stored) +- audit_logs Nutzung: nur Metadaten (wie oben) + +Sicherheits‑ und Audit‑Aspekte (Details) +- Passwort‑Handling: + - Password im API‑Request wird unmittelbar dem Job übergeben und nur im RAM gehalten. + - Nach Jobabschluss oder nach Fehlern/Timeouts wird der Speicher unverzüglich ohne Swap/Logging freigegeben. + - Implementationshinweis: nutze sichere Speicher‑APIs (mmap mit mlock wenn möglich) oder Host‑Mechanismen, um Swapping zu verhindern. +- Private Key Handling: + - Erzeugung im Job‑Prozess oder in isolierter Secure Enclave; privater Key nie auf Disk. + - Wenn Ops‑Review/Transfer erforderlich, Key kann kurzfristig (maximal Job‑Dauer) in verschlüsseltem Prozess‑Speicher gehalten werden; standardmäßig verboten. +- Fingerprint Handling: + - Vergleich gegen Ops known_hosts (Ops liefert Pfad oder API für known_hosts‑Hash). + - Bei Mismatch: keine automatische Akzeptanz; Ops‑Approval erforderlich. +- Auditing: + - Erfasste Felder: event_type, job_id, host, fingerprint_sha256, requested_by, operator_id (bei approval), short status message, timestamp. + - Keine Speicherung sensibler Inhalte (kein Passwort, kein privater Key). + - Audit‑Retention: 365 Tage; älterer Einträge werden anonymisiert oder gelöscht gemäß Ops‑Policy. +- Log‑Sanitization: + - Playbook/SSH Outputs werden auf sensitive Patterns geprüft/redigiert bevor sie persistiert oder angezeigt werden. + +Betriebsverantwortung / Ops‑Aufgaben +- Ops liefert trusted known_hosts repository oder API‑Endpoint zur Fingerprint‑Prüfung. +- Ops sorgt für Transport‑TLS und sichere Deployment‑Konfiguration (no plaintext HTTP in Prod). +- Ops prüft und genehmigt Host‑Fingerprints in Approval‑Queue. +- Ops führt periodische Reviews/Key‑Rotation durch (nicht automatisch durch App). + +Nächste Schritte (Dokumentation) +- API‑Specs aktualisieren: [`webapp/entwicklung/api_endpoints.md`](webapp/entwicklung/api_endpoints.md:1) +- Schema‑Änderungen in: [`webapp/entwicklung/schema.sql`](webapp/entwicklung/schema.sql:1) +- Roadmap: neue Phase "Onboarding" mit Aufwandsschätzung und Meilensteine: [`webapp/entwicklung/roadmap.md`](webapp/entwicklung/roadmap.md:1) + +(Ende Erweiterung) \ No newline at end of file diff --git a/entwicklung/roadmap.md b/entwicklung/roadmap.md new file mode 100644 index 0000000..798c11f --- /dev/null +++ b/entwicklung/roadmap.md @@ -0,0 +1,57 @@ +# Roadmap — Web-UI für Ansible-TUI (Minimal-Profil) + +## Überblick +- Ziel: Bereitstellung einer schlanken Web-UI, die Ansible-Playbooks per CLI startet. +- Authentifizierung erfolgt ausschließlich via extern gepflegter .htpasswd. +- SSH-Keys werden nur abgelegt und referenziert; Berechtigungen bleiben bei Ops. +- Keine Reverse-Proxy/TLS-Komponente im MVP; Service lauscht direkt auf Port 8000. +- Hochgeladene Artefakte: ZIP/TAR. +- Geplante Erweiterung: Automatisches Host-Onboarding (Strict Ops-First Policy) nach MVP-Release, abgestimmt mit [`webapp/entwicklung/architektur.md`](webapp/entwicklung/architektur.md:1) und [`webapp/entwicklung/api_endpoints.md`](webapp/entwicklung/api_endpoints.md:1). + +## Phase 0 – Vorbereitung (Woche 1) +- Container-Basisimage finalisieren, Port 8000 exponieren. +- Ordnerstruktur für Uploads, temporäre Runs und SSH-Key-Ablage anlegen. +- Integration der Webserver-Basic-Auth (Übernahme REMOTE_USER, Fehlermeldungen). +- Dokumentation für Ops zur Pflege der .htpasswd erstellen. + +## Phase 1 – Implementierung Kernfunktionen (Woche 2–3) +- Datei-Uploads (ZIP/TAR) inkl. Validierung implementieren. +- Playbook-Metadaten persistieren laut [`schema.sql`](webapp/entwicklung/schema.sql:1). +- CLI-Executor: Wrapper für ansible-playbook inkl. Parameteraufbereitung. +- Run-Verwaltung (Status, Logpfade) bauen. +- Read-only-API zur SSH-Key-Liste implementieren. +- API-Endpunkte gemäß [`api_endpoints.md`](webapp/entwicklung/api_endpoints.md:1) fertigstellen. + +## Phase 2 – Tests & Härtung (Woche 4) +- Funktionale Tests für Upload, Run-Start, Statuspolling. +- CLI-Aufruf unter verschiedenen Inventaren/Variablen validieren. +- Security-Checks: Keine Speicherung sensitiver Daten, Log-Redaktion. +- Belastungstest der Upload/Polling-Flows (einfacher Load-Test). +- Container-Image Review (kein TLS, nur HTTP). + +## Phase 3 – Release & Ops Handover (Woche 5) +- Build-/Release-Pipeline konfigurieren (Container Registry). +- Deployment-Checkliste erstellen (Port 8000, externe TLS-Schicht durch Ops). +- Monitoring-Anbindung (Basis: Health Endpoint, Log-Verfügbarkeit). +- Go-Live-Freigabe und Übergabe an Ops inkl. Betriebsdokumentation. + +## Phase 4 – Automatisches Host-Onboarding (Woche 6–9) +- Architektur- und Security-Review abschließen; Spezifikation finalisieren (Strict Ops-First Policy, siehe [`webapp/entwicklung/architektur.md`](webapp/entwicklung/architektur.md:1)). +- Datenbankmigrationen und Service-Layer implementieren (Hosts, Onboard-Jobs, Key-Metadaten) gemäß [`webapp/entwicklung/schema.sql`](webapp/entwicklung/schema.sql:1). +- Backend: Onboarding-Service & Job-Runner (Key-Erzeugung, Fingerprint-Prüfung, sudo-Tasks) plus API-Endpunkte & Rate-Limits laut [`webapp/entwicklung/api_endpoints.md`](webapp/entwicklung/api_endpoints.md:1). +- UI/UX: Onboarding-Formular, Status-Detailansicht, Ops-Approval-Dashboard inkl. Log-Viewer. +- QA & Security: Integrationstests, Sudo-/SSH-Härtung, Pen-Test light, Memory-Handling-Checks (keine Persistenz von Passwort/Secret). +- Ops Enablement: Prozesse für known_hosts-Sync, Runbooks, Alerting, Eskalationspfade; Schulung der Ops-Gruppe. +- Staging-Dryrun mit realistischem Host; Post-Mortem + Go/No-Go Entscheidung. +- Abhängigkeiten: Abschluss Phase 1–3, verfügbare Ops-Ressourcen für Fingerprint-Freigaben und Sicherheitstests. + +## Post-MVP Backlog +- Integrierter Reverse-Proxy/TLS-Termination. +- Erweiterter Credential-Store oder Secrets-Management. +- Rollenbasierte Zugriffssteuerung jenseits .htpasswd. +- UI-Verbesserungen für Run-Output (Streaming, Filter). +- Automatisierte Key-Rotation inklusive Berechtigungsupdates. +- Self-Service Ops Workflow für Re-Onboarding/Key-Rotation (Erweiterung der Onboarding-Plattform). + +## Offene Fragen +- Keine; Onboarding-Policy (Strict Ops-First) und Sicherheitsvorgaben abgestimmt. \ No newline at end of file diff --git a/entwicklung/schema.sql b/entwicklung/schema.sql new file mode 100644 index 0000000..1512838 --- /dev/null +++ b/entwicklung/schema.sql @@ -0,0 +1,114 @@ +-- Schema für minimale Metadatenhaltung (SQLite-kompatibel) +-- Profil: Minimal — externe .htpasswd, direkte ansible-playbook CLI, SSH-Keys nur abgelegt +-- Hinweis: Keine sensitiven Secrets oder Passwörter werden in dieser DB gespeichert. +-- Erweiterung: Automatisches Host-Onboarding (Strict Ops-First Policy) + +PRAGMA foreign_keys = ON; + +CREATE TABLE playbooks ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + filename TEXT NOT NULL UNIQUE, + uploaded_by TEXT, + uploaded_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE runs ( + id TEXT PRIMARY KEY, + playbook_id INTEGER NOT NULL, + inventory TEXT, + extra_vars_json TEXT, -- JSON-codierte Extra-Variablen + tags_json TEXT, -- JSON-codierte Tags + become INTEGER NOT NULL DEFAULT 0, + status TEXT NOT NULL, -- queued|running|success|failed + exit_code INTEGER, + started_at DATETIME DEFAULT CURRENT_TIMESTAMP, + finished_at DATETIME, + stdout_path TEXT, -- Dateipfad zu stdout + stderr_path TEXT, -- Dateipfad zu stderr + FOREIGN KEY(playbook_id) REFERENCES playbooks(id) ON DELETE CASCADE +); + +-- Host-Onboarding: Hosts und Jobs +CREATE TABLE hosts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + host_identifier TEXT NOT NULL UNIQUE, -- FQDN oder IP + port INTEGER NOT NULL DEFAULT 22, + fingerprint_sha256 TEXT, -- SHA256 des Host-Keys (nur Fingerprint) + fingerprint_status TEXT NOT NULL DEFAULT 'pending', -- pending|verified|mismatch + status TEXT NOT NULL DEFAULT 'pending', -- pending|awaiting_approval|onboarded|failed|cancelled + description TEXT, + requested_by TEXT, + approved_by TEXT, + approved_at DATETIME, + onboarded_at DATETIME, + last_error TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME +); + +CREATE TABLE onboard_jobs ( + id TEXT PRIMARY KEY, -- UUID + host_id INTEGER NOT NULL, + requested_by TEXT NOT NULL, + login_user TEXT NOT NULL, + desired_user TEXT NOT NULL, + options_json TEXT, -- JSON-codierte Optionen/Flags + approval_required INTEGER NOT NULL DEFAULT 0, + approved_by TEXT, + approved_at DATETIME, + status TEXT NOT NULL, -- created|queued|running|awaiting_approval|approved|onboarded|failed|cancelled + retries INTEGER NOT NULL DEFAULT 0, + max_retries INTEGER NOT NULL DEFAULT 3, + started_at DATETIME, + finished_at DATETIME, + error_code TEXT, + error_message TEXT, + last_fingerprint_sha256 TEXT, -- Fingerprint zuletzt geprüft + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY(host_id) REFERENCES hosts(id) ON DELETE CASCADE +); + +CREATE TABLE ssh_keys ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + key_id TEXT NOT NULL UNIQUE, + host_id INTEGER UNIQUE, -- Pro Host genau ein aktiver Key + key_type TEXT NOT NULL, -- ed25519|rsa4096|external + public_key TEXT, -- Öffentlicher Schlüssel (optional für extern verwaltete Keys) + fingerprint_sha256 TEXT NOT NULL UNIQUE, + managed_externally INTEGER NOT NULL DEFAULT 0, -- 0: App erzeugt/verwalten, 1: Ops verwaltet + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY(host_id) REFERENCES hosts(id) ON DELETE CASCADE +); + +CREATE TABLE audit_logs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + run_id TEXT, + onboard_job_id TEXT, + host_id INTEGER, + category TEXT NOT NULL, + message TEXT NOT NULL, + metadata_json TEXT, -- JSON-kodierte Zusatzinfos (z. B. Fingerprint-Hash) + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY(run_id) REFERENCES runs(id) ON DELETE SET NULL, + FOREIGN KEY(onboard_job_id) REFERENCES onboard_jobs(id) ON DELETE SET NULL, + FOREIGN KEY(host_id) REFERENCES hosts(id) ON DELETE SET NULL +); + +CREATE INDEX idx_runs_playbook ON runs(playbook_id); +CREATE INDEX idx_runs_status ON runs(status); + +CREATE INDEX idx_hosts_status ON hosts(status); +CREATE INDEX idx_hosts_fingerprint_status ON hosts(fingerprint_status); + +CREATE INDEX idx_onboard_jobs_host ON onboard_jobs(host_id); +CREATE INDEX idx_onboard_jobs_status ON onboard_jobs(status); + +CREATE INDEX idx_ssh_keys_fingerprint ON ssh_keys(fingerprint_sha256); + +CREATE INDEX idx_audit_logs_run ON audit_logs(run_id); +CREATE INDEX idx_audit_logs_job ON audit_logs(onboard_job_id); +CREATE INDEX idx_audit_logs_host ON audit_logs(host_id); + +-- Alle Betriebsaufgaben (Pflege .htpasswd, TLS, SSH-Key Berechtigungen) liegen außerhalb der Applikation. +-- Onboarding-Policy: Privater Schlüssel und Passwörter werden nicht persistiert; nur Fingerprints und Metadaten. \ No newline at end of file