umask und Dateirechte in Cloud-Mac-CI beherrschen

umask und Dateirechte in Cloud-Mac-CI beherrschen

Wenn dasselbe Build-Skript in einer SSH-Sitzung problemlos läuft, in einer Cloud-Mac-CI jedoch mit Permission denied abbricht, lautet die häufigste Fehldiagnose: „Ein weiteres chmod +x wird das Problem lösen.“ Die eigentliche Ursache erstreckt sich meist über vier Ebenen: die umask am Einstiegspunkt des Jobs, das im Git-Index gespeicherte Ausführungsbit, die Berechtigungssemantik der Kopier- und Entpackwerkzeuge sowie den tatsächlich vom Skript verwendeten Interpreter. Wird nur eine Datei im Arbeitsverzeichnis korrigiert, tritt der Fehler beim nächsten sauberen Checkout erneut auf.

Berechtigungsfehler zunächst in vier Kategorien einteilen

Vor der Fehlersuche sollten das betroffene Objekt und die Art der fehlgeschlagenen Operation erfasst werden. Ein nicht ausführbares Skript, ein nicht beschreibbares Verzeichnis, zu weit gefasste Schlüsselberechtigungen und nach dem Entpacken veränderte Dateimodi erfordern jeweils völlig unterschiedliche Maßnahmen.

Mit stat lassen sich der oktale Modus, der Eigentümer und der Dateityp gemeinsam prüfen:

target="${1:?path required}"
stat -f 'mode=%Sp octal=%OLp owner=%Su group=%Sg type=%HT path=%N' "$target"
ls -ldeO@ "$target"

Der zweite Befehl macht außerdem ACLs, Datei-Flags und erweiterte Attribute sichtbar. Wenn die normalen Berechtigungen korrekt erscheinen, der Vorgang aber weiterhin fehlschlägt, sollten die Rechte nicht sofort erhöht werden. Zuerst ist zu prüfen, ob die übergeordneten Verzeichnisse durchlaufen werden können, ob das Volume die Ausführung erlaubt und unter welchem Benutzerkonto der Prozess läuft.

rwx ist nicht das einzige Kriterium. Jede Verzeichnisebene des Pfads benötigt die entsprechenden Berechtigungen, und ACLs können die normalen Modusbits übersteuern. Der fortgesetzte Einsatz von sudo führt lediglich zu einem Arbeitsverzeichnis mit gemischten Eigentümern und erschwert die Reproduzierbarkeit nachfolgender Jobs.

Mindestumfang der Diagnoseinformationen erfassen

Mindestens id, umask, das aktuelle Verzeichnis, die stat-Ausgabe der Zieldatei und die Mount-Informationen sollten gespeichert werden. Es ist davon abzuraten, sämtliche Umgebungsvariablen zu protokollieren, da sie Token enthalten können. Die Diagnose sollte sich auf Identität, Pfade und Berechtigungen konzentrieren und keine Zugangsdaten erfassen.

umask am Einstiegspunkt des Jobs festlegen

umask wirkt sich nur auf neu erstellte Objekte aus und ändert keine vorhandenen Dateien rückwirkend. Mit dem üblichen Basiswert 022 erhalten normale Dateien standardmäßig den Modus 644 und Verzeichnisse 755. Für kontrollierte, gemeinsam von einer Gruppe verwendete Verzeichnisse kann 002 in Betracht gezogen werden, sollte aber nicht als universelle Lösung dienen. Der Wert sollte am Einstiegspunkt des Jobs ausdrücklich gesetzt und durch das Erstellen von Prüfobjekten verifiziert werden:

set -euo pipefail
umask 022

probe_dir="$(mktemp -d)"
probe_file="$probe_dir/probe"
: > "$probe_file"

file_mode="$(stat -f '%OLp' "$probe_file")"
dir_mode="$(stat -f '%OLp' "$probe_dir")"

test "$file_mode" = "644"
test "$dir_mode" = "700"
rm -rf "$probe_dir"

Zum Schutz temporärer Inhalte erstellt mktemp -d normalerweise ein Verzeichnis mit dem Modus 700. Daraus lässt sich daher nicht ableiten, dass normale Verzeichnisse den Modus 755 haben sollten. Um den Basiswert für Verzeichnisse zu testen, muss in einem bekannten übergeordneten Verzeichnis mkdir ausgeführt und das Ergebnis anschließend separat geprüft werden. Dieser Unterschied ist auch ein häufiger Grund für Fehlalarme in Berechtigungstests.

Die korrekte Ausführungsabsicht in Git speichern

Git erfasst hauptsächlich, ob eine Datei ausführbar ist, speichert aber nicht alle Unix-Berechtigungen vollständig. Wenn ein Skript auf einem bestimmten Rechner manuell auf 755 gesetzt wurde, bedeutet das nicht, dass diese Änderung bereits im Index vermerkt ist. Deshalb sollte nicht nur das Arbeitsverzeichnis, sondern der Index geprüft werden:

git ls-files --stage |
awk '$1 == "100755" {print $4}' |
sort

Soll das Ausführungsbit eingecheckt werden, sind folgende Befehle zu verwenden:

git update-index --chmod=+x scripts/build.sh
git diff --summary
git diff --cached --summary

Umgekehrt sollten Konfigurationsdateien oder Dokumente, die versehentlich ein Ausführungsbit tragen, mit git update-index --chmod=-x korrigiert werden. Bei .sh-Dateien ist außerdem die erste Zeile zu prüfen. Empfehlenswert ist ein Interpreter, der in der jeweiligen Umgebung nachweislich vorhanden ist, beispielsweise #!/bin/zsh oder #!/usr/bin/env bash. Zudem sollten Skripte keine CRLF-Zeilenenden enthalten.

