From 425fd76bc1d023440b7ab75210db4edc96abf410 Mon Sep 17 00:00:00 2001 From: Nick Szaunig Date: Thu, 17 Sep 2026 06:59:03 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20fsGroup=20wirkt=20bei=20SMB/NFS=20nicht?= =?UTF-8?q?=20=E2=80=94=20Rechte=20gehoeren=20in=20die=20mountOptions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der Kommentar am fsGroup-Feld behauptete, Kubernetes ordne damit JEDES gemountete Volume der Gruppe 1001 zu. Das stimmt nur fuer blockbasierten Speicher. Bei SMB/CIFS und NFS wird fsGroup ignoriert: diese Protokolle kennen keinen per-Datei-Unix-Besitz, den der Knoten nachtraeglich aendern koennte. Ohne uid/gid in den mountOptions erscheint dort alles als root:root, und der als uid 1001 laufende Container scheitert bei jedem Upload mit EACCES — also genau die Meldung "Die Datei konnte nicht gespeichert werden", ohne dass am Deployment etwas falsch aussieht. k8s/README.md bekommt dafuer einen eigenen Abschnitt: Diagnose im laufenden Pod, die noetigen mountOptions (uid=1001, gid=1001 zusaetzlich zu dir_mode/file_mode) und der Hinweis, dass Aenderungen daran erst beim naechsten Einhaengen greifen, ein Rollout-Restart also dazugehoert. Ebenfalls vermerkt: noperm umgeht die Rechtepruefung, statt den Besitz richtigzustellen — es ersetzt uid/gid nicht. Co-Authored-By: Claude Opus 5 (1M context) --- k8s/05-app-deployment.yaml | 19 ++++++++++- k8s/README.md | 67 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/k8s/05-app-deployment.yaml b/k8s/05-app-deployment.yaml index 29ee5a5..88573d9 100644 --- a/k8s/05-app-deployment.yaml +++ b/k8s/05-app-deployment.yaml @@ -51,8 +51,25 @@ spec: # Image bereits per chown gesetzten Besitzer von /app/uploads # vollstaendig, und der nicht-root laufende Container koennte trotzdem # keine Datei dort anlegen (EACCES bei jedem Admin-Upload). fsGroup - # sorgt dafuer, dass Kubernetes JEDES gemountete Volume zusaetzlich der + # sorgt dafuer, dass Kubernetes das gemountete Volume zusaetzlich der # Gruppe 1001 zuordnet und gruppen-schreibbar macht. + # + # ACHTUNG, GILT NICHT FUER JEDES BACKEND: fsGroup wirkt nur dort, wo + # Kubernetes den Besitz tatsaechlich setzen kann — also bei + # blockbasiertem Speicher. Bei SMB/CIFS und NFS wird es IGNORIERT, weil + # diese Protokolle keinen per-Datei-Unix-Besitz kennen, den der Knoten + # nachtraeglich aendern koennte. Dort gehoert die Zuordnung in die + # mountOptions des PersistentVolume: + # + # mountOptions: + # - uid=1001 + # - gid=1001 + # - dir_mode=0770 + # - file_mode=0660 + # + # Ohne uid/gid erscheint bei SMB ALLES als root:root, und der Container + # scheitert trotz fsGroup mit EACCES. Siehe k8s/README.md, Abschnitt + # "Uploads lassen sich nicht speichern". securityContext: fsGroup: 1001 containers: diff --git a/k8s/README.md b/k8s/README.md index be226ab..f7fab7a 100644 --- a/k8s/README.md +++ b/k8s/README.md @@ -221,6 +221,73 @@ Speicher- und CPU-Bedarf nicht mehr von der Größe des Knotens abhängt — sie erfolgreicher Upload beim nächsten Neustart trotzdem weg. Siehe „Bilder/Videos verschwinden nach jedem Redeploy" oben. +## Uploads lassen sich nicht speichern + +Symptom: Im Admin erscheint „Die Datei konnte nicht gespeichert werden. Bitte +informiere die Technik." Im Pod-Log steht dahinter ein `EACCES`. + +Ursache ist fast immer der Besitz des gemounteten Volumes. Der Container läuft +als **`nextjs`, uid 1001, Gruppe `nodejs` gid 1001** (siehe `Dockerfile`). Das +Image setzt zwar `chown nextjs:nodejs /app/uploads`, aber ein darübergelegtes +Volume überschreibt das vollständig. + +Zuerst feststellen, was der Container tatsächlich sieht: + +```bash +kubectl -n wabenhain exec deploy/wabenhain-app -- sh -c \ + 'id; ls -ld /app/uploads /app/invoices; \ + touch /app/uploads/.probe && echo SCHREIBEN_OK || echo SCHREIBEN_FEHLT' +``` + +Der Fix hängt am Speicher-Backend — die beiden Wege gelten für verschiedene +Fälle, nicht alternativ: + +### Blockspeicher (die meisten CSI-Treiber) + +`securityContext.fsGroup: 1001` im Deployment. Kubernetes ordnet das Volume +dann der Gruppe 1001 zu und macht es gruppenschreibbar. Steht bereits in +`05-app-deployment.yaml`. + +### SMB/CIFS und NFS + +**Hier wird `fsGroup` ignoriert.** Diese Protokolle kennen keinen +per-Datei-Unix-Besitz, den der Knoten nachträglich ändern könnte; ohne +Zuordnung erscheint alles als `root:root`, und der Container scheitert trotz +`fsGroup` mit `EACCES`. Die Zuordnung gehört in die `mountOptions` des +PersistentVolume: + +```yaml +spec: + mountOptions: + - vers=3.0 + - uid=1001 # muss zum Containerbenutzer passen + - gid=1001 + - dir_mode=0770 + - file_mode=0660 + - mfsymlinks + - nobrl # keine Byte-Range-Locks über SMB +``` + +`dir_mode`/`file_mode` allein genügen nicht: sie bestimmen nur, welche Rechte +angezeigt werden — wem die Datei gehört, entscheiden `uid`/`gid`. + +`noperm` (der Client prüft Rechte gar nicht erst) wirkt in vielen Aufbauten +ebenfalls, ist aber die schwächere Zusicherung: es umgeht die Prüfung, statt +den Besitz richtigzustellen. `uid`/`gid` also auch dann setzen, wenn `noperm` +bereits gesetzt ist. + +**Änderungen an `mountOptions` greifen erst beim nächsten Einhängen.** Nach dem +Anpassen des PV den Pod neu starten: + +```bash +kubectl -n wabenhain rollout restart deploy/wabenhain-app +``` + +Lässt sich das PV nicht mehr ändern (bei bereits gebundenen PVs je nach +Kubernetes-Version), PVC und PV löschen und neu anlegen. Bei +`persistentVolumeReclaimPolicy: Retain` bleiben die Daten auf der Freigabe +erhalten. + ## Sicherung und Wiederherstellung `07-backup-cronjob.yaml` legt jede Nacht um 03:15 Uhr eine