Vollstaendiger Neubau der Anwendung. Der vorherige Stand bleibt unveraendert im Zweig SONNET5 erhalten. Aufbau: apps/tesm (Anwendung), packages/tesm-core (gemeinsamer Kern), packages/tesm-licensing (Lizenzprotokoll), deploy (Installation, systemd, privilegierter Helfer), docs, tests. Der Lizenzserver liegt in seinem eigenen Repository; beide Repositorien bringen die gemeinsamen Pakete mit, damit sich jedes allein installieren laesst. Die wichtigsten Unterschiede zum Vorgaenger, jeweils an der Stelle im Code kommentiert, an der der Fehler entstanden ist: * Der Webprozess laeuft unprivilegiert. Alles, was Root braucht, geht ueber einen einzigen Helfer mit Positivlisten fuer jedes Argument. * CSRF-Schutz ueberhaupt -- der Vorgaenger hatte keinen. * Rechte werden serverseitig geprueft, nicht nur im Template ausgeblendet. * Die nginx-Site wird bei jedem Lauf inhaltlich verglichen und erneuert. * Jede erzeugte Konfiguration wird vor dem Uebernehmen geprueft (nginx, Kea). * Kein Hostname im Lizenz-Fingerabdruck. * Zwei Installationen auf einem Host stoeren sich nicht (eigener SITE_KEY). * Verschachtelte Datenbankverbindungen sind ein Fehler, kein Deadlock. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
177 lines
5.7 KiB
Markdown
177 lines
5.7 KiB
Markdown
# TESM
|
|
|
|
Verwaltung von PoE-Geraeten und Aruba-Switchen: Inventar, Zustandsueberwachung,
|
|
Portsteuerung, Neustart (PoE, SSH, RPC), DHCP ueber Kea, Dateifreigaben,
|
|
Wartung ueber SSH, Protokolle, Papierkorb, verschluesselte Ein- und Ausfuhr und
|
|
ein SSH-Terminal im Browser.
|
|
|
|
Der Lizenzserver liegt in einem eigenen Repository:
|
|
[alientim/tesm-license](https://gitea.int.eertmoed.net/alientim/tesm-license).
|
|
Beide teilen sich den Kern (`packages/tesm-core`) und das Lizenzprotokoll
|
|
(`packages/tesm-licensing`); diese Pakete sind hier mit enthalten, damit sich
|
|
das Repository allein installieren laesst.
|
|
|
|
## Zweige
|
|
|
|
| Zweig | Inhalt |
|
|
|---|---|
|
|
| `main` | dieser Neubau (Version 2.0.0) |
|
|
| `SONNET5` | der Stand davor, unveraendert aufbewahrt |
|
|
|
|
---
|
|
|
|
## Installation auf einem Server
|
|
|
|
```bash
|
|
sudo bash deploy/install.sh --app tesm --https
|
|
sudo -u tesm tesm-admin create-admin
|
|
```
|
|
|
|
Danach `https://<host>/`. Ohne `--domain` entsteht ein selbstsigniertes
|
|
Zertifikat; der Browser warnt einmal, die Verbindung ist verschluesselt.
|
|
|
|
Mit oeffentlichem Namen und Let's Encrypt stattdessen:
|
|
|
|
```bash
|
|
sudo bash deploy/install.sh --app tesm --https --domain tesm.firma.de --acme-email it@firma.de
|
|
```
|
|
|
|
Zielzustand: **Port 443 mit TLS, Port 80 leitet dorthin um** -- ausser dem
|
|
ACME-Pfad, der erreichbar bleiben muss, sonst kann Let's Encrypt nie erneuern.
|
|
|
|
Sitzt ein eigener Reverse Proxy davor, der TLS beendet, laeuft TESM ohne
|
|
`--https` schlicht auf Port 80.
|
|
|
|
Eine zweite Installation zum Testen stoert die laufende nicht:
|
|
|
|
```bash
|
|
sudo bash deploy/install.sh --app tesm --instance test --port 5100 --http-port 8080
|
|
```
|
|
|
|
Jede Instanz bekommt eigene Datenbank, eigene Schluessel, eigene nginx-Site und
|
|
ein eigenes Sitzungscookie.
|
|
|
|
### Aktualisieren
|
|
|
|
```bash
|
|
sudo bash deploy/update.sh --app tesm
|
|
```
|
|
|
|
Das laedt ein Release, entpackt es und ruft dessen `install.sh` auf. Vor jedem
|
|
Update entsteht ein vollstaendiges Backup -- unabhaengig davon, ob sich das
|
|
Schema geaendert hat -- und danach laeuft ein Health-Check mit automatischem
|
|
Rollback.
|
|
|
|
---
|
|
|
|
## Release bauen
|
|
|
|
```bash
|
|
git tag -a v2.0.0 -m "TESM 2.0.0" && git push origin v2.0.0
|
|
bash deploy/release.sh v2.0.0
|
|
```
|
|
|
|
Heraus kommt `tesm-v2.0.0.tar.gz` samt SHA-256. Der Name kommt aus dem
|
|
Repository, nicht aus einer festen Zeichenkette.
|
|
|
|
---
|
|
|
|
## Entwicklung
|
|
|
|
```bash
|
|
python -m venv .venv && . .venv/bin/activate
|
|
pip install -e packages/tesm-licensing -e packages/tesm-core -e apps/tesm
|
|
pip install pytest
|
|
pytest tests -q
|
|
```
|
|
|
|
Ohne gesetzte Umgebungsvariablen legt die Anwendung ihre Daten unter
|
|
`instance/tesm/` im Arbeitsverzeichnis an -- kein Zugriff auf `/srv` oder
|
|
`/var/log` noetig.
|
|
|
|
```bash
|
|
TESM_DEBUG=1 flask --app tesm run
|
|
```
|
|
|
|
Im Debug-Modus wird `COOKIE_SECURE` abgeschaltet, sonst kaeme das
|
|
Sitzungscookie ueber `http://127.0.0.1` nie an.
|
|
|
|
---
|
|
|
|
## Verwaltung von der Kommandozeile
|
|
|
|
```bash
|
|
sudo -u tesm tesm-admin status # Zustand der Installation
|
|
sudo -u tesm tesm-admin create-admin # Konto anlegen oder zuruecksetzen
|
|
sudo -u tesm tesm-admin check-now # Ueberwachungsdurchlauf sofort
|
|
sudo -u tesm tesm-admin housekeeping # Sitzungen und Protokolle aufraeumen
|
|
sudo -u tesm tesm-admin web-setup --https # nginx und TLS einrichten
|
|
```
|
|
|
|
### Umzug aus dem alten TESM
|
|
|
|
Ein Export des Vorgaengers laesst sich uebersetzen und dann ueber den normalen
|
|
Import einlesen -- Clients, Switche, Zugangsdaten und die DHCP-Konfiguration:
|
|
|
|
```bash
|
|
tesm-admin convert-legacy tesm_export_20260813_195455.json --dry-run
|
|
```
|
|
|
|
`--dry-run` zeigt nur den Bericht. Ohne die Option entsteht eine Datei im
|
|
neuen Format, die unter *Verwaltung -> Sicherung* eingelesen wird. Der Bericht
|
|
nennt ausdruecklich, was **nicht** uebersetzt werden kann (lokale Konten, Rechte,
|
|
Verzeichnisdienst, Webserver) -- das ist von Hand nachzuziehen.
|
|
|
|
Die Passphrase gehoert an die verdeckte Eingabeaufforderung. `--passphrase`
|
|
existiert nur fuer Automatisierung: es schreibt sie in Shell-Historie und
|
|
Prozessliste.
|
|
|
|
---
|
|
|
|
## Dokumentation
|
|
|
|
| | |
|
|
|---|---|
|
|
| [docs/ARCHITEKTUR.md](docs/ARCHITEKTUR.md) | Aufbau, Schnitte und die Gruende dafuer |
|
|
| [docs/SICHERHEIT.md](docs/SICHERHEIT.md) | Anmeldung, Sitzungen, CSRF, Geheimnisse, Rechtetrennung |
|
|
| [docs/BETRIEB.md](docs/BETRIEB.md) | Installation, Update, HTTPS, Sicherung, Fehlersuche |
|
|
|
|
---
|
|
|
|
## Tests
|
|
|
|
```bash
|
|
pytest tests -q
|
|
```
|
|
|
|
Dazu ein Skript, das **auf dem Zielhost gegen die laufende Installation**
|
|
spricht -- ueber nginx, gunicorn und echtes TLS. Es wird nicht mitinstalliert
|
|
(`install.sh` laesst `tests/` aus); bei Bedarf dorthin kopieren. Jeder Lauf legt
|
|
eigene Datensaetze an, ist also beliebig oft wiederholbar:
|
|
|
|
```bash
|
|
python3 tests/e2e_smoke.py https://host admin 'Passwort'
|
|
```
|
|
|
|
---
|
|
|
|
## Was diese Fassung anders macht
|
|
|
|
Ein Neubau. Die Fehler des Vorgaengers sind bekannt; an den Stellen, an denen
|
|
sie entstanden sind, steht jetzt ein Kommentar, der erklaert, warum es hier
|
|
anders geloest ist. Die wichtigsten:
|
|
|
|
* **Kein root fuer die Anwendung.** Ein einziger allowlisted Helfer mit
|
|
Argumentpruefung statt eines Webprozesses als `root`.
|
|
* **CSRF-Schutz ueberhaupt.** Der Vorgaenger hatte keinen.
|
|
* **Rechte werden serverseitig geprueft**, nicht nur im Template ausgeblendet.
|
|
* **Die nginx-Site wird bei jedem Lauf inhaltlich verglichen und erneuert.** Ein
|
|
falscher `alias`-Pfad ueberlebte im Vorgaenger jahrelang jedes Update.
|
|
* **Kein Hostname im Lizenz-Fingerabdruck.** Umbenennen brach die Bindung.
|
|
* **Zwei Installationen stoeren sich nicht** -- eigener `SITE_KEY` fuer
|
|
nginx-Site, netplan, logrotate, Zertifikate und Sitzungscookie.
|
|
* **Verschachtelte Datenbankverbindungen sind ein Fehler**, kein Deadlock.
|
|
* **Jede Konfiguration wird vor dem Uebernehmen geprueft** -- nginx wie Kea.
|
|
Eine fehlerhafte Datei faellt beim Schreiben auf, nicht erst beim naechsten
|
|
Start des Dienstes.
|