Gegenbuch-Container: Rechte am Bind-Mount selbst geraderuecken

Fehlerbild aus dem echten Betrieb: "mkdir: cannot create directory
'/gegenbuch/schluessel': Permission denied", Container in der
Neustart-Schleife. Ursache: Das Datenverzeichnis kommt als Bind-Mount vom Host;
der Betreiber hatte das Projekt als root geklont, der Container lief aber
direkt als UID 1000 und durfte dort nichts anlegen. Der .gitkeep-Ansatz hatte
stillschweigend angenommen, dass als normaler Benutzer geklont wird - auf einem
Server ist root der Normalfall.

Fix: Der Container startet als root, setzt /gegenbuch per chown auf den
Arbeitsbenutzer (PUID/PGID, Standard 1000) und startet sich per setpriv als
dieser neu. Die eigentliche Arbeit laeuft weiterhin unprivilegiert. Schlaegt
das chown fehl (rootless Docker), gibt es einen Hinweis mit dem passenden
Host-Befehl statt eines stummen Abbruchs.

Verifiziert mit exakt der Ausgangslage: Datenverzeichnis auf root:root gesetzt,
Container gestartet -> laeuft durch, erzeugte Dateien gehoeren 1000:1000, der
private Schluessel liegt mit 0600.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-22 19:51:10 +02:00
co-authored by Claude Opus 5
parent 1eb65809ec
commit 601fb03b22
4 changed files with 49 additions and 4 deletions
+18
View File
@@ -97,6 +97,24 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
## ✅ Erledigt ## ✅ Erledigt
- [x] **🔧 Gegenbuch-Container: Rechte am Bind-Mount selbst geraderuecken** (2026-08-22)
- Fehlerbild aus dem echten Betrieb: `mkdir: cannot create directory
'/gegenbuch/schluessel': Permission denied`, Container in der
Neustart-Schleife. Ursache: Das Datenverzeichnis kommt als Bind-Mount vom
Host; der Betreiber hatte das Projekt als **root** geklont, der Container
lief aber direkt als UID 1000 und durfte dort nichts anlegen.
Mein `.gitkeep`-Ansatz hatte stillschweigend angenommen, dass als normaler
Benutzer geklont wird auf einem Server ist root der Normalfall.
- Fix: Der Container startet als root, setzt `/gegenbuch` per `chown` auf den
Arbeitsbenutzer (`PUID`/`PGID`, Standard 1000) und startet sich per
`setpriv` als dieser neu. Die eigentliche Arbeit laeuft damit weiterhin
unprivilegiert. Schlaegt das `chown` fehl (z. B. rootless Docker), gibt es
einen Hinweis mit dem passenden Host-Befehl statt eines stummen Abbruchs.
- Verifiziert mit exakt der Ausgangslage: Datenverzeichnis auf `root:root`
gesetzt, Container gestartet → laeuft durch, Schluessel und Buch werden
angelegt, erzeugte Dateien gehoeren 1000:1000, der private Schluessel liegt
mit `0600`.
- [x] **🔑 Gegenbuch: Dienstkonto statt Token, .env mit gueltigen Werten** (2026-08-22) - [x] **🔑 Gegenbuch: Dienstkonto statt Token, .env mit gueltigen Werten** (2026-08-22)
- **Blocker gefunden und behoben:** Ich hatte ein dauerhaftes API-Token - **Blocker gefunden und behoben:** Ich hatte ein dauerhaftes API-Token
vorausgesetzt das gibt es in OpenCRM gar nicht. Zugangstoken leben vorausgesetzt das gibt es in OpenCRM gar nicht. Zugangstoken leben
+7 -3
View File
@@ -15,9 +15,13 @@ COPY notary.mjs /opt/notary/notary.mjs
COPY entrypoint.sh /opt/notary/entrypoint.sh COPY entrypoint.sh /opt/notary/entrypoint.sh
RUN chmod +x /opt/notary/entrypoint.sh RUN chmod +x /opt/notary/entrypoint.sh
# Nicht als root laufen. Das Node-Image bringt bereits einen Benutzer mit # Der Container STARTET als root aber nur, um die Rechte auf dem
# UID 1000 mit ("node") der passt zu Bind-Mounts unter ./data/. # Datenverzeichnis geradezuziehen. Danach gibt der entrypoint die Privilegien
USER node # ab und arbeitet als unprivilegierter Benutzer weiter.
#
# Grund: Das Datenverzeichnis kommt als Bind-Mount vom Host. Wer das Projekt
# als root geklont hat, hat dort root-eigene Verzeichnisse ein Container, der
# direkt als UID 1000 startet, kann darin nichts anlegen.
WORKDIR /gegenbuch WORKDIR /gegenbuch
ENTRYPOINT ["/opt/notary/entrypoint.sh"] ENTRYPOINT ["/opt/notary/entrypoint.sh"]
+8 -1
View File
@@ -113,7 +113,14 @@ tools/audit-notary/data/prod/
``` ```
Der Inhalt ist vom Repository ausgenommen der Signaturschlüssel gehört dort Der Inhalt ist vom Repository ausgenommen der Signaturschlüssel gehört dort
nicht hinein. Die Verzeichnisse selbst sind über `.gitkeep` vorhanden, damit nicht hinein.
**Zu Dateirechten:** Der Container startet kurz als `root`, setzt das
Datenverzeichnis auf den Arbeitsbenutzer um und gibt die Privilegien dann ab.
Das ist nötig, weil das Verzeichnis vom Host kommt wer das Projekt als `root`
geklont hat, hätte sonst ein Verzeichnis, in das der Container nicht schreiben
darf. Passiert automatisch, du musst nichts tun. Ein anderer Zielbenutzer geht
über `PUID`/`PGID`. Die Verzeichnisse selbst sind über `.gitkeep` vorhanden, damit
sie nach einem `git clone` schon existieren und Docker sie nicht als `root` sie nach einem `git clone` schon existieren und Docker sie nicht als `root`
anlegt. anlegt.
+16
View File
@@ -14,6 +14,22 @@ export NOTARY_STATE_FILE=/gegenbuch/beobachtungen.jsonl
export NOTARY_ALLOW_LOCAL=true # Buch liegt auf dieser Maschine export NOTARY_ALLOW_LOCAL=true # Buch liegt auf dieser Maschine
export NOTARY_PUSH=false # es gibt kein Ziel zum Pushen export NOTARY_PUSH=false # es gibt kein Ziel zum Pushen
# --- Rechte geraderuecken, dann Privilegien abgeben ------------------------
# Laeuft der Container als root, gehoert das Bind-Mount-Verzeichnis vermutlich
# ebenfalls root (typisch, wenn das Projekt als root geklont wurde). Wir setzen
# es auf den Arbeitsbenutzer um und starten uns selbst als dieser neu.
BENUTZER_UID="${PUID:-1000}"
BENUTZER_GID="${PGID:-1000}"
if [ "$(id -u)" = "0" ]; then
if ! chown -R "$BENUTZER_UID:$BENUTZER_GID" /gegenbuch 2>/dev/null; then
echo "[$INSTANZ] HINWEIS: Rechte auf /gegenbuch liessen sich nicht setzen." >&2
echo "[$INSTANZ] Falls es gleich an fehlenden Schreibrechten scheitert, auf dem Host:" >&2
echo "[$INSTANZ] chown -R $BENUTZER_UID:$BENUTZER_GID <datenverzeichnis>" >&2
fi
exec setpriv --reuid="$BENUTZER_UID" --regid="$BENUTZER_GID" --init-groups \
--inh-caps=-all "$0" "$@"
fi
echo "[$INSTANZ] Gegenbuch startet Quelle: $CRM_URL, Takt: ${INTERVALL}s" echo "[$INSTANZ] Gegenbuch startet Quelle: $CRM_URL, Takt: ${INTERVALL}s"
# --- Signaturschlüssel: einmalig auf DIESER Maschine erzeugen --------------- # --- Signaturschlüssel: einmalig auf DIESER Maschine erzeugen ---------------