Umlaute in der Oberflaeche, Sitzungskopplung, Statusanzeigen

Echte Umlaute in allen Texten der Oberflaeche und in den Markdown-Dateien.
Die Einbindung von Dateifreigaben haengt jetzt an der Sitzung: wird sie
widerrufen oder laeuft sie ab, verschwinden die Freigaben mit ihr. Neu
einstellbar ist die automatische Abmeldung bei Leerlauf (Vorgabe 30 Minuten).
Ein Geraet, das nicht antwortet, bietet nur noch "Einschalten (PoE)" an --
Neustart ueber SSH oder RPC braucht ein erreichbares Geraet. Der Zeitgeber der
naechsten Pruefung und die Restlaufzeit der Lizenz stehen jetzt oben in der
Kopfzeile; der doppelte Knopf darunter ist weg. Millisekunden werden ab einer
Sekunde als Sekunden und ab einer Minute als m:ss dargestellt.

Ausserdem: Release-Tarball nicht mehr im Repository -- er haengt am Release.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-02 23:54:33 +02:00
co-authored by Claude Opus 5
parent 9a760274f8
commit fbbf8eb87b
68 changed files with 1235 additions and 691 deletions
+65 -65
View File
@@ -2,7 +2,7 @@
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.
dem Host tatsächlich passiert -- nicht, wie die Oberfläche bedient wird.
---
@@ -11,7 +11,7 @@ dem Host tatsaechlich passiert -- nicht, wie die Oberflaeche bedient wird.
| Zweck | Pfad |
|---|---|
| Anwendung (Code, venv, statische Dateien) | `/srv/<name>/` |
| Instanzdaten (Datenbank, Schluessel, Lizenz) | `/srv/<name>/data/` |
| Instanzdaten (Datenbank, Schlüssel, Lizenz) | `/srv/<name>/data/` |
| Protokolle | `/var/log/<name>/` |
| Umgebungsdatei | `/etc/tesm/<name>.env` |
| Zertifikate (selbstsigniert) | `/etc/tesm/certs/<name>/` |
@@ -20,13 +20,13 @@ dem Host tatsaechlich passiert -- nicht, wie die Oberflaeche bedient wird.
| Privilegierter Helfer | `/usr/local/lib/tesm/tesm-helper` |
| Verwaltungswerkzeug | `/usr/local/bin/<name>-admin` |
`<name>` ist `tesm` bzw. `tesm-license` -- oder, bei einer zusaetzlichen
`<name>` ist `tesm` bzw. `tesm-license` -- oder, bei einer zusätzlichen
Instanz, `tesm-<instanz>`. Dieser Name (der **SITE_KEY**) benennt alles, was
ausserhalb von `/srv/<name>` liegt. Deshalb koennen zwei Installationen
ausserhalb von `/srv/<name>` liegt. Deshalb können zwei Installationen
derselben Anwendung nebeneinander laufen, ohne sich gegenseitig die
nginx-Site, die netplan-Datei oder das Sitzungscookie zu ueberschreiben.
nginx-Site, die netplan-Datei oder das Sitzungscookie zu überschreiben.
Alles, was ein Mensch verlieren wuerde, liegt unter `data/`. Das Verzeichnis
Alles, was ein Mensch verlieren würde, liegt unter `data/`. Das Verzeichnis
wird von `install.sh` nie angefasst.
---
@@ -43,10 +43,10 @@ Wichtige Optionen:
| Option | Wirkung |
|---|---|
| `--https` | Port 443 mit TLS, Port 80 leitet dorthin um. Ohne `--domain` entsteht ein selbstsigniertes Zertifikat. |
| `--domain <name>` | Oeffentlicher Name. Setzt `server_name`; die Site ist dann **nicht** mehr die Vorgabe-Site. |
| `--acme-email <a>` | 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 <name>` | Zusaetzliche Instanz neben der Hauptinstallation. |
| `--port` / `--http-port` / `--https-port` | Abweichende Ports (nur fuer Testinstanzen sinnvoll). |
| `--domain <name>` | Öffentlicher Name. Setzt `server_name`; die Site ist dann **nicht** mehr die Vorgabe-Site. |
| `--acme-email <a>` | 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 bestätigen. |
| `--instance <name>` | Zusätzliche Instanz neben der Hauptinstallation. |
| `--port` / `--http-port` / `--https-port` | Abweichende Ports (nur für Testinstanzen sinnvoll). |
| `--skip-packages` | Keine Systempakete installieren. |
Danach:
@@ -55,17 +55,17 @@ Danach:
sudo -u tesm tesm-admin create-admin
```
Beim Lizenzserver zusaetzlich, **in dieser Reihenfolge**:
Beim Lizenzserver zusätzlich, **in dieser Reihenfolge**:
```bash
sudo -u tesm-license tesm-license-admin init-key # Signaturschluessel -- sofort sichern!
sudo -u tesm-license tesm-license-admin init-key # Signaturschlüssel -- 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
> **Der Signaturschlüssel ist nicht wiederherstellbar.** Geht
> `data/master_signing_key.json` verloren, ist keine ausgestellte Lizenz mehr
> pruefbar. Sichern Sie ihn getrennt vom Server.
> prüfbar. Sichern Sie ihn getrennt vom Server.
### Zwei Anwendungen auf einem Host
@@ -78,7 +78,7 @@ sudo bash deploy/install.sh --app tesm-license --https --domain lizenz.firma.loc
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
demselben Port weist nginx ab -- richtig so, und beim Übernehmen sofort
sichtbar.
---
@@ -90,14 +90,14 @@ 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:
Aktualisierung ist, und tut dann zusätzlich:
1. **Vollstaendige Sicherung** nach `/srv/<name>-backup-<zeitstempel>` --
unabhaengig davon, ob sich das Schema geaendert hat.
1. **Vollständige Sicherung** nach `/srv/<name>-backup-<zeitstempel>` --
unabhängig davon, ob sich das Schema geändert 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
3. Schema-Migrationen beim Start (versioniert, mit Prüfsumme).
4. **Gesundheitsprüfung.** Antwortet die Anwendung nicht, wird der alte Stand
automatisch zurückgeholt; die fehlgeschlagene Version bleibt unter
`/srv/<name>-failed-<zeitstempel>` zur Analyse liegen.
Eine bereits eingerichtete HTTPS-Konfiguration bleibt erhalten, auch ohne
@@ -114,7 +114,7 @@ Drei Wege dorthin:
**a) Bei der Installation** -- siehe oben, `--https`.
**b) Nachtraeglich ueber die Kommandozeile:**
**b) Nachträglich über die Kommandozeile:**
```bash
sudo -u tesm tesm-admin web-setup --https --self-signed --apply
@@ -124,42 +124,42 @@ sudo -u tesm tesm-admin web-setup --https --domain tesm.firma.de \
`--print-config` zeigt die Datei, ohne etwas zu schreiben.
**c) In der Oberflaeche** unter *Einstellungen -> Webserver und TLS*.
**c) In der Oberfläche** unter *Einstellungen -> Webserver und TLS*.
Danach muss `COOKIE_SECURE` in `/etc/tesm/<name>.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,
Sie ist **kein** Ort für Handarbeit. Jede Übernahme schreibt sie vollständig
aus der Vorlage neu. Was früher 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.
Der Grund steht im Vorgängerprojekt: dort wurde die Datei nur angelegt, wenn
sie fehlte. Ein einmal falscher `alias`-Pfad überlebte dadurch jahrelang jedes
Update, und die Oberfläche kam die ganze Zeit ohne Design.
### Selbstsigniert oder Let's Encrypt?
* **Oeffentlich erreichbar, echter DNS-Name:** Let's Encrypt. Nur dann ist HSTS
* **Öffentlich 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`
Verbindung ist trotzdem verschlüsselt 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.
monatelang und die Warnung lässt 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.
Die Erneuerung von Let's Encrypt läuft über `certbot certonly --webroot`
(bewusst nicht über das nginx-Plugin, das die erzeugte Site umschreiben
würde). 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.service # Weboberfläche (gunicorn)
systemctl status tesm-monitor.service # Überwachungsschleife (nur TESM)
systemctl status tesm-license.service
journalctl -u tesm -n 100 -f
```
@@ -179,8 +179,8 @@ Die Anwendung hat keine. Alles, was Root braucht, geht durch **einen** Pfad:
/usr/local/lib/tesm/tesm-helper <verb> [argumente]
```
Der Helfer gehoert `root`, ist per sudoers ausschliesslich fuer genau diesen
Pfad mit `NOPASSWD` freigegeben und prueft jedes Argument gegen eine
Der Helfer gehört `root`, ist per sudoers ausschliesslich für genau diesen
Pfad mit `NOPASSWD` freigegeben und prüft jedes Argument gegen eine
Positivliste. Er reicht nie etwas an eine Shell weiter (kein `eval`, kein
`sh -c`).
@@ -191,16 +191,16 @@ sudo /usr/local/lib/tesm/tesm-helper
grep -n ')$' /usr/local/lib/tesm/tesm-helper
```
Regel fuer Aenderungen: **Jedes neue Verb braucht eine Argumentpruefung.**
Regel für Änderungen: **Jedes neue Verb braucht eine Argumentprüfung.**
`tests/test_repo_hygiene.py` erzwingt, dass jedes der Anwendung bekannte Verb
im Helfer vorkommt und dass jede Pfadpruefung `..` abweist.
im Helfer vorkommt und dass jede Pfadprüfung `..` 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
In der Oberfläche unter *Sicherung* -- verschlüsselte Ausfuhr (Argon2id +
AES-256-GCM) mit einem selbst gewählten Kennwort. Der Import zeigt zuerst eine
Vorschau, bevor irgendetwas geschrieben wird.
Auf dem Host reicht:
@@ -214,10 +214,10 @@ sudo systemctl start tesm
Unverzichtbar:
* `data/app.db` -- alle Daten
* `data/secret.key` -- ohne sie sind alle Sitzungen ungueltig
* `data/secret.key` -- ohne sie sind alle Sitzungen ungültig
* `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`
* Lizenzserver zusätzlich: `data/master_signing_key.json`
---
@@ -230,17 +230,17 @@ 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.**
**nginx lässt 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
Die Übernahme aus der Anwendung nimmt eine abgelehnte Konfiguration selbst
zurück. 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.**
**Anmeldung schlägt ohne Fehlermeldung fehl.**
Fast immer `COOKIE_SECURE=1` ohne HTTPS. Der Browser bekommt dann ein Cookie,
das er ueber HTTP nie zuruecksendet.
das er über HTTP nie zurücksendet.
```bash
grep COOKIE_SECURE /etc/tesm/tesm.env
```
@@ -250,38 +250,38 @@ 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.**
**Statische Dateien fehlen, die Oberfläche 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.
Beide `alias`-Pfade müssen in das Installationsverzeichnis zeigen.
**Zwei Instanzen stoeren sich.**
**Zwei Instanzen stören 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".**
**Die Wartung meldet "Der SSH-Host-Schlüssel 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
Schlüssel akzeptieren. Die Freigabe erfolgt von Hand, in zwei Schritten, auf
der Detailseite des Geräts (*Clients -> Gerät -> SSH-Host-Schlüssel*):
erst **Auslesen**, dann den Fingerprint mit einer unabhängigen Quelle
vergleichen, dann **Freigeben**. Die Wartungsübersicht zeigt in einer eigenen
Spalte, für welche Geräte das noch offen ist. Für 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.
Der Schlüssel wird nach **IP und Port** gespeichert, nicht nach Name -- TESM
verbindet sich immer über die hinterlegte IP-Adresse. Ändert sich die IP
eines Geräts, ist der Schlüssel erneut freizugeben.
**Die Pruefkette des Aenderungsprotokolls ist gebrochen.**
**Die Prüfkette des Änderungsprotokolls 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
## 9. Zurück auf eine ältere Version
```bash
sudo systemctl stop tesm
@@ -290,6 +290,6 @@ sudo cp -a /srv/tesm-backup-<zeitstempel> /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 --
Schema-Migrationen laufen nur vorwärts. Ein Rücksprung über eine
Schema-Änderung hinweg braucht deshalb auch die Datenbank aus der Sicherung --
genau das liegt im Backup-Verzeichnis.