From bf7431d672c52e55cd94d84b62347418a50c6d61 Mon Sep 17 00:00:00 2001 From: spektr Date: Sun, 20 Sep 2026 01:04:13 +0200 Subject: [PATCH] Ghost.net AI Struktur um Encoding erweitert. --- README.md | 22 +- ansible/README.md | 31 ++- ansible/playbooks/host.yml | 1 + ansible/roles/host_baseline/README.md | 6 +- ansible/roles/host_baseline/defaults/main.yml | 8 - ansible/roles/workload_accounts/README.md | 16 ++ .../roles/workload_accounts/defaults/main.yml | 43 ++++ .../roles/workload_accounts/tasks/main.yml | 207 ++++++++++++++++++ docs/GNAI-000-DOC_Index.md | 68 ++++-- docs/{ => ai}/GNAI-003-INFRA_Architecture.md | 4 +- .../GNAI-007-PLAN_Ansible-Deployment.md | 122 +++++++---- docs/ai/README.md | 15 ++ .../GNAI-001-INFRA_BaseSystem.md | 0 docs/{ => platform}/GNAI-002-INFRA_Users.md | 20 +- .../GNAI-004-SEC_Access-Control.md | 4 +- .../GNAI-005-SEC_Reverse-Proxy.md | 2 +- docs/{ => platform}/GNAI-006-SEC_VPN.md | 2 +- .../GNAI-006-SEC_VPN_Client-Management.md | 0 .../GNAI-006-SEC_VPN_Clients_Linux.md | 0 .../GNAI-006-SEC_VPN_Clients_Windows11.md | 0 .../GNAI-006-SEC_VPN_Implementation-Log.md | 4 +- .../GNAI-006-profiles/.gitignore | 0 docs/platform/README.md | 21 ++ docs/video-encoding/GNENC-000-DOC_Index.md | 32 +++ .../GNENC-001-PLAN_Architecture.md | 142 ++++++++++++ 25 files changed, 668 insertions(+), 102 deletions(-) create mode 100644 ansible/roles/workload_accounts/README.md create mode 100644 ansible/roles/workload_accounts/defaults/main.yml create mode 100644 ansible/roles/workload_accounts/tasks/main.yml rename docs/{ => ai}/GNAI-003-INFRA_Architecture.md (96%) rename docs/{ => ai}/GNAI-007-PLAN_Ansible-Deployment.md (86%) create mode 100644 docs/ai/README.md rename docs/{ => platform}/GNAI-001-INFRA_BaseSystem.md (100%) rename docs/{ => platform}/GNAI-002-INFRA_Users.md (84%) rename docs/{ => platform}/GNAI-004-SEC_Access-Control.md (95%) rename docs/{ => platform}/GNAI-005-SEC_Reverse-Proxy.md (99%) rename docs/{ => platform}/GNAI-006-SEC_VPN.md (98%) rename docs/{ => platform}/GNAI-006-SEC_VPN_Client-Management.md (100%) rename docs/{ => platform}/GNAI-006-SEC_VPN_Clients_Linux.md (100%) rename docs/{ => platform}/GNAI-006-SEC_VPN_Clients_Windows11.md (100%) rename docs/{ => platform}/GNAI-006-SEC_VPN_Implementation-Log.md (97%) rename docs/{ => platform}/GNAI-006-profiles/.gitignore (100%) create mode 100644 docs/platform/README.md create mode 100644 docs/video-encoding/GNENC-000-DOC_Index.md create mode 100644 docs/video-encoding/GNENC-001-PLAN_Architecture.md diff --git a/README.md b/README.md index a9b6727..e2459e4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Dokumentation für die Infrastruktur eines privaten Homelabs mit öffentlich erreichbaren Webdiensten, einem WireGuard-Zugang und einem dedizierten KI-Server. Dieses Repository hält Architekturentscheidungen, den belegten Betriebsstand und Verfahren für Einrichtung, Betrieb und Sicherheitsabnahme fest. -> **Projektstand: 20.09.2026.** Die Dokumentationsrevisionen sind zusammengeführt. Mehrere Sicherheitsmaßnahmen und technische Endabnahmen stehen noch aus. Auf dem KI-Server sind die gemeinsame Host-Baseline und Podman eingerichtet; der LLM-Anwendungsstack ist noch nicht installiert. +> **Projektstand: 20.09.2026.** Die Dokumentationsrevisionen sind zusammengeführt. Mehrere Sicherheitsmaßnahmen und technische Endabnahmen stehen noch aus. Auf dem KI-Server sind Host-Baseline, Podman und getrennte rootless Workload-Konten eingerichtet; LLM- und Encoding-Anwendungen sind noch nicht installiert. ## Überblick @@ -26,7 +26,7 @@ Die drei vorgesehenen VPN-Netzwerkrollen sind **Familie** (gesamtes Heimnetz), * | Bereich | Belegter Stand | Nächster Nachweis oder Schritt | | --- | --- | --- | -| KI-Server | Debian-Basissystem, verifizierter Deployment-Zugang, minimale Host-Baseline und Podman 5.4.2 mit geprüftem Quadlet-Generator | Ausführungsmodell und Service-Accounts für Production und Testing festlegen; danach GPU-Laufzeit prüfen | +| KI-Server | Debian-Basissystem, Deployment-Zugang, Host-Baseline, Podman 5.4.2 und drei getrennte rootless Workload-Konten | GPU-Zugriff für KI prüfen sowie Encoding-Zeitfenster und Ressourcenlimits festlegen | | VPN | wg-easy v15.4.0 auf CT 105 aktiv; ein externer Laptop-Teiltest liegt vor | Rollenrechte serverseitig umsetzen und mit erlaubten sowie gesperrten Zielen testen | | Reverse Proxy | NPM auf CT 103 dokumentiert; Fail2Ban laut Betreiber fest gepinnt und wieder in Betrieb | Effektive Jails, Sperren und Wiederanlauf prüfen | | Identität | Keycloak/OIDC laut Betreiber produktiv für Vaultwarden | Öffentlich erreichbare Keycloak-Administration sperren und den OIDC-Login erneut prüfen | @@ -38,21 +38,15 @@ Der [Dokumentationsindex](docs/GNAI-000-DOC_Index.md) enthält die datierten Que | Einstieg | Inhalt | | --- | --- | | [Dokumentationsindex und Projektstand](docs/GNAI-000-DOC_Index.md) | Dokumentenübersicht, bestätigte Entscheidungen und offene Befunde | -| [Zielarchitektur](docs/GNAI-003-INFRA_Architecture.md) | Komponenten, Datenwege und geplante Sicherheitszonen | -| [Zugriffsmodell](docs/GNAI-004-SEC_Access-Control.md) | Rollen, Zugriffsmatrix und Authentifizierung | -| [Basissystem](docs/GNAI-001-INFRA_BaseSystem.md) und [Benutzer, Netz und SSH](docs/GNAI-002-INFRA_Users.md) | KI-Server-Basis und zugehörige Prüfungen | -| [Reverse Proxy und Fail2Ban](docs/GNAI-005-SEC_Reverse-Proxy.md) | Konfiguration, Keycloak-Korrektur und ausstehende Abnahme | -| [WireGuard-Gateway](docs/GNAI-006-SEC_VPN.md) | Gateway-Aufbau, Ist-Stand und Rollen-Firewall | -| [Client-Verwaltung](docs/GNAI-006-SEC_VPN_Client-Management.md) | Profile ausgeben, ändern, widerrufen und Rollen prüfen | -| [Windows 11](docs/GNAI-006-SEC_VPN_Clients_Windows11.md) und [Linux](docs/GNAI-006-SEC_VPN_Clients_Linux.md) | Einrichtung und Nutzung auf Endgeräten | -| [WireGuard-Implementation-Log](docs/GNAI-006-SEC_VPN_Implementation-Log.md) | Historie, Tests und Korrekturen früherer Befunde | -| [Ansible-Deployment-Planung](docs/GNAI-007-PLAN_Ansible-Deployment.md) | nicht finaler Arbeitsentwurf zur reproduzierbaren Bereitstellung, Gitea-Integration und geplanten Folgedokumentation | -| [Ansible-Control-Projekt](ansible/README.md) | lokale Einrichtung, Preflight, Deployment-Account, Host-Baseline und Podman-Runtime | +| [Plattform-Dokumentation](docs/platform/README.md) | Hostbasis, Zugriffsmodell, Reverse Proxy und VPN | +| [KI-Dokumentation](docs/ai/README.md) | KI-Zielarchitektur und nicht finale Ansible-Deployment-Planung | +| [Video-Encoding](docs/video-encoding/GNENC-000-DOC_Index.md) | getrennter Planungs- und späterer SOP-Bereich für den CPU-Encoding-Workload | +| [Ansible-Control-Projekt](ansible/README.md) | lokale Einrichtung, Preflight, Deployment-Account, Host-Baseline, Podman und Workload-Konten | ## Arbeiten mit dem Repository -Dieses Repository enthält Dokumentation und ein Ansible-Control-Projekt. Es prüft Inventar, SSH-Zugang, Hostidentität, Betriebssystem und die dokumentierten GPUs. Gemeinsame Rollen verwalten die Konfigurations- und Datenwurzeln sowie Podman und Quadlet. Images, Container und der KI-Anwendungsstack sind noch nicht bereitgestellt. Vor einer Infrastrukturänderung den Ist-Stand am betroffenen Host prüfen, die passende Fachanleitung lesen und die dort genannten Abnahmeschritte dokumentieren. +Dieses Repository enthält Dokumentation und ein Ansible-Control-Projekt. Es prüft Inventar, SSH-Zugang, Hostidentität, Betriebssystem und die dokumentierten GPUs. Gemeinsame Rollen verwalten Plattform, Podman und getrennte Workload-Identitäten. KI- und Encoding-Fachdokumentation bleiben in getrennten Bereichen; Images und Container sind noch nicht bereitgestellt. Vor einer Infrastrukturänderung den Ist-Stand am betroffenen Host prüfen, die passende Fachanleitung lesen und die dort genannten Abnahmeschritte dokumentieren. -WireGuard-Profile enthalten private Schlüssel. Sie gehören nicht in Git, Ausgaben oder freigegebene Dokumente. Das lokale Verzeichnis `docs/GNAI-006-profiles/` schließt Profil-Dateien über eine eigene `.gitignore` aus; es ist kein Verteiler- oder Backup-Ort für Zugangsdaten. +WireGuard-Profile enthalten private Schlüssel. Sie gehören nicht in Git, Ausgaben oder freigegebene Dokumente. Das lokale Verzeichnis `docs/platform/GNAI-006-profiles/` schließt Profil-Dateien über eine eigene `.gitignore` aus; es ist kein Verteiler- oder Backup-Ort für Zugangsdaten. Änderungen am Projektstand sollten zuerst im zuständigen Fachdokument mit Datum und Beleg erfasst werden. Anschließend den [Index](docs/GNAI-000-DOC_Index.md) und diese README aktualisieren, wenn sich eine übergreifende Aussage geändert hat. diff --git a/ansible/README.md b/ansible/README.md index 5cf1f0d..bfe2c8d 100644 --- a/ansible/README.md +++ b/ansible/README.md @@ -106,13 +106,14 @@ unprivileged SSH identity and non-interactive escalation to effective UID 0. ## Shared host baseline -The host baseline is common to Production and Testing. It verifies the target -identity and at least 100 GiB free on the root filesystem, then manages these -root-owned directory roots: +The host baseline is common to all workloads. It verifies the target identity +and at least 100 GiB free on the root filesystem, then manages these root-owned +parent and shared-data roots: ```text -/etc/ghostnet-ai/{production,testing} -/var/lib/ghostnet-ai/{production,testing,shared} +/etc/ghostnet-ai +/var/lib/ghostnet-ai +/var/lib/ghostnet-ai/shared ``` It does not install software or start services. Use the dedicated host @@ -147,7 +148,23 @@ overlay storage driver and exercises the installed Quadlet generator without creating a workload. The role explicitly keeps the Podman API socket and automatic image-update -timer disabled. It pulls no image and does not yet choose rootful or rootless -execution for the two environments. Apply and check it through the same +timer disabled. It pulls no image. Apply and check it through the same `playbooks/host.yml` commands shown above. The first run may install packages; an immediate second run must report `changed=0`. + +## Workload accounts + +The shared host play creates three locked service identities with deterministic +UID/GID and subordinate-ID ranges: + +| Account | UID/GID | Purpose | Rootless Podman graph root | +| --- | --- | --- | --- | +| `gnai-production` | 980 | stable AI Production | `/var/lib/ghostnet-ai/production/.local/share/containers/storage` | +| `gnai-testing` | 981 | temporary AI Testing | `/var/lib/ghostnet-ai/testing/.local/share/containers/storage` | +| `gn-encoding` | 982 | scheduled CPU video encoding | `/var/lib/ghostnet-encoding/.local/share/containers/storage` | + +Each account has a persistent systemd user manager and a separate, +administrator-managed directory below `/etc/containers/systemd/users/`. +Configuration is root-owned and group-readable; workload data belongs to its +service account. The role does not grant GPU access and creates no workload +containers. diff --git a/ansible/playbooks/host.yml b/ansible/playbooks/host.yml index e778e5e..6cfc630 100644 --- a/ansible/playbooks/host.yml +++ b/ansible/playbooks/host.yml @@ -11,3 +11,4 @@ roles: - role: host_baseline - role: podman_runtime + - role: workload_accounts diff --git a/ansible/roles/host_baseline/README.md b/ansible/roles/host_baseline/README.md index c7da041..460e1ca 100644 --- a/ansible/roles/host_baseline/README.md +++ b/ansible/roles/host_baseline/README.md @@ -8,8 +8,10 @@ Its first version deliberately has a narrow scope: - refuse changes when hostname, Debian major version or primary IPv4 address do not match the declared target; - require at least 100 GiB free on the root filesystem; and -- create root-owned configuration and persistent-data directory roots for both - environments and future shared data. +- create root-owned parent directories and the future shared-data root. + +Environment-specific directory ownership is managed by `workload_accounts` so +the shared baseline and workload identities do not compete over permissions. It does not install packages, drivers, Podman or applications and does not start or stop services. Ownership below the directory roots will be assigned diff --git a/ansible/roles/host_baseline/defaults/main.yml b/ansible/roles/host_baseline/defaults/main.yml index 020eb69..2144ff2 100644 --- a/ansible/roles/host_baseline/defaults/main.yml +++ b/ansible/roles/host_baseline/defaults/main.yml @@ -10,15 +10,7 @@ host_baseline_minimum_root_free_gib: 100 host_baseline_directories: - path: /etc/ghostnet-ai mode: "0755" - - path: /etc/ghostnet-ai/production - mode: "0750" - - path: /etc/ghostnet-ai/testing - mode: "0750" - path: /var/lib/ghostnet-ai mode: "0755" - - path: /var/lib/ghostnet-ai/production - mode: "0750" - - path: /var/lib/ghostnet-ai/testing - mode: "0750" - path: /var/lib/ghostnet-ai/shared mode: "0755" diff --git a/ansible/roles/workload_accounts/README.md b/ansible/roles/workload_accounts/README.md new file mode 100644 index 0000000..3be0f85 --- /dev/null +++ b/ansible/roles/workload_accounts/README.md @@ -0,0 +1,16 @@ +# Workload accounts + +The `workload_accounts` role creates separate locked service identities for AI +Production, AI Testing and CPU-based video encoding. Each identity receives a +stable UID/GID, a non-overlapping subordinate ID range, a private data root, an +administrator-managed rootless Quadlet directory and a persistent systemd user +manager. + +Configuration remains owned by root and is readable by the corresponding +service group. Workload data and the rootless Podman store belong to the +service account. The role verifies that all three Podman stores are rootless +and use distinct graph roots. + +The role does not grant GPU device access and does not define containers, +networks, schedules or resource limits. Those states belong to the AI and +video-encoding workload roles. diff --git a/ansible/roles/workload_accounts/defaults/main.yml b/ansible/roles/workload_accounts/defaults/main.yml new file mode 100644 index 0000000..60e5069 --- /dev/null +++ b/ansible/roles/workload_accounts/defaults/main.yml @@ -0,0 +1,43 @@ +--- +workload_accounts: + - name: gnai-production + uid: 980 + gid: 980 + comment: Ghostnet AI Production workload + home: /var/lib/ghostnet-ai/production + config_directory: /etc/ghostnet-ai/production + subid_start: 362144 + supplementary_groups: [] + - name: gnai-testing + uid: 981 + gid: 981 + comment: Ghostnet AI Testing workload + home: /var/lib/ghostnet-ai/testing + config_directory: /etc/ghostnet-ai/testing + subid_start: 427680 + supplementary_groups: [] + - name: gn-encoding + uid: 982 + gid: 982 + comment: Ghostnet video encoding workload + home: /var/lib/ghostnet-encoding + config_directory: /etc/ghostnet-encoding + subid_start: 493216 + supplementary_groups: [] + +workload_subid_count: 65536 +workload_quadlet_user_directory: /etc/containers/systemd/users + +workload_data_directories: + - path: /var/lib/ghostnet-encoding/input + owner: 982 + group: 982 + mode: "0750" + - path: /var/lib/ghostnet-encoding/output + owner: 982 + group: 982 + mode: "0750" + - path: /var/lib/ghostnet-encoding/work + owner: 982 + group: 982 + mode: "0750" diff --git a/ansible/roles/workload_accounts/tasks/main.yml b/ansible/roles/workload_accounts/tasks/main.yml new file mode 100644 index 0000000..d96a43b --- /dev/null +++ b/ansible/roles/workload_accounts/tasks/main.yml @@ -0,0 +1,207 @@ +--- +- name: Verify workload account declarations + ansible.builtin.assert: + that: + - workload_accounts | map(attribute="name") | unique | length == workload_accounts | length + - workload_accounts | map(attribute="uid") | unique | length == workload_accounts | length + - workload_accounts | map(attribute="gid") | unique | length == workload_accounts | length + - >- + workload_accounts | map(attribute="subid_start") | unique | length == + workload_accounts | length + fail_msg: Workload account names and numeric IDs must be unique. + +- name: Create dedicated workload groups + ansible.builtin.group: + name: "{{ workload_account.name }}" + gid: "{{ workload_account.gid }}" + system: true + state: present + loop: "{{ workload_accounts }}" + loop_control: + loop_var: workload_account + label: "{{ workload_account.name }}" + +- name: Create locked workload service accounts + ansible.builtin.user: + name: "{{ workload_account.name }}" + uid: "{{ workload_account.uid }}" + group: "{{ workload_account.name }}" + comment: "{{ workload_account.comment }}" + home: "{{ workload_account.home }}" + shell: /usr/sbin/nologin + groups: "{{ workload_account.supplementary_groups | join(',') }}" + append: false + create_home: true + system: true + password_lock: true + state: present + loop: "{{ workload_accounts }}" + loop_control: + loop_var: workload_account + label: "{{ workload_account.name }}" + +- name: Assign deterministic subordinate UID ranges + ansible.builtin.lineinfile: + path: /etc/subuid + regexp: "^{{ workload_account.name | regex_escape }}:" + line: >- + {{ workload_account.name }}:{{ workload_account.subid_start }}:{{ workload_subid_count }} + owner: root + group: root + mode: "0644" + loop: "{{ workload_accounts }}" + loop_control: + loop_var: workload_account + label: "{{ workload_account.name }}" + +- name: Assign deterministic subordinate GID ranges + ansible.builtin.lineinfile: + path: /etc/subgid + regexp: "^{{ workload_account.name | regex_escape }}:" + line: >- + {{ workload_account.name }}:{{ workload_account.subid_start }}:{{ workload_subid_count }} + owner: root + group: root + mode: "0644" + loop: "{{ workload_accounts }}" + loop_control: + loop_var: workload_account + label: "{{ workload_account.name }}" + +- name: Assign workload data roots to their service accounts + ansible.builtin.file: + path: "{{ workload_account.home }}" + state: directory + owner: "{{ workload_account.uid }}" + group: "{{ workload_account.gid }}" + mode: "0750" + loop: "{{ workload_accounts }}" + loop_control: + loop_var: workload_account + label: "{{ workload_account.home }}" + +- name: Keep workload configuration root-managed + ansible.builtin.file: + path: "{{ workload_account.config_directory }}" + state: directory + owner: root + group: "{{ workload_account.gid }}" + mode: "0750" + loop: "{{ workload_accounts }}" + loop_control: + loop_var: workload_account + label: "{{ workload_account.config_directory }}" + +- name: Create workload-specific data directories + ansible.builtin.file: + path: "{{ workload_directory.path }}" + state: directory + owner: "{{ workload_directory.owner }}" + group: "{{ workload_directory.group }}" + mode: "{{ workload_directory.mode }}" + loop: "{{ workload_data_directories }}" + loop_control: + loop_var: workload_directory + label: "{{ workload_directory.path }}" + +- name: Create the administrator-managed rootless Quadlet root + ansible.builtin.file: + path: "{{ workload_quadlet_user_directory }}" + state: directory + owner: root + group: root + mode: "0755" + +- name: Create separate administrator-managed Quadlet directories + ansible.builtin.file: + path: "{{ workload_quadlet_user_directory }}/{{ workload_account.uid }}" + state: directory + owner: root + group: root + mode: "0755" + loop: "{{ workload_accounts }}" + loop_control: + loop_var: workload_account + label: "{{ workload_account.name }} (UID {{ workload_account.uid }})" + +- name: Enable persistent user managers for rootless Quadlets + ansible.builtin.command: + argv: + - /usr/bin/loginctl + - enable-linger + - "{{ workload_account.name }}" + creates: "/var/lib/systemd/linger/{{ workload_account.name }}" + loop: "{{ workload_accounts }}" + loop_control: + loop_var: workload_account + label: "{{ workload_account.name }}" + +- name: Wait for persistent user managers + ansible.builtin.command: + argv: + - /usr/bin/systemctl + - is-active + - "user@{{ workload_account.uid }}.service" + changed_when: false + register: workload_user_manager_status + retries: 5 + delay: 1 + until: workload_user_manager_status.stdout == "active" + loop: "{{ workload_accounts }}" + loop_control: + loop_var: workload_account + label: "{{ workload_account.name }}" + when: not ansible_check_mode + +- name: Initialize and inspect each rootless Podman store + ansible.builtin.command: + argv: + - /usr/sbin/runuser + - --user + - "{{ workload_account.name }}" + - -- + - /usr/bin/env + - "HOME={{ workload_account.home }}" + - "XDG_RUNTIME_DIR=/run/user/{{ workload_account.uid }}" + - /usr/bin/podman + - info + - --format=json + chdir: "{{ workload_account.home }}" + changed_when: false + register: workload_podman_info_commands + loop: "{{ workload_accounts }}" + loop_control: + loop_var: workload_account + label: "{{ workload_account.name }}" + when: not ansible_check_mode + +- name: Parse rootless Podman information + ansible.builtin.set_fact: + workload_podman_infos: >- + {{ workload_podman_info_commands.results + | map(attribute="stdout") + | map("from_json") + | list }} + when: not ansible_check_mode + +- name: Verify isolated rootless Podman stores + ansible.builtin.assert: + that: + - >- + workload_podman_infos | map(attribute="host.security.rootless") + | select("equalto", true) | list | length == workload_accounts | length + - >- + workload_podman_infos | map(attribute="store.graphRoot") + | unique | list | length == workload_accounts | length + fail_msg: Workload accounts do not have separate rootless Podman stores. + success_msg: All workload accounts use separate rootless Podman stores. + when: not ansible_check_mode + +- name: Report workload account isolation + ansible.builtin.debug: + msg: + accounts: "{{ workload_accounts | map(attribute='name') | list }}" + uids: "{{ workload_accounts | map(attribute='uid') | list }}" + graph_roots: >- + {{ workload_podman_infos | map(attribute="store.graphRoot") | list }} + when: not ansible_check_mode diff --git a/docs/GNAI-000-DOC_Index.md b/docs/GNAI-000-DOC_Index.md index 5c17194..d0082f3 100644 --- a/docs/GNAI-000-DOC_Index.md +++ b/docs/GNAI-000-DOC_Index.md @@ -5,20 +5,35 @@ ## 1. Dokumente und Zielgruppen +### Plattform und Host + | Dokument | Zweck / Zielgruppe | | --- | --- | -| [GNAI-001-INFRA_BaseSystem.md](GNAI-001-INFRA_BaseSystem.md) | Hardware und Debian-Installation; Intermediate IT | -| [GNAI-002-INFRA_Users.md](GNAI-002-INFRA_Users.md) | Benutzer, statisches Hostnetz, Resolver und SSH; Intermediate IT | -| [GNAI-003-INFRA_Architecture.md](GNAI-003-INFRA_Architecture.md) | Gesamtzielarchitektur und Inventar; Intermediate IT | -| [GNAI-004-SEC_Access-Control.md](GNAI-004-SEC_Access-Control.md) | verbindliches Rollen- und Zugriffsziel; Intermediate IT | -| [GNAI-005-SEC_Reverse-Proxy.md](GNAI-005-SEC_Reverse-Proxy.md) | NPM/Fail2Ban und Keycloak-Korrekturverfahren; Intermediate IT | -| [GNAI-006-SEC_VPN.md](GNAI-006-SEC_VPN.md) | wg-easy-Installation/Konfiguration in Proxmox LXC; Intermediate IT | -| [GNAI-006-SEC_VPN_Client-Management.md](GNAI-006-SEC_VPN_Client-Management.md) | neue eigenständige GUI-SOP für Ausgabe, Änderungen und Widerruf; Intermediate IT | -| [GNAI-006-SEC_VPN_Clients_Windows11.md](GNAI-006-SEC_VPN_Clients_Windows11.md) | offizieller Windows-Client, Import und Verwendung; Junior IT | -| [GNAI-006-SEC_VPN_Clients_Linux.md](GNAI-006-SEC_VPN_Clients_Linux.md) | Debian, Arch/CachyOS, Fedora/Kinoite; Junior IT | -| [GNAI-006-SEC_VPN_Implementation-Log.md](GNAI-006-SEC_VPN_Implementation-Log.md) | historische v14/v15-Umsetzung und Evidenzkorrekturen; Intermediate IT | -| [GNAI-007-PLAN_Ansible-Deployment.md](GNAI-007-PLAN_Ansible-Deployment.md) | nicht finaler Arbeitsentwurf zum Ansible-/Gitea-Deployment und zur geplanten Folgedokumentation; Infrastrukturadministratoren und Mitwirkende | -| [Ansible-Control-Projekt](../ansible/README.md) | Control-Node, Preflight, Deployment-Account, Host-Baseline und Podman-Runtime; Intermediate IT | +| [Bereichsindex](platform/README.md) | Einstieg in die Plattform- und Hostdokumentation | +| [GNAI-001-INFRA_BaseSystem.md](platform/GNAI-001-INFRA_BaseSystem.md) | Hardware und Debian-Installation; Intermediate IT | +| [GNAI-002-INFRA_Users.md](platform/GNAI-002-INFRA_Users.md) | Benutzer, statisches Hostnetz, Resolver und SSH; Intermediate IT | +| [GNAI-004-SEC_Access-Control.md](platform/GNAI-004-SEC_Access-Control.md) | verbindliches Rollen- und Zugriffsziel; Intermediate IT | +| [GNAI-005-SEC_Reverse-Proxy.md](platform/GNAI-005-SEC_Reverse-Proxy.md) | NPM/Fail2Ban und Keycloak-Korrekturverfahren; Intermediate IT | +| [GNAI-006-SEC_VPN.md](platform/GNAI-006-SEC_VPN.md) | wg-easy-Installation/Konfiguration in Proxmox LXC; Intermediate IT | +| [GNAI-006-SEC_VPN_Client-Management.md](platform/GNAI-006-SEC_VPN_Client-Management.md) | eigenständige GUI-SOP für Ausgabe, Änderungen und Widerruf; Intermediate IT | +| [GNAI-006-SEC_VPN_Clients_Windows11.md](platform/GNAI-006-SEC_VPN_Clients_Windows11.md) | offizieller Windows-Client, Import und Verwendung; Junior IT | +| [GNAI-006-SEC_VPN_Clients_Linux.md](platform/GNAI-006-SEC_VPN_Clients_Linux.md) | Debian, Arch/CachyOS, Fedora/Kinoite; Junior IT | +| [GNAI-006-SEC_VPN_Implementation-Log.md](platform/GNAI-006-SEC_VPN_Implementation-Log.md) | historische v14/v15-Umsetzung und Evidenzkorrekturen; Intermediate IT | + +### KI + +| Dokument | Zweck / Zielgruppe | +| --- | --- | +| [Bereichsindex](ai/README.md) | Einstieg in die KI-spezifische Dokumentation | +| [GNAI-003-INFRA_Architecture.md](ai/GNAI-003-INFRA_Architecture.md) | Gesamtzielarchitektur und Inventar des KI-Stacks; Intermediate IT | +| [GNAI-007-PLAN_Ansible-Deployment.md](ai/GNAI-007-PLAN_Ansible-Deployment.md) | nicht finaler Arbeitsentwurf zum Ansible-/Gitea-Deployment und zur geplanten Folgedokumentation; Infrastrukturadministratoren und Mitwirkende | + +### Video-Encoding und Automatisierung + +| Dokument | Zweck / Zielgruppe | +| --- | --- | +| [Video-Encoding-Dokumentation](video-encoding/GNENC-000-DOC_Index.md) | getrennter Index für Architektur und spätere Nutzer-SOPs des CPU-Encoding-Workloads | +| [Ansible-Control-Projekt](../ansible/README.md) | Control-Node, Preflight, Deployment-Account, Host-Baseline, Podman und Workload-Konten; Intermediate IT | ## 2. Verbindliche, bestätigte Entscheidungen @@ -26,32 +41,34 @@ NPM ist `.10`, WireGuard `.15`, Proxmox `.2`, KI-Server `.90`, Fritzbox `.1`, je Familie darf das gesamte Heimnetz erreichen; Server Admins Proxmox, Service-LXCs und KI-Server; LLM Admins ausschließlich KI-Server. Profile werden durch Administratoren pro Gerät erstellt und geschützt verteilt. Serverseitige Firewall Allowed IPs sollen die Rechte erzwingen. Per-Client Firewall ist aber derzeit deaktiviert. wg-easy-GUI-Zugriff ist für Familie und Server Admin vorgesehen; HTTPS ist ein offener Umsetzungsvorschlag. -wg-easy läuft rootful in CT 105 als Podman-Quadlet mit v15.4.0 und host-Netz im LXC. Ein lesender SSH-Abgleich am 18.09.2026 bestätigte aktives `wg-easy`/`nftables`, IPv4-Forwarding, eine breite VPN→LAN-Regel und MASQUERADE in `iptables-legacy`; Details und Grenzen stehen im [Implementation-Log](GNAI-006-SEC_VPN_Implementation-Log.md). MASQUERADE wird mangels Rückroute gebraucht. Ein nftables-Reload mit `flush ruleset` kann dynamische Regeln entfernen; Wiederanlauf muss abgenommen werden. +wg-easy läuft rootful in CT 105 als Podman-Quadlet mit v15.4.0 und host-Netz im LXC. Ein lesender SSH-Abgleich am 18.09.2026 bestätigte aktives `wg-easy`/`nftables`, IPv4-Forwarding, eine breite VPN→LAN-Regel und MASQUERADE in `iptables-legacy`; Details und Grenzen stehen im [Implementation-Log](platform/GNAI-006-SEC_VPN_Implementation-Log.md). MASQUERADE wird mangels Rückroute gebraucht. Ein nftables-Reload mit `flush ruleset` kann dynamische Regeln entfernen; Wiederanlauf muss abgenommen werden. -Der KI-Server heißt laut lesendem SSH-Abgleich vom 18.09.2026 `ghostnet-ki`; die Konten sind `ghost`, `debugofen` und das neu angelegte `ghost-agent`. `admin` existiert auf diesem Host nicht; der WireGuard-LXC verwendet separat `admin`. `ghostnet.ki` mit Punkt ist höchstens ein lokal angelegter SSH-Alias. OIDC ist laut Betreiber produktiv nur für Vaultwarden; weitere Dienste sind geplant. +Der KI-Server heißt laut lesendem SSH-Abgleich vom 18.09.2026 `ghostnet-ki`; damals bestanden die persönlichen Konten `ghost`, `debugofen` und das Agentenkonto `ghost-agent`. Inzwischen ergänzen `ansible-deploy` sowie die gesperrten Workload-Konten `gnai-production`, `gnai-testing` und `gn-encoding` diesen Bestand. `admin` existiert auf diesem Host nicht; der WireGuard-LXC verwendet separat `admin`. `ghostnet.ki` mit Punkt ist höchstens ein lokal angelegter SSH-Alias. OIDC ist laut Betreiber produktiv nur für Vaultwarden; weitere Dienste sind geplant. Die getrennten Schlüsselzugänge für `ghost` und `ghost-agent` wurden am 19.09.2026 erfolgreich getestet. Der persönliche Login wurde vom Betreiber interaktiv bestätigt; der Agentenlogin wurde technisch gegen Benutzername und Hostname geprüft. Der KI-Server wurde als frisches Debian-Basissystem begonnen. Inzwischen sind die dokumentierten Ansible-Zugänge, die minimale Host-Baseline und Podman eingerichtet. Die LLM-Anwendungen sind weiterhin nicht installiert; GNAI-003 und GNAI-004 beschreiben für sie ein Zielbild. -Der [Ansible-Deployment-Entwurf](GNAI-007-PLAN_Ansible-Deployment.md) persistiert den seit dem 19.09.2026 fortgeschriebenen Planungs- und Implementierungsstand. Er ist ausdrücklich keine verbindliche Spezifikation und kein Nachweis einer vollständig eingerichteten Deploymentkette. Die dort genannten Runner-, Freigabe- und Secret-Verfahren sind bis zu ihrer Prüfung und formalen Übernahme vorläufig. +Der [Ansible-Deployment-Entwurf](ai/GNAI-007-PLAN_Ansible-Deployment.md) persistiert den seit dem 19.09.2026 fortgeschriebenen Planungs- und Implementierungsstand. Er ist ausdrücklich keine verbindliche Spezifikation und kein Nachweis einer vollständig eingerichteten Deploymentkette. Die dort genannten Runner-, Freigabe- und Secret-Verfahren sind bis zu ihrer Prüfung und formalen Übernahme vorläufig. Das erste Ansible-Control-Projekt mit Testing-Inventar und lesendem Preflight wurde am 19.09.2026 angelegt. Der Live-Lauf über `ghost-agent` bestätigte Hostidentität, Debian 13.6, IPv4-Adresse, Python, CPU, RAM und zwei Navi-21-Geräte mit `changed=0`, `unreachable=0` und `failed=0`. Dieser Nachweis umfasst weder privilegierten Zugriff noch eine eingerichtete Production-/Testing-Laufzeitumgebung. Der separate Ed25519-Schlüssel mit Fingerprint `SHA256:IKEInbIo8VVI1USg5+a3ypGK8H8FweJiQu+v7KvhQDU` und das Bootstrap-Playbook wurden am 19.09.2026 verwendet, um `ansible-deploy` einzurichten. Der private Schlüssel liegt ausschließlich außerhalb des Repositories. Der Account besitzt ein gesperrtes Passwort, einen auf LAN- und WireGuard-Quellnetze beschränkten Public Key und eine mit `visudo` validierte Regel `NOPASSWD: ALL`. Ein getrenntes Prüfplaybook bestätigte anschließend Anmeldung, Modulausführung und effektive UID 0 über sudo mit `ok=5 changed=0 unreachable=0 failed=0`. -Die erste schreibende Rolle `host_baseline` wurde am 19.09.2026 nach erfolgreichem Syntax- und Check-Modus-Lauf angewendet. Sie prüft Hostname, Debian-Hauptversion, Standard-IPv4 sowie mindestens 100 GiB freien Speicher und verwaltet ausschließlich `/etc/ghostnet-ai/{production,testing}` und `/var/lib/ghostnet-ai/{production,testing,shared}` mit Root-Eigentum. Der erste reale Lauf meldete `ok=6 changed=1 unreachable=0 failed=0`; der unmittelbar folgende identische Lauf belegte Idempotenz mit `ok=6 changed=0 unreachable=0 failed=0`. Pakete, Dienste und Anwendungen wurden dabei nicht verändert. +Die erste schreibende Rolle `host_baseline` wurde am 19.09.2026 nach erfolgreichem Syntax- und Check-Modus-Lauf angewendet. Sie prüft Hostname, Debian-Hauptversion, Standard-IPv4 sowie mindestens 100 GiB freien Speicher. Die zunächst dort angelegten Umgebungsverzeichnisse werden seit Einführung der Servicekonten durch `workload_accounts` verwaltet; `host_baseline` besitzt nur noch die gemeinsamen Eltern und `/var/lib/ghostnet-ai/shared`. Der erste reale Lauf meldete `ok=6 changed=1 unreachable=0 failed=0`; der unmittelbar folgende identische Lauf belegte Idempotenz mit `ok=6 changed=0 unreachable=0 failed=0`. Die Rolle `podman_runtime` wurde am 20.09.2026 angewendet. Sie installierte Podman 5.4.2 aus Debian 13 einschließlich expliziter DNS- und rootless Netzwerkvoraussetzungen, prüfte systemd, cgroup v2, Overlay-Storage und den Quadlet-Generator und legte `/etc/containers/systemd` an. `podman.socket` und `podman-auto-update.timer` werden deaktiviert und gestoppt gehalten. Der abschließende Gesamtlauf meldete `ok=20 changed=0 unreachable=0 failed=0`; eine getrennte Prüfung bestätigte leere Image- und Containerlisten sowie beide Units als `disabled` und `inactive`. +Die Rolle `workload_accounts` richtete am 20.09.2026 die gesperrten Konten `gnai-production` (UID/GID 980), `gnai-testing` (981) und `gn-encoding` (982) mit festen, getrennten SubID-Bereichen ein. Konfiguration bleibt rootverwaltet; Daten, rootless Podman-Stores und persistente systemd-User-Manager sind pro Workload getrennt. Der korrigierte Gesamtlauf meldete `ok=36 changed=0 unreachable=0 failed=0` und bestätigte drei unterschiedliche rootless Graph Roots. Images und Container wurden nicht angelegt. + ## 3. Sicherheitsbefunde mit Priorität | Befund | dokumentiertes Verfahren / Status | | --- | --- | -| Keycloak-Administration öffentlich erreichbar, externe Anmeldung möglich | [GNAI-005](GNAI-005-SEC_Reverse-Proxy.md): Adminpfad-Schutz, interner Adminzugang, externe Sperrtests und Vaultwarden-OIDC-Prüfung; noch nicht angewendet | -| VPN-Zielrechte nur im lokalen Profil | [GNAI-006](GNAI-006-SEC_VPN.md) und [GUI-SOP](GNAI-006-SEC_VPN_Client-Management.md): Per-Client Firewall, Gateway-INPUT-Regeln und negative Tests; noch nicht angewendet | -| Fail2Ban-Image auf `1.1.0-r2-ls43` fest gepinnt; Container läuft wieder | [GNAI-005](GNAI-005-SEC_Reverse-Proxy.md): Pin umgesetzt; effektives Image, Jails, kontrollierte Bans sowie Dienst-/LXC-Wiederanlauf noch abzunehmen | -| HTTP-Client-User-Agents werden bewusst gebannt | [GNAI-005](GNAI-005-SEC_Reverse-Proxy.md): mögliche legitime Treffer und rollen-/routingabhängiger VPN-Ausweichweg dokumentiert | +| Keycloak-Administration öffentlich erreichbar, externe Anmeldung möglich | [GNAI-005](platform/GNAI-005-SEC_Reverse-Proxy.md): Adminpfad-Schutz, interner Adminzugang, externe Sperrtests und Vaultwarden-OIDC-Prüfung; noch nicht angewendet | +| VPN-Zielrechte nur im lokalen Profil | [GNAI-006](platform/GNAI-006-SEC_VPN.md) und [GUI-SOP](platform/GNAI-006-SEC_VPN_Client-Management.md): Per-Client Firewall, Gateway-INPUT-Regeln und negative Tests; noch nicht angewendet | +| Fail2Ban-Image auf `1.1.0-r2-ls43` fest gepinnt; Container läuft wieder | [GNAI-005](platform/GNAI-005-SEC_Reverse-Proxy.md): Pin umgesetzt; effektives Image, Jails, kontrollierte Bans sowie Dienst-/LXC-Wiederanlauf noch abzunehmen | +| HTTP-Client-User-Agents werden bewusst gebannt | [GNAI-005](platform/GNAI-005-SEC_Reverse-Proxy.md): mögliche legitime Treffer und rollen-/routingabhängiger VPN-Ausweichweg dokumentiert | ## 4. Noch erforderliche Angaben @@ -69,7 +86,7 @@ Es werden keine Werte für diese offenen Angaben angenommen. Die betreffenden Ve Bei der Dokumentationskonsolidierung wurden keine Serverkonfigurationen geändert, keine VPN-Praxisprüfungen nachgeholt und keine Keycloak-Sperre live bestätigt. Aussagen zum Ist-Stand beruhen auf den protokollierten Betreiberangaben und Tests; sie sind keine neue Live-Inventur. -Am 18.09.2026 wurden WireGuard-LXC und KI-Server ergänzend lesend per SSH geprüft. Der KI-Server-Teilabgleich bestätigt Hostname, Konten, Netzkonfiguration, Debian-Version, CPU, RAM, zwei Navi-21-GPUs und SSD-Partitionierung; BIOS, ROCm und die Anwendungsdienste wurden dabei nicht abgenommen. Details stehen in [GNAI-001](GNAI-001-INFRA_BaseSystem.md) und [GNAI-002](GNAI-002-INFRA_Users.md). +Am 18.09.2026 wurden WireGuard-LXC und KI-Server ergänzend lesend per SSH geprüft. Der KI-Server-Teilabgleich bestätigt Hostname, Konten, Netzkonfiguration, Debian-Version, CPU, RAM, zwei Navi-21-GPUs und SSD-Partitionierung; BIOS, ROCm und die Anwendungsdienste wurden dabei nicht abgenommen. Details stehen in [GNAI-001](platform/GNAI-001-INFRA_BaseSystem.md) und [GNAI-002](platform/GNAI-002-INFRA_Users.md). Am 19.09.2026 bestätigte ein erster reproduzierbarer Ansible-Preflight über das unprivilegierte Konto `ghost-agent` erneut `ghostnet-ki`, Debian 13.6, `10.0.0.90`, 14 vCPUs, 64.123 MiB RAM, Remote-Python 3.13.5 und zwei PCI-Geräte `1002:73bf`. Der Play Recap lautete `ok=5 changed=0 unreachable=0 failed=0`. Das Playbook verwendete kein `sudo` und nahm keine Serverkonfiguration vor. @@ -79,4 +96,13 @@ Für die gemeinsame Host-Baseline bestätigte ein vorgelagerter Check-Modus gena Am 20.09.2026 sagte der Podman-Check-Modus 38 neue Debian-Pakete und das Quadlet-Verzeichnis voraus. Der reale Lauf installierte diese Pakete; seine erste Nachprüfung bewertete den Debian-Symlink des Quadlet-Generators irrtümlich nicht als reguläre Datei. Nach Korrektur der Ansible-`stat`-Prüfung auf das aufgelöste Ziel bestanden Podman 5.4.2, cgroup v2, Overlay-Storage und der Generator-Dry-Run. Der finale Gesamtlauf war mit `changed=0` idempotent. Es wurden weder Images geladen noch Container oder Anwendungsdienste angelegt. +Der anschließende Workload-Account-Lauf richtete drei Konten, feste +SubUID-/SubGID-Bereiche, rootverwaltete Quadlet-Pfade und persistente +systemd-User-Manager ein. Zwei reine Abschlussprüfungen deckten zunächst den +nicht erlaubten direkten sudo-Benutzerwechsel sowie konkurrierende +Verzeichniseigentümer zwischen Rollen auf. Nach Umstellung der Prüfung auf +rootseitiges `runuser` und eindeutiger Eigentümerzuständigkeit endete der +Gesamtlauf mit `ok=36 changed=0 unreachable=0 failed=0`. Die drei rootless +Podman-Stores sind voneinander getrennt und noch leer. + Die WireGuard-Evidenz bleibt der Teiltest vom 12.09.2026: CachyOS-Laptop von WAN, Handshake, Ping auf Gateway/Proxmox und direkter IPv4-Internetpfad bestanden; KI-Server war ausgeschaltet. Neue Rollen, weitere Clientvarianten, Gateway-GUI-Zugriff, WAN-/IPv6-Isolation, Neustart und Restore sind noch nicht abgenommen. GNAI-006 ist erst nach Klärung der offenen Werte, Umsetzung und dokumentierter Abnahme technisch abgeschlossen. diff --git a/docs/GNAI-003-INFRA_Architecture.md b/docs/ai/GNAI-003-INFRA_Architecture.md similarity index 96% rename from docs/GNAI-003-INFRA_Architecture.md rename to docs/ai/GNAI-003-INFRA_Architecture.md index a55ba97..ea0d717 100644 --- a/docs/GNAI-003-INFRA_Architecture.md +++ b/docs/ai/GNAI-003-INFRA_Architecture.md @@ -3,7 +3,7 @@ **Dokumenten-ID:** GNAI-003-INFRA **Titel:** Homelab / LLM-Infrastruktur – Zielarchitektur **Status:** Zielarchitektur; bestätigter Teilstand und offene Umsetzung separat ausgewiesen -**Vorgänger / Referenzen:** [Basisinstallation](GNAI-001-INFRA_BaseSystem.md), [Benutzer/SSH](GNAI-002-INFRA_Users.md), [Zugriffskonzept](GNAI-004-SEC_Access-Control.md), [Reverse Proxy](GNAI-005-SEC_Reverse-Proxy.md), [WireGuard](GNAI-006-SEC_VPN.md) +**Vorgänger / Referenzen:** [Basisinstallation](../platform/GNAI-001-INFRA_BaseSystem.md), [Benutzer/SSH](../platform/GNAI-002-INFRA_Users.md), [Zugriffskonzept](../platform/GNAI-004-SEC_Access-Control.md), [Reverse Proxy](../platform/GNAI-005-SEC_Reverse-Proxy.md), [WireGuard](../platform/GNAI-006-SEC_VPN.md) --- @@ -338,4 +338,4 @@ Die Architektur gilt als korrekt umgesetzt, wenn mindestens folgende Eigenschaft ## 14. Umsetzungsstand -Die Architektur-Abnahmekriterien in Abschnitt 13 sind Sollkriterien und keine bestätigte Abnahme. Der aktuelle Projektstand und die priorisierten Befunde stehen im [Dokumentationsindex](GNAI-000-DOC_Index.md); technische Nachweise und offene Prüfungen werden in GNAI-005 und GNAI-006 geführt. Backendisolation und lokale Netzwerksegmentierung sind ebenfalls noch nachzuweisen. +Die Architektur-Abnahmekriterien in Abschnitt 13 sind Sollkriterien und keine bestätigte Abnahme. Der aktuelle Projektstand und die priorisierten Befunde stehen im [Dokumentationsindex](../GNAI-000-DOC_Index.md); technische Nachweise und offene Prüfungen werden in GNAI-005 und GNAI-006 geführt. Backendisolation und lokale Netzwerksegmentierung sind ebenfalls noch nachzuweisen. diff --git a/docs/GNAI-007-PLAN_Ansible-Deployment.md b/docs/ai/GNAI-007-PLAN_Ansible-Deployment.md similarity index 86% rename from docs/GNAI-007-PLAN_Ansible-Deployment.md rename to docs/ai/GNAI-007-PLAN_Ansible-Deployment.md index 0e264e1..d0fcb34 100644 --- a/docs/GNAI-007-PLAN_Ansible-Deployment.md +++ b/docs/ai/GNAI-007-PLAN_Ansible-Deployment.md @@ -4,7 +4,7 @@ **Titel:** Ansible-Deployment für den KI-Server – Planungsstand **Stand:** 2026-09-20 **Status:** Nicht finaler Arbeitsentwurf zur Persistierung des aktuellen Planungsstands -**Referenzen:** [Projektstand](GNAI-000-DOC_Index.md), [Zielarchitektur](GNAI-003-INFRA_Architecture.md), [Zugriffskonzept](GNAI-004-SEC_Access-Control.md) +**Referenzen:** [Projektstand](../GNAI-000-DOC_Index.md), [Zielarchitektur](GNAI-003-INFRA_Architecture.md), [Zugriffskonzept](../platform/GNAI-004-SEC_Access-Control.md) > **Abgrenzung:** Dieses Dokument hält die Ergebnisse der am 19.09.2026 > begonnenen und am 20.09.2026 fortgeschriebenen Planungssession fest. Es ist @@ -27,6 +27,7 @@ | 2026-09-19 | 0.6 | spektr / Codex | `ansible-deploy` auf dem KI-Server eingerichtet und SSH-/sudo-Zugriff unabhängig erfolgreich verifiziert | | 2026-09-19 | 0.7 | spektr / Codex | minimale gemeinsame Host-Baseline mit Schutzprüfungen und getrennten Verzeichniswurzeln implementiert, angewendet und auf Idempotenz geprüft | | 2026-09-20 | 0.8 | spektr / Codex | gemeinsame Podman-/Quadlet-Runtime implementiert, installiert und ohne Images oder Container verifiziert | +| 2026-09-20 | 0.9 | spektr / Codex | getrennte rootless Konten für AI Production, AI Testing und CPU-Encoding eingerichtet; Parallelitäts- und Repository-Grenzen ergänzt | --- @@ -73,6 +74,11 @@ ein Production-Inventar existieren noch nicht. Der Preflight selbst nahm keine Änderungen vor; die bislang angewendeten Rollen bleiben auf die nachfolgend dokumentierten Zugangs-, Verzeichnis- und Runtime-Zustände begrenzt. +Ein fachlich vom LLM-Stack getrennter CPU-Encoding-Workload wird auf demselben +Host vorgesehen. Sein Ansible-Sollzustand bleibt wegen des gemeinsamen Hosts im +Infrastructure-Repository; Architektur und spätere Nutzer-SOPs werden unter +[`docs/video-encoding/`](../video-encoding/GNENC-000-DOC_Index.md) getrennt geführt. + --- ## 3. Vorläufiges Zielbild @@ -114,7 +120,7 @@ Planungsrichtung und noch nicht bestätigten Implementierungsdetails. | Thema | Aktueller Planungsstand | Verbindlichkeit / Nachweis | | --- | --- | --- | -| Konfigurationsmanagement | Ansible beschreibt Basissystem, Laufzeitumgebung, Dienste und Konfiguration | Control-Projekt, Preflight, `host_baseline` und `podman_runtime` vorhanden und angewendet; GPU- und Anwendungsrollen fehlen noch | +| Konfigurationsmanagement | Ansible beschreibt Basissystem, Laufzeitumgebung, Dienste und Konfiguration | Control-Projekt, Preflight, `host_baseline`, `podman_runtime` und `workload_accounts` vorhanden und angewendet; GPU- und Anwendungsrollen fehlen noch | | Quell- und Freigabeverfahren | Änderungen über Gitea-Branches und Pull Requests; kein regulärer Direkt-Push auf `main` | vorgesehen; Branch-Schutz noch nicht geprüft oder eingerichtet | | CI | Pull Requests erhalten Lint-, YAML- und Ansible-Syntaxprüfungen ohne produktive Zugangsdaten | vorgesehen; Workflow noch nicht vorhanden | | Produktionsausführung | Gitea Actions startet Ansible über eine dedizierte Production-Berechtigungsstufe | bevorzugte Lösung; Gitea-Version, Runner-Trennung und Actions-Funktion noch zu prüfen | @@ -125,13 +131,14 @@ Planungsrichtung und noch nicht bestätigten Implementierungsdetails. | Zielzugang | eigener technischer SSH-Account `ansible-deploy` mit separatem, quellnetzbeschränktem Schlüssel und validiertem `NOPASSWD: ALL` | auf dem KI-Server eingerichtet und unabhängig verifiziert; spätere Runner-Übernahme und Rotation offen | | Geheimnisse | verschlüsselte Ansible-Variablen plus getrennt gespeichertes Vault-Passwort und Runner-Zugangsdaten | Richtung angenommen; konkreter Secret-Workflow noch offen | | Artefaktversionen | Pakete, Collections und Container-Images werden auf konkrete Versionen, nach Möglichkeit Digests, festgelegt | vorgesehen | -| Repository-Zuschnitt | vorerst ausschließlich das bestehende Repository `infrastructure`; weitere Quellcode-Repositories erst bei tatsächlich eigenständig entwickelten Anwendungen oder MCP-Komponenten | als aktuelle Planungsentscheidung angenommen | +| Repository-Zuschnitt | `infrastructure` bleibt alleinige Quelle für alle Ansible-Zustände des Hosts; KI- und Encoding-Dokumentation werden getrennt geführt; weitere Quellcode-Repositories erst bei eigenständig entwickelten Anwendungen | als aktuelle Planungsentscheidung angenommen | | Anwendungsupdates | Upstream-Artefakte werden im Infrastruktur-Repository auf eine geprüfte Version festgelegt; eigene Build-Repositories sind vorerst nicht Bestandteil des Projekts | vorgesehen; Registry- und Buildverfahren bei späterem Eigenentwicklungsbedarf neu bewerten | | Automatische Updates | kein unbeaufsichtigtes Produktionsupdate über `latest` oder Watchtower | vorläufige Sicherheitsentscheidung | | Laufzeitumgebungen | Production und Testing werden als getrennte Stacks auf demselben physischen KI-Server betrieben | als aktuelle Planungsentscheidung angenommen | | Production | stabiler, extern nutzbarer Stand mit bewusst freigegebenen Versionen | Zielbild; Dienste noch nicht eingerichtet | -| Testing | interner, kurzlebiger oder zurücksetzbarer Stand für kontrollierte Tests vor dem Merge nach `main`; nutzt während eines Wartungsfensters die vollständige Hardware exklusiv | Zielbild; Dienste, Zugriffsweg und Umschaltverfahren noch nicht eingerichtet | -| Ressourcenbetrieb | keine feste physische Aufteilung; Production und Testing laufen nicht gleichzeitig und werden über einen exklusiven Umschaltvorgang zeitlich gestaffelt | als aktuelle Planungsentscheidung angenommen | +| Testing | interner, kurzlebiger oder zurücksetzbarer Stand für kontrollierte Tests vor dem Merge nach `main`; nutzt während eines Wartungsfensters beide GPUs exklusiv gegenüber Production | Zielbild; Dienste, Zugriffsweg und Umschaltverfahren noch nicht eingerichtet | +| Ressourcenbetrieb | AI Production und AI Testing laufen nicht gleichzeitig; der getrennte CPU-Encoding-Workload darf nachts parallel zur jeweils aktiven KI-Umgebung laufen | als aktuelle Planungsentscheidung angenommen; Grenzwerte und Lasttest offen | +| Workload-Konten | `gnai-production`, `gnai-testing` und `gn-encoding` verwenden getrennte rootless Podman-Stores und systemd-User-Manager | eingerichtet und mit `changed=0` verifiziert | | Betriebsnachweis | erfolgreicher Lauf dokumentiert Git-Commit, Zielhost, Zeitpunkt, Prüfungen und Ergebnis | vorgesehen | | Rollback | Konfigurationsänderung über Revert und erneute Freigabe; persistente Daten zusätzlich über getestete Backups wiederherstellen | Grundprinzip; konkrete SOP noch zu erstellen | @@ -223,9 +230,11 @@ Umgebung läuft. Production und Testing laufen zunächst auf demselben physischen KI-Server. Damit können Anwendungscontainer, Konfigurationen und Daten sauber logisch getrennt werden. Die Hardware kann wegen des hohen Leistungsbedarfs nicht -dauerhaft zwischen beiden Umgebungen aufgeteilt werden. Stattdessen erhält die -jeweils aktive Umgebung CPU, RAM und beide GPUs exklusiv. Es entstehen dennoch -keine getrennten Fehlerdomänen: +dauerhaft zwischen beiden KI-Umgebungen aufgeteilt werden. Stattdessen erhält +die jeweils aktive KI-Umgebung beide GPUs exklusiv gegenüber der anderen +KI-Umgebung. Ein fachlich getrennter CPU-Encoding-Workload darf außerhalb der +Stoßzeiten parallel laufen. Es entstehen dennoch keine getrennten +Fehlerdomänen: - Kernel, AMD-Treiber beziehungsweise ROCm, Podman und systemd werden gemeinsam verwendet; @@ -256,19 +265,27 @@ geplante Unterbrechung von Production. | Persistente Daten | eigene Volumes mit Backup- und Restore-Verfahren | eigene zurücksetzbare Volumes; keine Production-Datenkopie ohne Bereinigung und Freigabe | | Secrets und API-Keys | ausschließlich Production-Credentials | eigene eingeschränkte Test-Credentials | | Ports und Hostnamen | stabile freigegebene Endpunkte | getrennte interne Ports und noch festzulegende Testhostnamen | -| Ressourcen | erhält im Normalbetrieb die vollständige Hardware exklusiv | erhält ausschließlich im freigegebenen Testfenster die vollständige Hardware exklusiv | +| Ressourcen | erhält im Normalbetrieb beide GPUs und das festzulegende KI-Ressourcenbudget | erhält im Testfenster beide GPUs und das festzulegende KI-Ressourcenbudget | | GPU | beide GPUs, solange Production aktiv ist | beide GPUs, nachdem Production vollständig beendet wurde | | Ausführungszustand | standardmäßig aktiv | standardmäßig gestoppt | | Verfügbarkeit | stabiler Betriebsstand außerhalb angekündigter Wartungsfenster | best effort; wird nach jedem Testlauf wieder vollständig gestoppt | -Eine gleichzeitige Inferenzlast beider Umgebungen ist nicht vorgesehen. Vor dem +Eine gleichzeitige Inferenzlast beider KI-Umgebungen ist nicht vorgesehen. Vor dem Start von Testing muss nachweisbar sein, dass Production beendet wurde und keine zugehörigen Prozesse mehr CPU-, RAM- oder GPU-Ressourcen halten. Vor dem Wiederanlauf von Production muss Testing entsprechend vollständig beendet und sein temporärer Zustand bereinigt sein. Speicherplatz- und Prozessgrenzen -bleiben auch bei exklusiver Rechenleistung sinnvoll, damit ein Test nicht den +bleiben auch bei exklusiver GPU-Nutzung sinnvoll, damit ein Test nicht den gemeinsamen Datenträger füllt oder den Host dauerhaft beeinträchtigt. +Der CPU-basierte Encoding-Workload ist nicht Teil dieser KI-Umschaltung. Er darf +nachts parallel zu Production oder Testing laufen, erhält keinen GPU-Zugriff +und muss über CPU-, Speicher- und I/O-Grenzen die Reaktionsfähigkeit der +KI-Dienste schützen. Die Betreiberfestlegung „CPU-basiert“ und die geplante +Parallelität sind noch durch einen repräsentativen gemeinsamen Lasttest zu +bestätigen. Zeitfenster und Grenzwerte werden ausschließlich im getrennten +[Encoding-Plan](../video-encoding/GNENC-001-PLAN_Architecture.md) spezifiziert. + Große schreibgeschützte Modelldateien können später aus einem gemeinsamen, nur lesbar eingebundenen Modellspeicher bereitgestellt werden. Datenbanken, Uploads, Caches mit Schreibzugriff, Benutzerkonten und Anwendungsschlüssel @@ -290,7 +307,7 @@ Production. Der vorläufige Ablauf lautet: geordnet beenden; 5. prüfen, dass alle Production-Units, -Container und GPU-Prozesse beendet sind; -6. einen hostweiten exklusiven Deployment-Lock setzen; +6. einen exklusiven Lock für den KI-Umgebungswechsel setzen; 7. Testing für den festgelegten Commit ausrollen und mit der vollständigen Hardware starten; 8. Health Checks und fachliche Tests ausführen und Ergebnisse dem Pull Request @@ -318,7 +335,8 @@ Verfügung. Production und Testing dürfen niemals gleichzeitig als aktiv markiert sein. Jeder Start prüft den Gegenstatus und bricht bei einem widersprüchlichen oder -unklaren Zustand ab. +unklaren Zustand ab. Der Encoding-Workload bleibt von diesem KI-Lock unabhängig; +ein allgemeines Hostwartungsfenster kann weiterhin alle Workloads stoppen. ### 7.4 Vorgesehene Ansible-Abbildung @@ -327,37 +345,38 @@ getrennt: ```text ansible/ -├── inventories/ -│ ├── production/ -│ │ ├── hosts.yml -│ │ ├── group_vars/ -│ │ └── host_vars/ -│ └── testing/ -│ ├── hosts.yml -│ ├── group_vars/ -│ └── host_vars/ ├── playbooks/ │ ├── host.yml -│ ├── environment.yml -│ └── verify.yml +│ ├── ai.yml +│ ├── video-encoding.yml +│ └── site.yml └── roles/ ├── host_baseline/ - └── environment_stack/ + ├── podman_runtime/ + ├── workload_accounts/ + ├── ai_stack/ + ├── ai_environment_switch/ + └── video_encoding/ ``` -Beide Inventare zeigen auf `ghostnet-ki`, enthalten aber ausschließlich die -für ihre Umgebung zulässigen Variablen und Secrets. `host.yml` verwaltet -gemeinsame Pakete, Treiber, Benutzer und Runtime-Komponenten einmalig. -`environment.yml` muss genau eine explizit gewählte Umgebung verwalten und vor -Änderungen prüfen, ob Namen, Pfade, Ports und Zielvariablen zur Auswahl passen. -Hoständerungen dürfen nicht beiläufig durch einen Testing-Lauf erfolgen. +`host.yml` verwaltet gemeinsame Pakete, Treiber, Benutzer und +Runtime-Komponenten einmalig. `ai.yml` verwaltet ausschließlich den LLM-Stack +und genau eine explizit gewählte KI-Umgebung. `video-encoding.yml` verwaltet nur +Encoding-Pfade, Images, Quadlets, Timer und Prüfungen. `site.yml` kann den +unterstützten Gesamtzustand in definierter Reihenfolge herstellen. Hoständerungen +dürfen nicht beiläufig durch einen Workload-Lauf erfolgen. Für den ersten umgesetzten Baseline-Stand gelten getrennte Konfigurationsbäume unter `/etc/ghostnet-ai/{production,testing}` und getrennte persistente Datenbäume unter `/var/lib/ghostnet-ai/{production,testing}`. Der Pfad `/var/lib/ghostnet-ai/shared` ist für später ausdrücklich freigegebene gemeinsame Daten reserviert. Konkrete Unterverzeichnisse, Eigentümer sowie -Unit- und Container-Namen werden mit den jeweiligen Fachrollen festgelegt. +Unit- und Container-Namen werden mit den jeweiligen Fachrollen festgelegt. Die +Konten `gnai-production` und `gnai-testing` besitzen inzwischen ihre +Datenwurzeln und getrennten rootless Podman-Stores; Konfiguration bleibt +rootverwaltet und für die jeweilige Gruppe lesbar. Der Encoding-Workload +verwendet entsprechend `/etc/ghostnet-encoding` und +`/var/lib/ghostnet-encoding`. --- @@ -534,8 +553,8 @@ nicht als Nachweis einer erfolgreichen Einrichtung. ### Phase D – KI-Laufzeit -- Podman- und Quadlet-Grundruntime eingerichtet und geprüft; konkretes - rootful/rootless Ausführungsmodell noch festzulegen; +- Podman- und Quadlet-Grundruntime sowie getrennte rootless Workload-Konten + eingerichtet und geprüft; - AMD-GPU-/ROCm- oder alternative Laufzeit nach technischer Prüfung; - `llama.cpp` und lokaler Funktionstest; - Wiederanlauf nach geplantem Reboot prüfen. @@ -585,9 +604,9 @@ Vor der technischen Spezifikation sind mindestens folgende Punkte zu klären: Wiederanlauf von Production sicher, wenn Runner oder Testing ausfallen? 15. Welche internen Hostnamen und Ports erhält Testing, und welche LAN-/VPN- Rollen dürfen darauf zugreifen? -16. Können Production und Testing mit getrennten unprivilegierten Accounts - verwaltet werden, oder erfordert die gewählte Podman-/systemd-Architektur - einen stärker privilegierten gemeinsamen Deploymentpfad? +16. Funktioniert der benötigte AMD-GPU-Zugriff zuverlässig aus den getrennten + rootless Konten, und welche Gruppen-, Device- und Containeroptionen sind + dafür minimal erforderlich? 17. Wie wird ein Testing-Deployment eines Pull-Request-Commits in der eingesetzten Gitea-Version autorisiert, ohne Production-Secrets oder Production-Runner für veränderbaren PR-Code verfügbar zu machen? @@ -715,13 +734,33 @@ Eine getrennte Prüfung bestätigte außerdem leere Image- und Containerlisten sowie `podman.socket` und `podman-auto-update.timer` jeweils als `disabled` und `inactive`. +Die Rolle `workload_accounts` wurde anschließend in `host.yml` aufgenommen. Sie +verwaltet feste IDs, gesperrte Passwörter, `/usr/sbin/nologin`, getrennte +SubUID-/SubGID-Bereiche, Daten- und Konfigurationsrechte, rootverwaltete +Quadlet-Verzeichnisse und Linger für `gnai-production` (UID/GID 980), +`gnai-testing` (981) und `gn-encoding` (982). Die Datenwurzeln gehören dem +jeweiligen Konto; Konfiguration gehört root und ist für die jeweilige Gruppe +lesbar. + +Beim ersten realen Lauf scheiterte nur die Abschlussprüfung, weil die +sudoers-Regel von `ansible-deploy` den direkten Ansible-Wechsel zu den +Servicekonten nicht erlaubt. Die Prüfung wurde auf einen rootseitigen Aufruf +über `runuser` umgestellt, ohne die sudoers-Regel zu erweitern. Ein weiterer +Lauf deckte eine konkurrierende Eigentümerverwaltung durch `host_baseline` auf; +die umgebungsspezifischen Pfade wurden daraufhin vollständig an +`workload_accounts` übertragen. Der korrigierte Gesamtlauf endete mit +`ok=36 changed=0 unreachable=0 failed=0` und bestätigte drei aktive +systemd-User-Manager sowie drei getrennte rootless Podman Graph Roots. Es +wurden keine Images oder Workload-Container angelegt. + --- ## 15. Abschlussgrenze dieses Entwurfs -Mit Version 0.8 sind der aktuelle Planungsstand, der ausdrücklich begrenzte +Mit Version 0.9 sind der aktuelle Planungsstand, der ausdrücklich begrenzte Ansible-Preflight, der verifizierte Deployment-Account und die angewendete -minimale Host-Baseline sowie die Podman-/Quadlet-Grundruntime persistiert. +minimale Host-Baseline, die Podman-/Quadlet-Grundruntime und die getrennten +Workload-Konten persistiert. Insbesondere wurden noch nicht: - ein Gitea Runner angelegt, @@ -733,5 +772,6 @@ Insbesondere wurden noch nicht: - Backups oder Rollbacks praktisch getestet oder - die geplante Deploymentkette technisch abgenommen. -Der nächste Dokumentationsschritt ist die Ist-Aufnahme aus Phase A. Auf deren -Ergebnissen baut die verbindliche technische Spezifikation auf. +Die nächsten technischen Schritte sind der begrenzte GPU-Zugriffstest für die +beiden KI-Konten und die Festlegung von Zeitfenster sowie Ressourcenlimits im +getrennten Encoding-Plan. Darauf bauen die jeweiligen Fachrollen und SOPs auf. diff --git a/docs/ai/README.md b/docs/ai/README.md new file mode 100644 index 0000000..aa6f1b3 --- /dev/null +++ b/docs/ai/README.md @@ -0,0 +1,15 @@ +# KI-Dokumentation + +Dieser Bereich enthält die fachliche Zielarchitektur und Deployment-Planung für +den KI-Stack. Gemeinsame Host-, Netzwerk- und Sicherheitsgrundlagen liegen in +der [Plattformdokumentation](../platform/README.md). Der CPU-basierte +Video-Encoding-Workload besitzt einen +[eigenen Dokumentationsbereich](../video-encoding/GNENC-000-DOC_Index.md). + +| Dokument | Inhalt | +| --- | --- | +| [GNAI-003](GNAI-003-INFRA_Architecture.md) | Zielarchitektur, Komponenten und Datenwege des KI-Stacks | +| [GNAI-007](GNAI-007-PLAN_Ansible-Deployment.md) | nicht finaler Arbeitsentwurf für Ansible- und Gitea-Deployment | + +Der übergreifende Projektstand steht im +[zentralen Dokumentationsindex](../GNAI-000-DOC_Index.md). diff --git a/docs/GNAI-001-INFRA_BaseSystem.md b/docs/platform/GNAI-001-INFRA_BaseSystem.md similarity index 100% rename from docs/GNAI-001-INFRA_BaseSystem.md rename to docs/platform/GNAI-001-INFRA_BaseSystem.md diff --git a/docs/GNAI-002-INFRA_Users.md b/docs/platform/GNAI-002-INFRA_Users.md similarity index 84% rename from docs/GNAI-002-INFRA_Users.md rename to docs/platform/GNAI-002-INFRA_Users.md index a313382..f03424c 100644 --- a/docs/GNAI-002-INFRA_Users.md +++ b/docs/platform/GNAI-002-INFRA_Users.md @@ -15,12 +15,13 @@ | 2026-09-16 | 1.2 | spektr | Tatsächliche Konten, Gruppen, Netz- und SSH-Konfiguration sowie Grenzen des Login-Tests dokumentiert. | | 2026-09-18 | 1.3 | spektr | Workstation-spezifische Schlüsselpfade und Host-Fingerprint in den lokalen Arbeitskontext verlagert. | | 2026-09-19 | 1.4 | spektr | Persönlichen Login `ghost` und Agentenlogin `ghost-agent` als erfolgreich getestet dokumentiert. | +| 2026-09-20 | 1.5 | spektr / Codex | Deployment- und getrennte rootless Workload-Konten mit festen IDs und Zuständigkeiten ergänzt. | --- ## 1. Zweck und Geltungsbereich -Dieses Dokument beschreibt Benutzer, statische IP und SSH des KI-Servers. Der lesend geprüfte Kontenbestand am 18.09.2026 lautet `ghost`, `debugofen`, `ghost-agent`; `admin` existiert auf diesem Host nicht. Die folgenden Befehle sind für Neuinstallation oder gezielte Änderung gedacht und ersetzen keine Prüfung des vorhandenen Systems. Der WireGuard-LXC besitzt ein separates Konto `admin`. +Dieses Dokument beschreibt Benutzer, statische IP und SSH des KI-Servers. Der ursprüngliche lesende Kontenabgleich vom 18.09.2026 umfasste `ghost`, `debugofen` und `ghost-agent`; `admin` existiert auf diesem Host nicht. Am 19. und 20.09.2026 kamen der Ansible-Deployment-Account sowie drei gesperrte Workload-Konten hinzu. Die folgenden Befehle sind für Neuinstallation oder gezielte Änderung gedacht und ersetzen keine Prüfung des vorhandenen Systems. Der WireGuard-LXC besitzt ein separates Konto `admin`. --- @@ -59,6 +60,22 @@ sudo usermod -aG sudo ghost Weitere Konten nur nach Rollenentscheidung anlegen. Die sudo-Gruppenmitgliedschaft ist pro Konto ausdrücklich festzulegen; auf dem bestehenden Host keine bereits vorhandenen Konten erneut anlegen. +Die folgenden technischen Konten werden ausschließlich durch das +Ansible-Projekt verwaltet: + +| Konto | UID/GID | Zweck | interaktiver Login / sudo | +| --- | --- | --- | --- | +| `ansible-deploy` | 1003 | privilegierte Ansible-Ausführung | SSH-Key, gesperrtes Passwort, `NOPASSWD` gemäß eigener sudoers-Regel | +| `gnai-production` | 980 | rootless AI Production | gesperrtes Passwort, `/usr/sbin/nologin`, kein sudo | +| `gnai-testing` | 981 | rootless AI Testing | gesperrtes Passwort, `/usr/sbin/nologin`, kein sudo | +| `gn-encoding` | 982 | rootless CPU-Encoding | gesperrtes Passwort, `/usr/sbin/nologin`, kein sudo | + +Die drei Workload-Konten besitzen nicht überlappende SubUID-/SubGID-Bereiche, +eigene Daten- und Podman-Stores sowie persistente systemd-User-Manager. Ihre +rootverwalteten Quadlet-Verzeichnisse liegen unter +`/etc/containers/systemd/users/`. Direkte SSH-Schlüssel werden dort nicht +hinterlegt. + --- ### 3.2 Statische Netzwerkkonfiguration (Server-Seite) @@ -170,6 +187,7 @@ sudo systemctl restart ssh ## 4. Abnahmekriterien (Checkliste für den Junior-Admin) * [ ] **Benutzerprüfung:** `ghost` und `debugofen` können ihre vorgesehenen sudo-Rechte nutzen; `ghost-agent` bleibt ohne sudo-Zuweisung. +* [x] **Workload-Konten:** `gnai-production`, `gnai-testing` und `gn-encoding` besitzen getrennte rootless Podman-Stores und aktive persistente User-Manager; geprüft am 20.09.2026. * [ ] **Netzwerk:** Server ist unter der statischen IP `10.0.0.90` erreichbar. Ausgehender Datenverkehr funktioniert (`ping google.com`). * [x] **SSH-Aliase:** `ghostnet-ai-ghost` meldet `ghost`, `ghostnet-ai-agent` meldet `ghost-agent`. Beide Zugänge wurden am 19.09.2026 bestätigt. Die Passphrase des persönlichen Schlüssels ist keine Server-Passwortanmeldung. * [ ] **Root-Sperre:** Direct-Login als Root per SSH wird abgelehnt (`ssh root@10.0.0.90` -> *Permission denied*). diff --git a/docs/GNAI-004-SEC_Access-Control.md b/docs/platform/GNAI-004-SEC_Access-Control.md similarity index 95% rename from docs/GNAI-004-SEC_Access-Control.md rename to docs/platform/GNAI-004-SEC_Access-Control.md index 827d44a..d34014c 100644 --- a/docs/GNAI-004-SEC_Access-Control.md +++ b/docs/platform/GNAI-004-SEC_Access-Control.md @@ -3,7 +3,7 @@ **Dokumenten-ID:** GNAI-004-SEC **Titel:** Homelab / LLM-Infrastruktur – Sicherheits-, Authentifizierungs- und Zugriffskonzept **Status:** Verbindliches Zielmodell; technische Durchsetzung teilweise offen -**Vorgänger / Referenzen:** [Architektur](GNAI-003-INFRA_Architecture.md), [Gateway](GNAI-006-SEC_VPN.md), [GUI-SOP](GNAI-006-SEC_VPN_Client-Management.md), [Reverse Proxy](GNAI-005-SEC_Reverse-Proxy.md) +**Vorgänger / Referenzen:** [Architektur](../ai/GNAI-003-INFRA_Architecture.md), [Gateway](GNAI-006-SEC_VPN.md), [GUI-SOP](GNAI-006-SEC_VPN_Client-Management.md), [Reverse Proxy](GNAI-005-SEC_Reverse-Proxy.md) --- @@ -297,4 +297,4 @@ Das Sicherheitsmodell basiert damit nicht auf einem einzelnen „Security-Progra ## 11. Umsetzungsnachweise -Dieses Dokument definiert die Zugriffsziele. Der [Dokumentationsindex](GNAI-000-DOC_Index.md) bündelt den aktuellen Projektstand. Konkrete Gateway-Regeln und Rollentests stehen in [GNAI-006](GNAI-006-SEC_VPN.md), der Keycloak-Adminpfad und Fail2Ban in [GNAI-005](GNAI-005-SEC_Reverse-Proxy.md). Offene Zielrechte gelten bis zu ihrer technischen Abnahme nicht als durchgesetzt. +Dieses Dokument definiert die Zugriffsziele. Der [Dokumentationsindex](../GNAI-000-DOC_Index.md) bündelt den aktuellen Projektstand. Konkrete Gateway-Regeln und Rollentests stehen in [GNAI-006](GNAI-006-SEC_VPN.md), der Keycloak-Adminpfad und Fail2Ban in [GNAI-005](GNAI-005-SEC_Reverse-Proxy.md). Offene Zielrechte gelten bis zu ihrer technischen Abnahme nicht als durchgesetzt. diff --git a/docs/GNAI-005-SEC_Reverse-Proxy.md b/docs/platform/GNAI-005-SEC_Reverse-Proxy.md similarity index 99% rename from docs/GNAI-005-SEC_Reverse-Proxy.md rename to docs/platform/GNAI-005-SEC_Reverse-Proxy.md index 75066c4..5d4fd30 100644 --- a/docs/GNAI-005-SEC_Reverse-Proxy.md +++ b/docs/platform/GNAI-005-SEC_Reverse-Proxy.md @@ -3,7 +3,7 @@ **Dokumenten-ID:** GNAI-005-SEC **Titel:** Reverse Proxy und Fail2Ban – Konfiguration, Betrieb und Sicherheitsabnahme **Status:** NPM aktiv; Fail2Ban laut Betreiber fest gepinnt und wieder in Betrieb; Keycloak-Admin-Exposure bestätigt, Korrektur offen -**Vorgänger / Referenzen:** [Architektur](GNAI-003-INFRA_Architecture.md), [Zugriffskonzept](GNAI-004-SEC_Access-Control.md), [WireGuard](GNAI-006-SEC_VPN.md) +**Vorgänger / Referenzen:** [Architektur](../ai/GNAI-003-INFRA_Architecture.md), [Zugriffskonzept](GNAI-004-SEC_Access-Control.md), [WireGuard](GNAI-006-SEC_VPN.md) --- diff --git a/docs/GNAI-006-SEC_VPN.md b/docs/platform/GNAI-006-SEC_VPN.md similarity index 98% rename from docs/GNAI-006-SEC_VPN.md rename to docs/platform/GNAI-006-SEC_VPN.md index 48f0616..cad5aa1 100644 --- a/docs/GNAI-006-SEC_VPN.md +++ b/docs/platform/GNAI-006-SEC_VPN.md @@ -2,7 +2,7 @@ **Dokumenten-ID:** GNAI-006-SEC **Status:** Dokumentation bereinigt; technische Umsetzung und Abnahme teilweise offen -**Referenzen:** [Zielarchitektur](GNAI-003-INFRA_Architecture.md), [Zugriffskonzept](GNAI-004-SEC_Access-Control.md), [GUI-SOP](GNAI-006-SEC_VPN_Client-Management.md), [Implementation-Log](GNAI-006-SEC_VPN_Implementation-Log.md) +**Referenzen:** [Zielarchitektur](../ai/GNAI-003-INFRA_Architecture.md), [Zugriffskonzept](GNAI-004-SEC_Access-Control.md), [GUI-SOP](GNAI-006-SEC_VPN_Client-Management.md), [Implementation-Log](GNAI-006-SEC_VPN_Implementation-Log.md) ## Änderungsverlauf diff --git a/docs/GNAI-006-SEC_VPN_Client-Management.md b/docs/platform/GNAI-006-SEC_VPN_Client-Management.md similarity index 100% rename from docs/GNAI-006-SEC_VPN_Client-Management.md rename to docs/platform/GNAI-006-SEC_VPN_Client-Management.md diff --git a/docs/GNAI-006-SEC_VPN_Clients_Linux.md b/docs/platform/GNAI-006-SEC_VPN_Clients_Linux.md similarity index 100% rename from docs/GNAI-006-SEC_VPN_Clients_Linux.md rename to docs/platform/GNAI-006-SEC_VPN_Clients_Linux.md diff --git a/docs/GNAI-006-SEC_VPN_Clients_Windows11.md b/docs/platform/GNAI-006-SEC_VPN_Clients_Windows11.md similarity index 100% rename from docs/GNAI-006-SEC_VPN_Clients_Windows11.md rename to docs/platform/GNAI-006-SEC_VPN_Clients_Windows11.md diff --git a/docs/GNAI-006-SEC_VPN_Implementation-Log.md b/docs/platform/GNAI-006-SEC_VPN_Implementation-Log.md similarity index 97% rename from docs/GNAI-006-SEC_VPN_Implementation-Log.md rename to docs/platform/GNAI-006-SEC_VPN_Implementation-Log.md index 03efc6c..a4e3d1b 100644 --- a/docs/GNAI-006-SEC_VPN_Implementation-Log.md +++ b/docs/platform/GNAI-006-SEC_VPN_Implementation-Log.md @@ -50,7 +50,7 @@ Der aktuelle Quadlet-Pin `15.4.0` ist durch den Betreiber bestätigt. v15 verwen | Laptop-FullAdmin | `10.8.0.2` | 1 | neue Rolle Familie oder Server Admin noch ausdrücklich zuordnen | | Android-LLMAdmin | `10.8.0.3` | 2 | Altbestand; Android außerhalb des neuen SOP-Umfangs, keine Praxisabnahme | -Profile wurden laut Log manuell aus der SQLite-Datenbank generiert und gegen die Serverkonfiguration geprüft. Das ist ein historischer Vorgang, kein künftiges SOP-Verfahren. Neue Profile werden über die GUI exportiert. Der Pfad `docs/GNAI-006-profiles/` wurde als gitignored genannt; Profile bleiben unabhängig von Git-Ausschlüssen Geheimnisse. +Profile wurden laut Log manuell aus der SQLite-Datenbank generiert und gegen die Serverkonfiguration geprüft. Das ist ein historischer Vorgang, kein künftiges SOP-Verfahren. Neue Profile werden über die GUI exportiert. Der Pfad `docs/platform/GNAI-006-profiles/` wurde als gitignored genannt; Profile bleiben unabhängig von Git-Ausschlüssen Geheimnisse. Der Laptop wurde von außerhalb des Heimnetzes über EasyCable mit damaliger Außen-IPv4 `80.187.120.60` getestet. Diese und die damalige Heimnetz-WAN-IP sind zeitgebundene Messwerte, keine DNS-Sollwerte. @@ -135,4 +135,4 @@ Diese Beobachtungen bestätigen den breiten Netzpfad und die NAT-Abhängigkeit. | O8 | Reboot, Firewall-Reload und Restore | offen | | O9 | wg-easy-GUI-HTTPS und lokales Gruppenmodell | geplant; Werte noch festzulegen | -Andere Sicherheitsbefunde werden im [Dokumentationsindex](GNAI-000-DOC_Index.md) geführt. Dieses Log hält nur WireGuard-Umsetzung und zugehörige Tests fest. +Andere Sicherheitsbefunde werden im [Dokumentationsindex](../GNAI-000-DOC_Index.md) geführt. Dieses Log hält nur WireGuard-Umsetzung und zugehörige Tests fest. diff --git a/docs/GNAI-006-profiles/.gitignore b/docs/platform/GNAI-006-profiles/.gitignore similarity index 100% rename from docs/GNAI-006-profiles/.gitignore rename to docs/platform/GNAI-006-profiles/.gitignore diff --git a/docs/platform/README.md b/docs/platform/README.md new file mode 100644 index 0000000..b9d05cb --- /dev/null +++ b/docs/platform/README.md @@ -0,0 +1,21 @@ +# Plattform- und Hostdokumentation + +Dieser Bereich enthält die gemeinsame Infrastruktur, auf der KI- und +Encoding-Workloads aufbauen. Dazu gehören Hostinstallation, Benutzer und SSH, +das übergreifende Zugriffsmodell, Reverse Proxy und VPN. Workload-spezifische +Architektur und Nutzerverfahren liegen in den jeweiligen Nachbarverzeichnissen. + +| Dokument | Inhalt | +| --- | --- | +| [GNAI-001](GNAI-001-INFRA_BaseSystem.md) | Hardware und Debian-Basisinstallation des KI-Servers | +| [GNAI-002](GNAI-002-INFRA_Users.md) | Hostbenutzer, Netzwerk, Resolver und SSH | +| [GNAI-004](GNAI-004-SEC_Access-Control.md) | Rollen- und Zugriffsziel für die Infrastruktur | +| [GNAI-005](GNAI-005-SEC_Reverse-Proxy.md) | Reverse Proxy, Keycloak-Adminpfad und Fail2Ban | +| [GNAI-006](GNAI-006-SEC_VPN.md) | WireGuard-Gateway und Rollen-Firewall | +| [VPN-Client-Verwaltung](GNAI-006-SEC_VPN_Client-Management.md) | Profile ausgeben, ändern und widerrufen | +| [Windows 11](GNAI-006-SEC_VPN_Clients_Windows11.md) | WireGuard-Client unter Windows 11 | +| [Linux](GNAI-006-SEC_VPN_Clients_Linux.md) | WireGuard-Client unter unterstützten Linux-Varianten | +| [VPN-Implementation-Log](GNAI-006-SEC_VPN_Implementation-Log.md) | historische Umsetzung, Tests und Evidenzkorrekturen | + +Der übergreifende Projektstand steht im +[zentralen Dokumentationsindex](../GNAI-000-DOC_Index.md). diff --git a/docs/video-encoding/GNENC-000-DOC_Index.md b/docs/video-encoding/GNENC-000-DOC_Index.md new file mode 100644 index 0000000..b37f6bc --- /dev/null +++ b/docs/video-encoding/GNENC-000-DOC_Index.md @@ -0,0 +1,32 @@ +# Video-Encoding – Dokumentationsindex und Projektstand + +**Dokumenten-ID:** GNENC-000-DOC +**Stand:** 2026-09-20 +**Status:** Nicht finaler Arbeitsstand; Encoding-Workload noch nicht installiert + +## Dokumente + +| Dokument | Zweck / Zielgruppe | +| --- | --- | +| [GNENC-001-PLAN_Architecture.md](GNENC-001-PLAN_Architecture.md) | vorläufige Architektur, Repository-Grenzen, Ressourcenmodell und offene Entscheidungen; Infrastrukturadministratoren | + +## Bestätigter Stand + +- Das gemeinsame Infrastructure-Repository bleibt die einzige Quelle für den + Ansible-Sollzustand des physischen Servers. +- Encoding-Dokumentation verwendet den getrennten Bereich `docs/video-encoding/` + und die Dokumentenkennung `GNENC`. +- Encoding ist nach Betreiberfestlegung CPU-basiert und darf parallel zur + jeweils aktiven KI-Umgebung laufen. +- Encoding soll ausschließlich außerhalb der Stoßzeiten, zunächst nachts, + automatisch ausgeführt werden. +- Das gesperrte Servicekonto `gn-encoding` mit UID/GID 982 und eigenem + rootless Podman-Store ist eingerichtet und verifiziert. +- Es sind noch kein Encoding-Image, kein Container, keine Quadlet-Unit und kein + Timer eingerichtet. + +## Nächste Festlegungen + +Vor einer Encoding-Implementierung müssen insbesondere Encoder und +Container-Image, Ein- und Ausgabeverfahren, genaue Zeitfenster, CPU-/I/O-Grenzen, +Abbruchverhalten am Ende des Zeitfensters und Aufbewahrung festgelegt werden. diff --git a/docs/video-encoding/GNENC-001-PLAN_Architecture.md b/docs/video-encoding/GNENC-001-PLAN_Architecture.md new file mode 100644 index 0000000..24d5c3b --- /dev/null +++ b/docs/video-encoding/GNENC-001-PLAN_Architecture.md @@ -0,0 +1,142 @@ +# Arbeitsentwurf + +**Dokumenten-ID:** GNENC-001-PLAN +**Titel:** CPU-basiertes Video-Encoding auf dem KI-Server +**Stand:** 2026-09-20 +**Status:** Nicht finaler Arbeitsentwurf zur Persistierung der Planungsentscheidung + +## 1. Zweck und Abgrenzung + +Der physische Server `ghostnet-ki` soll neben den KI-Workloads einen getrennten +Workload zum Encoding von Videodateien ausführen. Der Encoding-Workload gehört +nicht zum LLM-Stack. Seine fachliche Architektur, Bedienung und späteren SOPs +werden deshalb in diesem eigenen Dokumentationsbereich geführt. + +Ansible, Podman, Betriebssystem und hostweite Ressourcenregeln bleiben +gemeinsame Plattformbestandteile. Sie werden weiterhin ausschließlich im +Infrastructure-Repository verwaltet, damit nicht mehrere Repositories +unabhängig konkurrierende Sollzustände auf denselben Host anwenden. + +## 2. Bestätigte Entscheidungen + +| Thema | Entscheidung | Nachweisgrenze | +| --- | --- | --- | +| Repository | Host- und Deployment-Ansible bleibt im bestehenden Infrastructure-Repository | angenommen | +| Dokumentation | Encoding erhält `docs/video-encoding/` und die Kennung `GNENC` | umgesetzt | +| Ausführungsidentität | eigenes gesperrtes Konto `gn-encoding`, UID/GID 982 | eingerichtet und mit rootless Podman geprüft | +| Containertrennung | eigener rootless Podman-Store und eigener rootverwalteter Quadlet-Pfad | eingerichtet; noch keine Workload definiert | +| Rechenressource | Encoding ist nach Betreiberfestlegung rein CPU-basiert und erhält keinen GPU-Zugriff | Planungsentscheidung; reale Last noch nicht gemessen | +| Parallelität | darf parallel zu AI Production oder AI Testing laufen | Planungsentscheidung | +| Zeitsteuerung | automatische Ausführung ausschließlich außerhalb der Stoßzeiten, zunächst nachts | Richtung festgelegt; konkrete Uhrzeiten fehlen | +| Anwendungen | konkrete Encoder, Images, Profile und Queue sind noch nicht gewählt | offen | + +## 3. Zielbild + +```mermaid +flowchart LR + HOST[ghostnet-ki] + PROD[gnai-production\nrootless Podman] + TEST[gnai-testing\nrootless Podman] + ENC[gn-encoding\nrootless Podman] + GPU[beide GPUs] + CPU[CPU / I-O Budget] + TIMER[nächtliche systemd-Zeitsteuerung] + + HOST --> PROD + HOST --> TEST + HOST --> ENC + PROD --> GPU + TEST --> GPU + ENC --> CPU + TIMER --> ENC +``` + +AI Production und AI Testing bleiben untereinander exklusiv. Der +Encoding-Workload ist von dieser KI-Umschaltung unabhängig und darf parallel +zur jeweils aktiven KI-Umgebung laufen. Hostwartung kann weiterhin einen Stopp +aller Workloads erfordern. + +## 4. Ressourcen- und Zeitsteuerung + +Die nächtliche Einplanung allein schützt Production nicht vollständig: Ein +Encoding-Lauf kann länger dauern als sein Startfenster, und Inferenz benötigt +trotz GPU-Schwerpunkt CPU für Tokenisierung, Netzwerk und Begleitdienste. Die +spätere Encoding-Unit soll deshalb mindestens verwalten: + +- ein eindeutig definiertes Zeitfenster mit Zeitzone; +- Verhalten bei verpasstem Start, Neustart und überlangem Lauf; +- maximale CPU-Nutzung oder CPU-Gewichtung; +- niedrigere Scheduling-Priorität gegenüber interaktiven Diensten; +- I/O-Gewichtung und freien Speicher; +- geordneten Abbruch beziehungsweise Abschluss am Ende des erlaubten Fensters; +- Begrenzung auf genau einen gleichzeitigen Encoding-Job. + +Konkrete Grenzwerte werden erst nach einem repräsentativen Encoding-Test unter +gleichzeitiger Inferenzlast festgelegt. `gn-encoding` erhält keine Mitgliedschaft +in GPU-Gruppen und keine `/dev/kfd`- oder `/dev/dri`-Weitergabe. + +## 5. Repository-Grenzen + +Vorgesehene Ansible-Struktur: + +```text +ansible/ +├── playbooks/ +│ ├── host.yml +│ ├── ai.yml +│ ├── video-encoding.yml +│ └── site.yml +└── roles/ + ├── host_baseline/ + ├── podman_runtime/ + ├── workload_accounts/ + ├── ai_stack/ + ├── ai_environment_switch/ + └── video_encoding/ +``` + +`host.yml` verwaltet ausschließlich gemeinsame Plattformzustände. +`ai.yml` darf nur KI-Zustände verändern. `video-encoding.yml` verwaltet nur das +Konto `gn-encoding`, seine Pfade, Images, Quadlets, Timer und Health Checks. Ein +späteres `site.yml` kann die unterstützte Gesamtkonfiguration in definierter +Reihenfolge anwenden. + +Ein separates Quellcode-Repository wird erst benötigt, wenn eigene +Encoding-Software, eine Oberfläche oder ein Queue-Dienst entwickelt wird. Ein +solches Repository erzeugt ein versioniertes Artefakt oder Image; ausschließlich +das Infrastructure-Repository legt fest, welche Version auf dem Server läuft. + +## 6. Aktueller Implementierungsstand + +Die gemeinsame Rolle `workload_accounts` hat am 20.09.2026 eingerichtet: + +```text +Konto: gn-encoding (UID/GID 982) +Konfiguration: /etc/ghostnet-encoding +Daten: /var/lib/ghostnet-encoding +Eingang: /var/lib/ghostnet-encoding/input +Ausgang: /var/lib/ghostnet-encoding/output +Arbeitsdaten: /var/lib/ghostnet-encoding/work +Quadlets: /etc/containers/systemd/users/982 +Podman-Store: /var/lib/ghostnet-encoding/.local/share/containers/storage +``` + +Passwort und interaktive Shell sind gesperrt. Der systemd-User-Manager läuft +dank Linger unabhängig von einer Anmeldung. Der Podman-Store wurde als +rootless und getrennt von beiden KI-Stores geprüft. Es wurden noch keine Images +oder Container angelegt. + +## 7. Offene Entscheidungen + +1. Welcher Encoder und welches fest versionierte Container-Image werden genutzt? +2. Welche Codecs, Qualitätsprofile und Hardwarekompatibilitäten sind erforderlich? +3. Woher kommen Eingabedateien, und wie werden fertige Dateien abgeholt? +4. Welches konkrete nächtliche Zeitfenster und welche Zeitzone gelten? +5. Darf ein laufender Job das Zeitfenster überschreiten oder wird er gestoppt? +6. Welche CPU-, Speicher- und I/O-Grenzen schützen die KI-Dienste? +7. Wie werden Warteschlange, Wiederholung, Teilresultate und Fehler behandelt? +8. Welche Aufbewahrungs-, Backup- und Löschregeln gelten für Video- und + Arbeitsdateien? +9. Welche Statusanzeige oder Benachrichtigung benötigen Nutzer? + +Diese Werte werden nicht vorab in Playbooks angenommen.