/releases/ wurde bisher offen ausgeliefert, damit ausgelieferte Anwendungen
ohne Zugangsdaten nach Updates suchen koennen. Das bedeutete aber auch, dass
jeder im Internet die vollstaendigen Pakete herunterladen konnte - mitsamt
allem, was versehentlich darin liegt. Genau so lag ein echter API-Schluessel
in einer mitgelieferten appsettings.json oeffentlich abrufbar.
Zugang haengt jetzt am Lizenzschluessel: Wer eine gueltige Lizenz fuer ein
Produkt hat, kommt an dessen Updates. Die Anwendung kennt ihren Schluessel
ohnehin und versorgt sich damit selbst - es muss nichts verteilt werden.
Server
- ReleaseGuard erzeugt je Produktverzeichnis .htaccess und .htpasswd.
Bewusst getrennt: eine gemeinsame Datei wuerde bedeuten, dass eine Lizenz
fuer Produkt A auch Produkt B oeffnet. license_licenses.product_id bindet
jeden Schluessel ohnehin an genau ein Projekt.
- Eingetragen werden aktive, nicht abgelaufene Lizenzen (Benutzername =
Passwort = Schluessel; Basic Auth braucht zwei Felder, es gibt aber nur ein
Geheimnis) sowie alle Installationskonten - bei einer Erstinstallation gibt
es noch keinen Schluessel, mit dem sich das Paket holen liesse.
- Deren Hash wird unveraendert aus dc_users uebernommen: password_hash()
erzeugt bcrypt im Format $2y$, genau das versteht Apache. Ein
Klartextpasswort wird nirgends gebraucht. Argon2-Hashes werden erkannt und
uebersprungen statt eine unbrauchbare Datei zu erzeugen.
- Lizenzschluessel werden mit Kosten 8 gehasht statt 12: 29 Zeichen
maschineller Zufall sind kein Menschenpasswort, Apache prueft aber bei
*jeder* Anfrage neu.
- Geschrieben wird ueber eine temporaere Datei mit rename() - ein Abbruch
wuerde sonst eine halbe Zugangsdatei hinterlassen und in dem Moment die
halbe Kundschaft aussperren.
- Neu erzeugt bei jeder Lizenz- und Kontoaenderung. Abgelaufene Lizenzen
loesen anders als ein Widerruf nichts aus; dafuer gleicht cli/tick.php nach
und erzeugt spaetestens alle sechs Stunden neu.
- Statusanzeige und Schaltflaeche im WebUI unter UpdateService.
Client
- ReleaseCredentials: Lizenzschluessel oder Installationskonto als Basic Auth.
- UpdateClient und Agent senden sie fuer latest.json und package.tar.gz.
- UpdateCheckResult.Unauthorized trennt "Lizenz traegt nicht mehr" von einem
Netzwerkfehler. Ohne diese Unterscheidung sucht man an der falschen Stelle.
- LaunchUpdateAgent reicht licenseKey als --license-key durch.
- Der Installer benutzt die beim Anmelden eingegebenen Zugangsdaten auch fuer
den Paketabruf; das Setup-Token taugt dafuer nicht, weil Apache prueft und
nicht die Anwendung.
Sonstiges
- deploy.py klammert artifacts/ aus. Ohne das landeten die gebauten
Installer-Binaries zusaetzlich unter /artifacts/ im Webroot.
ACHTUNG Reihenfolge: Der Schutz sperrt jede Anwendung aus, die noch mit dem
alten SDK gebaut ist. Erst ausliefern, dann scharfschalten - siehe
UPGRADE.md §16.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>