Probleme mit Datei-Berechtigungen bei Docker unter Linux umschiffen mit ACLs

Docker wurde für Linux entwickelt und läuft dort im Allgemeinen auch besser als auf anderen Betriebssystemen. Was aber immer wieder Probleme bereitet(e) waren die Berechtigungen für Dateien. Das Problem hat mich lange geärgert, aber ich denke nun eine gut funktionierende Lösung mit Linux ACLs gefunden zu haben.

Sowohl beruflich bei TEAM23 als auch privat entwickle ich Webseiten seit ein paar Jahren fast ausschliesslich in dockerisierten Umgebungen. Das hat sich gut bewährt: Alle Projekte sind so unabhängig vom Host-System und man kann z.B. verschiedenste PHP-Versionen in den Projekten nutzen. Wie so ein Setup in etwa aussieht, hat der geschätzte Kollege Manuel Christlieb ausführlich in einem mehretiiligen Blog Post beschrieben: Local development environment with b5, docker and traefik.

In der Agentur arbeiten (natürlich) alle auf Macs. Alle bis auf einen, der zufällig ich bin. Daher durfte ich mich dann auch öfter mit Berechtigungsproblemen herumschlagen, die es so auf Mac und Windows nicht gibt.

Unterschiede in Dateiberechtigungen Linux vs. Mac/Win

Da Docker auf dem Linux-Kernel und dessen Funktionen basiert, läuft es auf Mac und Windows innerhalb einer virtuellen Linux-Maschine, z.B. innerhalb von VirtualBox. Dabei wird auch ein Teil des lokalen Dateisystems in die virtuelle Maschine gemounted. Die Dateiberechtigungen werden dabei automatisch vom Treiber für das virtuelle Dateisystem (z.B. osxfs oder vboxfs) umgeschrieben, so dass der Benutzer immer Zugriff darauf hat, unabhängig davon, wer innerhalb des Containers darauf welche Rechte hat. Auf Linux dagegen werden durch den bind-mount die Dateiberechtigungen 1:1 durchgereicht.

Wir gehen im Weiteren von diesem Szenario aus:

  • Der eigene Benutzer heisst mariakaefer und hat die UID und GID 1000
  • Es gibt einen PHP-Container, in dem PHP als User www-data mit UID und GID 33 läuft.

Wird nun im Container eine Datei auf einem Volume erstellt, so sehen wir ausserhalb des Containers unterschiedliche Berechtigungen. Dazu erstellen wir mit dem PHP-Image als User www-data eine Datei in einem Volume, das mit dem Host geteilt wird:

mkdir -p ~/vol1/dir
chmod 777 ~/vol1/dir
docker run --rm -u 33 -v ~/vol1:/vol1 -it php:latest bash -c "touch /vol1/dir/file; ls -al /vol1/dir"; ls -al ~/vol1/dir
Datei im Container erstellen und Berechtigungen im und ausserhalb des Containers ansehen

Mac: Datei im Container erstellt mit UID 33

Im Container:

-rw-r--r-- 1 www-data www-data    0 Apr 25 07:34 file

Ausserhalb des Containers:

-rw-r--r-- 1 mariakaefer staff    0 Apr 25 09:34 file

Linux: Datei im Container erstellt mit UID 33

Im Container:

-rw-r--r-- 1 www-data www-data    0 Apr 25 07:34 file

Ausserhalb des Containers:

-rw-r--r-- 1 www-data www-data    0 Apr 25 09:34 file

Man sieht hier: Unter Linux werden wir als der Benutzer auf dem Host das file nicht schreiben dürfen, da es www-data gehört. Das gilt auch andersrum: Erstellen wir ale Benutzer auf dem Host ein File innerhalb eines Volumes, dann darf es der Benutzer im Container nicht schreiben.

Lösungsansätze

