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>
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,
seqgegen 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_endpointwird abgelehnt statt eine Lizenz zu erzeugen, die sich nie aktivieren kann. - Verschachtelte Datenbankverbindungen sind ein Fehler, kein Deadlock.