Skripte vor dem Zusammenführen kontrollieren

Eine explizite Liste ausführbarer Skripte kann gepflegt und mit dem Ergebnis aus dem Index verglichen werden. Eine pauschale Regel wie „Alle .sh-Dateien müssen ausführbar sein“ ist ungeeignet, da Bibliotheksdateien, die per source eingebunden werden, nicht zwingend ein Ausführungsbit benötigen. Regeln sollten den Verwendungszweck abbilden, statt ihn anhand der Dateierweiterung zu erraten.

Berechtigungssemantik beim Kopieren, Archivieren und Entpacken kontrollieren

cp, ditto, rsync und unterschiedliche Archivformate behandeln Modusbits, ACLs und erweiterte Attribute nicht einheitlich. Build-Caches sollten nur Daten enthalten, die erneut erzeugt werden können. Auslieferungspakete müssen dagegen nach dem Entpacken erneut auf korrekte Berechtigungen geprüft werden.

Vor dem Kopieren eines Arbeitsverzeichnisses ist zu entscheiden, ob Metadaten erhalten bleiben müssen. Werden nur die Inhalte der Quelldateien benötigt, sollte eine unbeabsichtigte Übernahme alter Eigentümer, ACLs oder erweiterter Attribute vermieden werden. Muss das Ausführungsbit erhalten bleiben, empfiehlt sich ein Rundlauftest in einem temporären Verzeichnis:

src="scripts/build.sh"
tmp="$(mktemp -d)"
ditto "$src" "$tmp/build.sh"

before="$(stat -f '%OLp' "$src")"
after="$(stat -f '%OLp' "$tmp/build.sh")"
test "$before" = "$after"

rm -rf "$tmp"

Bei der Abnahme eines Archivs reicht es nicht aus, lediglich die Existenz der Dateien zu prüfen. Mindestens ist sicherzustellen, dass das Einstiegsskript ausführbar ist, normale Konfigurationsdateien nicht ausführbar sind und sensible Dateien weder von der Gruppe noch von anderen Benutzern gelesen werden können. Das Zielverzeichnis für das Entpacken sollte vollständig neu sein, damit die Modi vorhandener Dateien keine Probleme des Archivs selbst verdecken.

Korrekturen in eine ausführbare Richtlinie überführen

Eine stabile Lösung besteht nicht darin, nach einem Fehler pauschal chmod -R 777 auszuführen. Stattdessen sollten wenige, nachvollziehbare Zusicherungen definiert werden. Die folgende Prüfung weist Dateien zurück, die von anderen Benutzern beschrieben werden können, und stellt sicher, dass das Einstiegsskript ausführbar ist:

set -euo pipefail

entry="scripts/build.sh"
test -f "$entry"
test -x "$entry"

while IFS= read -r -d '' file; do
  mode="$(stat -f '%OLp' "$file")"
  other_write=$((8#$mode & 2))
  if (( other_write != 0 )); then
    printf 'world-writable file: %s mode=%s\n' "$file" "$mode" >&2
    exit 1
  fi
done < <(find . -type f -not -path './.git/*' -print0)

Das Richtlinienskript sollte im Repository liegen und sowohl für lokale Vorabprüfungen als auch in der CI verwendet werden. Fehlermeldungen sollten ausschließlich Pfad, erwarteten Modus und tatsächlichen Modus enthalten. So bleibt die Ursache leicht auffindbar, ohne Dateiinhalte offenzulegen. Auch bei Jobs auf dedizierten physischen Knoten von RentMini müssen diese Zusicherungen in jedem neuen Arbeitsverzeichnis erneut geprüft werden. Es darf nicht davon ausgegangen werden, dass die Umgebung eines vorherigen Durchlaufs unverändert erhalten bleibt.

Abschließend ist ein sauberer Checkout zu testen: Das temporäre Arbeitsverzeichnis wird gelöscht, das Repository neu geklont und anschließend werden ohne manuelle Korrekturen direkt die Berechtigungsprüfung und ein minimaler Build ausgeführt. Erst wenn dieser Ablauf erfolgreich ist, wurde die Berechtigungskorrektur tatsächlich in die Versionsverwaltung übernommen und verbleibt nicht nur in einer einzelnen Remote-Sitzung.

Häufig gestellte Fragen

Warum meldet CI trotz chmod +x weiterhin Permission denied?

Prüfen Sie den tatsächlich ausgeführten Pfad, die Ausführungsoptionen des Volumes, den Interpreter aus der Shebang-Zeile und spätere Kopier- oder Entpackschritte, die das Ausführungsbit erneut entfernen können.

Welche umask eignet sich für Cloud-Mac-CI?

022 ist für viele Build-Aufträge eines einzelnen Benutzers eine sinnvolle Basis. 002 sollte kontrollierten, gemeinsam beschreibbaren Arbeitsbereichen vorbehalten bleiben; entscheidend sind die resultierenden Dateimodi.

Wie lässt sich Rechte-Drift vor dem Merge verhindern?

Prüfen Sie Git-Ausführungsbits, Shebang-Zeilen, sensible Dateien und entpackte Artefakte. Bei jeder Abweichung muss der CI-Schritt mit einem Fehlerstatus enden.

Cloud-Mac nach Bedarf nutzen

Exklusiven physischen Mac mini für den nächsten Build vorbereiten

Wählen Sie Konfiguration, Mietdauer und Standort. Jede Bestellung umfasst ein exklusives physisches Gerät. Der tatsächlich verfügbare Status wird in Echtzeit über die Konsole angezeigt.

Konfiguration auswählen und mieten