Files
tesm-license/README.md
T
alientimandClaude Opus 5 f7805a2180 TESM-Lizenzserver 2.0.0 -- Neubau
Vollstaendiger Neubau der Anwendung. Der vorherige Stand bleibt unveraendert
im Zweig SONNET5 erhalten.

Aufbau: apps/tesm-license (Anwendung), packages/tesm-core (gemeinsamer Kern),
packages/tesm-licensing (Lizenzprotokoll), deploy (Installation, systemd,
privilegierter Helfer), docs, tests. Die verwaltete Anwendung liegt in ihrem
eigenen Repository; beide Repositorien bringen die gemeinsamen Pakete mit,
damit sich jedes allein installieren laesst.

Die wichtigsten Unterschiede zum Vorgaenger:

* Keine doppelte licensing.py -- ein Paket, das beide Anwendungen
  installieren, statt zweier Dateien, die byte-identisch bleiben sollen.
* 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.
* Keine Lizenz ohne master_endpoint: eine Ausstellung ohne Endpunkt wird
  abgelehnt statt eine Lizenz zu erzeugen, die sich nie aktivieren kann.
* Offline-Aktivierung in beide Richtungen; die Lizenz bleibt als
  "Aktivierung offen" markiert, bis sie zurueckkommt.
* Getrennte Signaturkontexte je Nachrichtenart, Nonce gegen Wiedereinspielung,
  seq gegen das Zurueckrollen auf eine aeltere Lizenz.
* Kein Hostname im Maschinen-Fingerabdruck.
* Verschachtelte Datenbankverbindungen sind ein Fehler, kein Deadlock.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 18:01:23 +02:00

5.5 KiB

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. 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

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

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

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

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.

TESM_LICENSE_DEBUG=1 flask --app tesm_license run

Verwaltung von der Kommandozeile

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 Aufbau, Schnitte und die Gruende dafuer
docs/SICHERHEIT.md Anmeldung, Sitzungen, CSRF, Geheimnisse, Rechtetrennung
docs/BETRIEB.md Installation, Update, HTTPS, Sicherung, Fehlersuche

Tests

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:

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.