Vollstaendiger Neubau der Anwendung. Der vorherige Stand bleibt unveraendert im Zweig SONNET5 erhalten. Aufbau: apps/tesm (Anwendung), packages/tesm-core (gemeinsamer Kern), packages/tesm-licensing (Lizenzprotokoll), deploy (Installation, systemd, privilegierter Helfer), docs, tests. Der Lizenzserver liegt in seinem eigenen Repository; beide Repositorien bringen die gemeinsamen Pakete mit, damit sich jedes allein installieren laesst. Die wichtigsten Unterschiede zum Vorgaenger, jeweils an der Stelle im Code kommentiert, an der der Fehler entstanden ist: * 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. * Die nginx-Site wird bei jedem Lauf inhaltlich verglichen und erneuert. * Jede erzeugte Konfiguration wird vor dem Uebernehmen geprueft (nginx, Kea). * Kein Hostname im Lizenz-Fingerabdruck. * Zwei Installationen auf einem Host stoeren sich nicht (eigener SITE_KEY). * Verschachtelte Datenbankverbindungen sind ein Fehler, kein Deadlock. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
63 lines
2.7 KiB
Desktop File
63 lines
2.7 KiB
Desktop File
[Unit]
|
|
Description=TESM -- Ueberwachung der PoE-Endgeraete
|
|
After=network-online.target tesm.service
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=tesm
|
|
Group=tesm
|
|
WorkingDirectory=/srv/tesm
|
|
Environment=TESM_BASE_DIR=/srv/tesm
|
|
Environment=TESM_DATA_DIR=/srv/tesm/data
|
|
Environment=TESM_LOG_DIR=/var/log/tesm
|
|
Environment=PYTHONUNBUFFERED=1
|
|
ExecStart=/srv/tesm/venv/bin/tesm-monitor
|
|
Restart=always
|
|
RestartSec=10
|
|
|
|
# Das Pruefintervall liest der Prozess bei jedem Durchlauf frisch aus der
|
|
# Datenbank -- der naechtliche Neustart-Timer des Vorgaengers entfaellt damit.
|
|
# 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
|
|
# 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
|
|
ProtectControlGroups=yes
|
|
RestrictSUIDSGID=yes
|
|
RestrictNamespaces=yes
|
|
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK
|
|
LockPersonality=yes
|
|
SystemCallArchitectures=native
|
|
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/data /var/log/tesm /etc/tesm /run
|
|
# ping braucht CAP_NET_RAW -- als einzige Ausnahme, und nur diese.
|
|
AmbientCapabilities=CAP_NET_RAW
|
|
# 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.
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|