Wie lösen wir dieses Problem nun? Mehrere Lösungsansätze, die ich versucht oder zumindest in Erwägung gezogen habe:

  • "einfach als root arbeiten und im Container die Dienste als root laufen lassen" - bedarf wohl keiner weiteren Erklärung, warum das eine dumme Idee ist.
  • auf dem Host und im Container exzessiv chown/chmod nutzen und halt die Berechtigungen manuell anpassen, wie man sie gerade braucht. Oder eben ein Script dafür bauen. - Been there, done that. Auf die Dauer nervts aber, ständig irgendwo erst die rechte "fixen" zu müssen.
  • auf dem Host das Volume mit bindfs nochmal remounten und dabei die Berechtigungen anpassen. - ist recht komplex und hat hier nie in allen Einzelheiten funktioniert.
  • Anpassen der UID, unter welcher der Container bzw. ein Prozess darin läuft. Das erfordert in der Regel Anpassungen an allen Dockerfiles: U.a. muss im Container ein User mit der UID des Benutzers auf dem Host hinzugefügt werden und Prozesse als dieser User gestartet werden.
  • User namespaces, damit kann man wohl UID und GID remappen - Ist länger her, dass ich dasgetestet hab, aber erschien mir sehr komplex und war nur mit mässigem Erfolg gesegnet.
  • Berechtigungen mittels umask bzw. in der Anwendung entsprechend vergeben. Was fast funktionierte für das obige Beispiel: den User auf dem Host der Gruppe www-data hinzufügen und sicherstellen, dass auch die Gruppe immer Schreibberechtigungen auf ein File bekommt. So rum kann zumindest der Benutzer auf dem Host die files schreiben, die im PHP-Container angelegt wurden. - Es gab hier jedoch andersrum Probleme, also wenn ein File ausserhalb des Containers erstellt wurde. Dazu kommt, dass manche Operationen (z.B. touch mit modify des Zeitstempels) nur für den Owner eines Files, nicht aber die Gruppe erlaubt sind.
  • Finally: Linux ACL nutzen, siehe nächster Abschnitt - scheint mir gerade eine gute Lösung zu sein.

ACLs, da war doch was

Neben den üblichen Filesystem-Permissions gibt es in Linux ja auch noch die Access Control List (ACL), mit der sich die Berechtigungen noch feingranularer verwalten lassen. U.a. haben wir damit zwei wichtige Features zur Hand:

  • eine Datei/Verzeichnis kann mehrere Besitzer haben
  • für neu angelegte Dateien/Verzeichnisse können Default-ACL vergeben werden

 

# vol1 is the volume we are going to use in docker, dir is a directory inside it
DIR=~/vol1/dir
HOST_USER_ID="$(id -u)"
DOCKER_USER_ID="33"

mkdir -p "$DIR"

# default permissions: both the host user and the user with UID 33 (www-data on many systems) are owners with rwx perms
setfacl -dRm u:${DOCKER_USER_ID}:rwx,${HOST_USER_ID}:rwx "$DIR"
# permissions: make both the host user and the user with UID 33 owner with rwx perms for all existing files/directories
setfacl -Rm u:${DOCKER_USER_ID}:rwx,${HOST_USER_ID}:rwx "$DIR"

# create file on the host
touch "$DIR"/file

# write to the file in docker and create a new file file-in-docker
docker run --rm -u 33 -v "$(readlink -f ~/vol1)":/vol1 -it php:latest bash -c "touch /vol1/dir/file-in-docker; echo foo > /vol1/dir/file; ls -al /vol1/dir"
Demo command setfacl, zwei owner mit rwx permissions

Im obigen Beispiel wird erst ein Volume (~/vol1) erstellt. Mit setfacl werden die Default ACL gesetzt, die für das Verzeichnis dir1 darin gelten.

Relevante Optionen für setfacl:

  • -d Setzt Default ACL für ganze Verzeichnisse
  • -R rekursiv anwenden
  • -m modify

Anschliessend erstellen wir jeweils eine Datei, einmal ausserhalb mit dem Host-User, einmal innerhalb von docker als UID 33. In beiden Fällen werden beide Benutzer zu Besitzern der neuen Dateien und haben rwx-Rechte darauf.

Das setzen des executable Bits für Alles sollte man für ein Production-Setup sicher noch optimieren, aber mir ging es hier ja ohnehin nur um das lokale Development-Setup, und das funktioniert mit dieser Lösung nun tadellos und völlig ohne irgendwelche Berechtigungsprobleme.

That's it! Happy coding.

Kommentare (0)

Bisher keine Kommentare vorhanden.

Neuen Kommentar schreiben