# TESM-Lizenzserver Ausgabe und Verwaltung der Lizenzen fuer TESM: Kunde -> Ticket -> Lizenz, Aktivierung online und offline, Kundenportal, Selbstbedienung, Versand ueber MS Graph und eine Hierarchie aus Unter-Lizenzservern. Die verwaltete Anwendung liegt in einem eigenen Repository: [alientim/tesm](https://gitea.int.eertmoed.net/alientim/tesm). 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-license --https --domain lizenz.firma.de sudo -u tesm-license tesm-license-admin init-key sudo -u tesm-license tesm-license-admin bootstrap sudo -u tesm-license tesm-license-admin create-admin ``` **`init-key` erzeugt den Signaturschluessel dieses Servers. Sichern Sie ihn sofort.** Ohne ihn laesst sich keine ausgestellte Lizenz mehr erneuern, und Kunden muessten einen Ausstellerwechsel bestaetigen. 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 der Dienst ohne `--https` schlicht auf Port 80. Der Lizenzserver darf neben TESM auf demselben Host laufen; nginx unterscheidet die beiden am Servernamen, und jede Installation hat ihren eigenen `SITE_KEY` fuer nginx-Site, logrotate, Zertifikate und Sitzungscookie. ### Aktualisieren ```bash sudo bash deploy/update.sh --app tesm-license ``` Vor jedem Update entsteht ein vollstaendiges Backup, danach laeuft ein Health-Check mit automatischem Rollback. --- ## Release bauen ```bash git tag -a v2.0.0 -m "TESM-Lizenzserver 2.0.0" && git push origin v2.0.0 bash deploy/release.sh v2.0.0 ``` Heraus kommt `tesm-license-v2.0.0.tar.gz` samt SHA-256. Der Name kommt aus dem Repository -- ein Lizenzserver-Release, das `tesm-...` heisst, verwechselt man genau einmal. --- ## Entwicklung ```bash python -m venv .venv && . .venv/bin/activate pip install -e packages/tesm-licensing -e packages/tesm-core -e apps/tesm-license pip install pytest pytest tests -q ``` Ohne gesetzte Umgebungsvariablen legt die Anwendung ihre Daten unter `instance/tesm-license/` im Arbeitsverzeichnis an -- kein Zugriff auf `/srv` oder `/var/log` noetig. ```bash TESM_LICENSE_DEBUG=1 flask --app tesm_license run ``` --- ## Verwaltung von der Kommandozeile ```bash sudo -u tesm-license tesm-license-admin status sudo -u tesm-license tesm-license-admin init-key # Signaturschluessel sudo -u tesm-license tesm-license-admin bootstrap # Grunddaten sudo -u tesm-license tesm-license-admin create-admin sudo -u tesm-license tesm-license-admin migrate-legacy # Altbestand uebernehmen ``` `migrate-legacy` liest die SQLite-Datei eines alten Lizenzservers **nur lesend**, uebernimmt dessen Signaturschluessel und stellt die Lizenzen mit ihrer restlichen Laufzeit neu aus. Wird der Schluessel nicht uebernommen, muessen Kunden den Ausstellerwechsel einmal bestaetigen. --- ## Lizenzprotokoll in Kurzform * Ed25519, getrennte Signaturkontexte je Nachrichtenart (`tesm/license-file/v2\0`) -- eine Signatur aus einem Kontext ist in einem anderen nicht verwendbar. * Kanonisches JSON, damit dieselbe Aussage genau eine Signatur hat. * Nonce gegen Wiedereinspielung, `seq` gegen das Zurueckrollen auf eine aeltere Lizenz. * Aussteller wird beim ersten Kontakt gemerkt (TOFU); ein Wechsel muss ausdruecklich bestaetigt werden. * Der Fingerabdruck der Maschine enthaelt **keinen** Hostnamen -- Umbenennen brach im Vorgaenger die Bindung. * Offline-Aktivierung ueber einen kurzen Code in beide Richtungen; die Lizenz bleibt auf dem Server als "Aktivierung offen" markiert, bis sie zurueckkommt. --- ## 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 zwei Skripte, die **auf dem Zielhost gegen laufende Installationen** sprechen -- ueber nginx, gunicorn und echtes TLS. Sie werden nicht mitinstalliert (`install.sh` laesst `tests/` aus); bei Bedarf dorthin kopieren: ```bash python3 tests/e2e_license_server.py https://lizenz.host admin 'Passwort' python3 tests/e2e_license.py https://lizenz.host https://host admin 'Passwort' http://127.0.0.1:5001 ``` Das zweite spielt den vollstaendigen Lizenz-Lebenszyklus ueber **beide** Anwendungen durch: ausstellen, herunterladen, einspielen, online aktivieren, Module pruefen, Heartbeat, neue Revision, Widerruf. Es braucht deshalb auch eine laufende TESM-Installation. --- ## Was diese Fassung anders macht * **Keine doppelte `licensing.py`.** Ein Paket, das beide Anwendungen installieren -- statt zweier Dateien, die byte-identisch bleiben sollen. * **Kein root fuer die Anwendung.** Ein einziger allowlisted Helfer mit Argumentpruefung. * **CSRF-Schutz ueberhaupt.** Der Vorgaenger hatte keinen. * **Rechte werden serverseitig geprueft**, nicht nur im Template ausgeblendet. * **Keine Lizenz ohne Endpunkt.** Eine Ausstellung ohne `master_endpoint` wird abgelehnt statt eine Lizenz zu erzeugen, die sich nie aktivieren kann. * **Verschachtelte Datenbankverbindungen sind ein Fehler**, kein Deadlock.