# Betrieb Dieses Dokument richtet sich an die Person, die die Anlage installiert, aktualisiert und im Fehlerfall wieder zum Laufen bringt. Es beschreibt, was auf dem Host tatsaechlich passiert -- nicht, wie die Oberflaeche bedient wird. --- ## 1. Was wo liegt | Zweck | Pfad | |---|---| | Anwendung (Code, venv, statische Dateien) | `/srv//` | | Instanzdaten (Datenbank, Schluessel, Lizenz) | `/srv//data/` | | Protokolle | `/var/log//` | | Umgebungsdatei | `/etc/tesm/.env` | | Zertifikate (selbstsigniert) | `/etc/tesm/certs//` | | nginx-Site | `/etc/nginx/sites-available/` | | systemd-Unit | `/etc/systemd/system/.service` | | Privilegierter Helfer | `/usr/local/lib/tesm/tesm-helper` | | Verwaltungswerkzeug | `/usr/local/bin/-admin` | `` ist `tesm` bzw. `tesm-license` -- oder, bei einer zusaetzlichen Instanz, `tesm-`. Dieser Name (der **SITE_KEY**) benennt alles, was ausserhalb von `/srv/` liegt. Deshalb koennen zwei Installationen derselben Anwendung nebeneinander laufen, ohne sich gegenseitig die nginx-Site, die netplan-Datei oder das Sitzungscookie zu ueberschreiben. Alles, was ein Mensch verlieren wuerde, liegt unter `data/`. Das Verzeichnis wird von `install.sh` nie angefasst. --- ## 2. Installation ```bash sudo bash deploy/install.sh --app tesm sudo bash deploy/install.sh --app tesm-license ``` Wichtige Optionen: | Option | Wirkung | |---|---| | `--https` | Port 443 mit TLS, Port 80 leitet dorthin um. Ohne `--domain` entsteht ein selbstsigniertes Zertifikat. | | `--domain ` | Oeffentlicher Name. Setzt `server_name`; die Site ist dann **nicht** mehr die Vorgabe-Site. | | `--acme-email ` | Erst zusammen mit `--domain` wird ein Let's-Encrypt-Zertifikat angefordert (und HSTS gesetzt). Ohne die Adresse bleibt es selbstsigniert -- interne Namen wie `lizenz.firma.local` kann keine CA bestaetigen. | | `--instance ` | Zusaetzliche Instanz neben der Hauptinstallation. | | `--port` / `--http-port` / `--https-port` | Abweichende Ports (nur fuer Testinstanzen sinnvoll). | | `--skip-packages` | Keine Systempakete installieren. | Danach: ```bash sudo -u tesm tesm-admin create-admin ``` Beim Lizenzserver zusaetzlich, **in dieser Reihenfolge**: ```bash sudo -u tesm-license tesm-license-admin init-key # Signaturschluessel -- sofort sichern! sudo -u tesm-license tesm-license-admin bootstrap # eigene Master-Lizenz sudo -u tesm-license tesm-license-admin create-admin ``` > **Der Signaturschluessel ist nicht wiederherstellbar.** Geht > `data/master_signing_key.json` verloren, ist keine ausgestellte Lizenz mehr > pruefbar. Sichern Sie ihn getrennt vom Server. ### Zwei Anwendungen auf einem Host Beide wollen Port 80 und 443. nginx unterscheidet sie am Namen: ```bash sudo bash deploy/install.sh --app tesm --https sudo bash deploy/install.sh --app tesm-license --https --domain lizenz.firma.local ``` Die Installation **ohne** Domain wird zur Vorgabe-Site (`default_server`) und beantwortet alles, was zu keinem Namen passt. Zwei namenlose Sites auf demselben Port weist nginx ab -- richtig so, und beim Uebernehmen sofort sichtbar. --- ## 3. Aktualisierung ```bash sudo bash deploy/install.sh --app tesm ``` Derselbe Befehl. Das Skript erkennt an der vorhandenen Datenbank, dass es eine Aktualisierung ist, und tut dann zusaetzlich: 1. **Vollstaendige Sicherung** nach `/srv/-backup-` -- unabhaengig davon, ob sich das Schema geaendert hat. 2. Dateiabgleich; `data/` bleibt unangetastet. 3. Schema-Migrationen beim Start (versioniert, mit Pruefsumme). 4. **Gesundheitspruefung.** Antwortet die Anwendung nicht, wird der alte Stand automatisch zurueckgeholt; die fehlgeschlagene Version bleibt unter `/srv/-failed-` zur Analyse liegen. Eine bereits eingerichtete HTTPS-Konfiguration bleibt erhalten, auch ohne `--https`. --- ## 4. HTTPS Der Zielzustand ist immer: **Port 443 mit TLS, Port 80 leitet um, ausser dem ACME-Pfad.** Drei Wege dorthin: **a) Bei der Installation** -- siehe oben, `--https`. **b) Nachtraeglich ueber die Kommandozeile:** ```bash sudo -u tesm tesm-admin web-setup --https --self-signed --apply sudo -u tesm tesm-admin web-setup --https --domain tesm.firma.de \ --acme --acme-email it@firma.de --hsts --apply ``` `--print-config` zeigt die Datei, ohne etwas zu schreiben. **c) In der Oberflaeche** unter *Einstellungen -> Webserver und TLS*. Danach muss `COOKIE_SECURE` in `/etc/tesm/.env` auf `1` stehen (alle drei Wege erledigen das selbst) und der Dienst neu gestartet werden. ### Die Site-Datei wird immer neu erzeugt Sie ist **kein** Ort fuer Handarbeit. Jede Uebernahme schreibt sie vollstaendig aus der Vorlage neu. Was frueher von Hand hineingeschrieben wurde -- Domain, Ports, Zertifikatspfade, HSTS, Upload-Grenze -- ist heute Einstellung in der Anwendung. Der Grund steht im Vorgaengerprojekt: dort wurde die Datei nur angelegt, wenn sie fehlte. Ein einmal falscher `alias`-Pfad ueberlebte dadurch jahrelang jedes Update, und die Oberflaeche kam die ganze Zeit ohne Design. ### Selbstsigniert oder Let's Encrypt? * **Oeffentlich erreichbar, echter DNS-Name:** Let's Encrypt. Nur dann ist HSTS sinnvoll. * **Geschlossenes Netz:** selbstsigniert. Browser zeigen eine Warnung, die Verbindung ist trotzdem verschluesselt und das Sitzungscookie darf `Secure` tragen. **HSTS hier nicht einschalten** -- der Browser merkt sich die Vorgabe monatelang und die Warnung laesst sich dann nicht mehr wegklicken. Die Erneuerung von Let's Encrypt laeuft ueber `certbot certonly --webroot` (bewusst nicht ueber das nginx-Plugin, das die erzeugte Site umschreiben wuerde). Der ACME-Pfad bleibt auch bei aktiver Umleitung erreichbar. --- ## 5. Dienste ```bash systemctl status tesm.service # Weboberflaeche (gunicorn) systemctl status tesm-monitor.service # Ueberwachungsschleife (nur TESM) systemctl status tesm-license.service journalctl -u tesm -n 100 -f ``` Die Units laufen unprivilegiert (`User=tesm`) mit `NoNewPrivileges`, `ProtectSystem=strict`, `PrivateTmp` und einer engen `RestrictAddressFamilies`-Liste. Schreibrechte gibt es nur auf `/srv//data` und `/var/log/`. --- ## 6. Root-Rechte Die Anwendung hat keine. Alles, was Root braucht, geht durch **einen** Pfad: ``` /usr/local/lib/tesm/tesm-helper [argumente] ``` Der Helfer gehoert `root`, ist per sudoers ausschliesslich fuer genau diesen Pfad mit `NOPASSWD` freigegeben und prueft jedes Argument gegen eine Positivliste. Er reicht nie etwas an eine Shell weiter (kein `eval`, kein `sh -c`). Verben ansehen: ```bash sudo /usr/local/lib/tesm/tesm-helper grep -n ')$' /usr/local/lib/tesm/tesm-helper ``` Regel fuer Aenderungen: **Jedes neue Verb braucht eine Argumentpruefung.** `tests/test_repo_hygiene.py` erzwingt, dass jedes der Anwendung bekannte Verb im Helfer vorkommt und dass jede Pfadpruefung `..` abweist. --- ## 7. Sicherung und Wiederherstellung In der Oberflaeche unter *Sicherung* -- verschluesselte Ausfuhr (Argon2id + AES-256-GCM) mit einem selbst gewaehlten Kennwort. Der Import zeigt zuerst eine Vorschau, bevor irgendetwas geschrieben wird. Auf dem Host reicht: ```bash sudo systemctl stop tesm sudo tar -czf /root/tesm-data-$(date +%F).tar.gz -C /srv/tesm data sudo systemctl start tesm ``` Unverzichtbar: * `data/app.db` -- alle Daten * `data/secret.key` -- ohne sie sind alle Sitzungen ungueltig * `data/data.keys` -- ohne sie sind **alle gespeicherten Geheimnisse verloren** * `data/license.json` und `data/license_key.json` * Lizenzserver zusaetzlich: `data/master_signing_key.json` --- ## 8. Wenn etwas nicht geht **Die Anwendung antwortet nicht.** ```bash journalctl -u tesm -n 200 --no-pager curl -s http://127.0.0.1:5000/gesundheit ``` Antwortet gunicorn, aber der Browser nicht, liegt es an nginx. **nginx laesst sich nicht neu laden.** ```bash sudo nginx -t ``` Die Uebernahme aus der Anwendung nimmt eine abgelehnte Konfiguration selbst zurueck. Eine von Hand bearbeitete Datei tut das nicht -- in dem Fall hilft `install.sh`, das eine unbrauchbare Site durch die Vorlage ersetzt. **Anmeldung schlaegt ohne Fehlermeldung fehl.** Fast immer `COOKIE_SECURE=1` ohne HTTPS. Der Browser bekommt dann ein Cookie, das er ueber HTTP nie zuruecksendet. ```bash grep COOKIE_SECURE /etc/tesm/tesm.env ``` **"Die Anfrage kam von einer fremden Herkunft (Origin/Referer)."** Der Reverse Proxy reicht den Host falsch durch. In der Site-Datei muss `proxy_set_header Host $http_host;` stehen -- **nicht** `$host`, das verwirft den Port. **Statische Dateien fehlen, die Oberflaeche ist unformatiert.** ```bash grep alias /etc/nginx/sites-available/tesm ls /srv/tesm/static /srv/tesm/static-core ``` Beide `alias`-Pfade muessen in das Installationsverzeichnis zeigen. **Zwei Instanzen stoeren sich.** `SITE_KEY` in beiden `/etc/tesm/*.env` vergleichen -- er muss sich unterscheiden. Sonst teilen sich beide nginx-Site und Sitzungscookie. **Die Wartung meldet "Der SSH-Host-Schluessel ist unbekannt".** Kein Fehler, sondern die Absicht: ein Hintergrundauftrag darf keinen fremden Schluessel akzeptieren. Die Freigabe erfolgt von Hand, in zwei Schritten, auf der Detailseite des Geraets (*Clients -> Geraet -> SSH-Host-Schluessel*): erst **Auslesen**, dann den Fingerprint mit einer unabhaengigen Quelle vergleichen, dann **Freigeben**. Die Wartungsuebersicht zeigt in einer eigenen Spalte, fuer welche Geraete das noch offen ist. Fuer Switche liegt dieselbe Freigabe auf der Switch-Detailseite. Der Schluessel wird nach **IP und Port** gespeichert, nicht nach Name -- TESM verbindet sich immer ueber die hinterlegte IP-Adresse. Aendert sich die IP eines Geraets, ist der Schluessel erneut freizugeben. **Die Pruefkette des Aenderungsprotokolls ist gebrochen.** Unter *Diagnose* sichtbar. Das Protokoll ist verkettet gehasht; ein Bruch heisst, dass jemand direkt in die Datenbank geschrieben hat. Der Eintrag, ab dem es nicht mehr stimmt, wird genannt. --- ## 9. Zurueck auf eine aeltere Version ```bash sudo systemctl stop tesm sudo mv /srv/tesm /srv/tesm-kaputt sudo cp -a /srv/tesm-backup- /srv/tesm sudo systemctl start tesm ``` Schema-Migrationen laufen nur vorwaerts. Ein Ruecksprung ueber eine Schema-Aenderung hinweg braucht deshalb auch die Datenbank aus der Sicherung -- genau das liegt im Backup-Verzeichnis.