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>
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
[Unit]
|
||||
Description=TESM-Lizenzserver
|
||||
Documentation=file:/srv/tesm-license/docs/BETRIEB.md
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=notify-reload
|
||||
User=tesm-license
|
||||
Group=tesm-license
|
||||
WorkingDirectory=/srv/tesm-license
|
||||
Environment=TESM_LICENSE_BASE_DIR=/srv/tesm-license
|
||||
Environment=TESM_LICENSE_DATA_DIR=/srv/tesm-license/data
|
||||
Environment=TESM_LICENSE_LOG_DIR=/var/log/tesm-license
|
||||
Environment=TESM_LICENSE_WEB_PROCESS=1
|
||||
Environment=TESM_LICENSE_COOKIE_SECURE=1
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
ExecStart=/srv/tesm-license/venv/bin/gunicorn \
|
||||
--workers 1 --worker-class gthread --threads 8 \
|
||||
--bind 127.0.0.1:5001 \
|
||||
--access-logfile - --error-logfile - \
|
||||
tesm_license.wsgi:app
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
# -- Haertung ---------------------------------------------------------------
|
||||
# Der Dienst braucht kein Root, keine neuen Privilegien und keinen Zugriff auf
|
||||
# das System ausserhalb seiner eigenen Verzeichnisse. Was er trotzdem braucht
|
||||
# (systemctl, nginx, netplan), laeuft ueber den Helfer und sudo.
|
||||
# Bewusst **kein** NoNewPrivileges: der Dienst ruft den privilegierten Helfer
|
||||
# ueber sudo auf, und sudo ist setuid. Unter NoNewPrivileges verweigert der
|
||||
# Kernel die Rechteerhoehung, sudo bricht ab -- und damit jede Systemaktion aus
|
||||
# der Oberflaeche. Die Sicherheitsgrenze liegt nicht hier, sondern in der
|
||||
# sudoers-Regel (genau ein erlaubter Pfad) und im Helfer selbst (jedes Argument
|
||||
# gegen eine Positivliste). Siehe docs/SICHERHEIT.md.
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
# ProtectSystem=full statt strict: "strict" sperrt den **ganzen Prozessbaum**
|
||||
# schreibgeschuetzt -- auch den Helfer, der als root gerade /etc/nginx,
|
||||
# /etc/netplan und /etc/kea schreiben soll. Gemessen: unter "strict" scheitert
|
||||
# schon "nginx -t". Mit "full" bleiben /usr und /boot schreibgeschuetzt, /etc
|
||||
# ist im Namensraum wieder erreichbar -- der unprivilegierte Dienst kommt
|
||||
# trotzdem nicht heran, denn die Dateien gehoeren root. Die Grenze ist damit
|
||||
# die gewoehnliche Dateiberechtigung plus die sudoers-Regel, nicht dieser
|
||||
# Schalter.
|
||||
ProtectSystem=full
|
||||
ProtectHome=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelLogs=yes
|
||||
ProtectControlGroups=yes
|
||||
ProtectClock=yes
|
||||
ProtectHostname=yes
|
||||
ProtectProc=invisible
|
||||
RestrictSUIDSGID=yes
|
||||
RestrictRealtime=yes
|
||||
RestrictNamespaces=yes
|
||||
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK
|
||||
LockPersonality=yes
|
||||
MemoryDenyWriteExecute=no
|
||||
SystemCallArchitectures=native
|
||||
SystemCallFilter=@system-service
|
||||
SystemCallErrorNumber=EPERM
|
||||
# Bewusst **kein** einschraenkendes CapabilityBoundingSet: sudo braucht
|
||||
# CAP_SETUID/CAP_SETGID, und der Helfer dahinter braucht Root. Was hier fehlt,
|
||||
# kann auch der Helfer nicht mehr erlangen -- gemessen: jede Einschraenkung
|
||||
# fuehrt zu "sudo: unable to change to root gid". Die Grenze ist die
|
||||
# sudoers-Regel und die Argumentpruefung im Helfer, nicht dieser Schalter.
|
||||
UMask=0027
|
||||
# Genau die Pfade, die der privilegierte Helfer schreibt -- nicht mehr.
|
||||
# ProtectSystem=full sperrt /usr, /boot **und /etc**; ohne diese Freigaben
|
||||
# scheitert der Helfer mit "Read-only file system", obwohl er root ist
|
||||
# (gemessen). Der unprivilegierte Dienst kommt hier trotzdem nicht heran: die
|
||||
# Dateien gehoeren root, und sudo laesst nur den einen Helferpfad zu.
|
||||
ReadWritePaths=/srv/tesm-license/data /var/log/tesm-license /etc/nginx /etc/netplan /etc/logrotate.d /etc/tesm /run
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user