Sobald ein Team externen Mitwirkenden erlaubt, Pull Requests einzureichen, geht es bei Cloud Mac CI nicht mehr nur um die Frage, ob sich der Code kompilieren lässt. Auch das Build-Skript selbst ist eine zu prüfende Eingabe: Es kann Umgebungsvariablen lesen, Benutzerverzeichnisse durchsuchen, gemeinsam genutzte Caches verändern oder sogar Werkzeuge ersetzen, die nachfolgende Jobs verwenden. Das Sicherheitsziel muss daher klar sein: Externe Pull Requests dürfen Builds und Tests ausführen, erhalten aber keinen Zugriff auf Release-Anmeldedaten und können erzeugte Artefakte nicht direkt in den produktiven Release-Prozess einschleusen.
Zwei getrennte Vertrauenspfade definieren
Teilen Sie die Jobs in einen „schlüssellosen Prüfpfad“ und einen „vertrauenswürdigen Release-Pfad“ auf. Der erste verarbeitet externe Pull Requests und führt ausschließlich die Abhängigkeitsauflösung, Kompilierung, statische Prüfungen sowie Tests aus, die keine echten Anmeldedaten benötigen. Der zweite akzeptiert nur feste Commits, die bereits geprüft und zusammengeführt wurden, und übernimmt Signierung, Archivierung und Auslieferung.
Auf den dedizierten physischen Nodes von VMArm lassen sich für beide Pfade unterschiedliche macOS-Systembenutzer einrichten. Der Prüfbenutzer gehört nicht zur Administratorgruppe, speichert keine Release-Zertifikate und kann das Benutzerverzeichnis des Release-Benutzers nicht lesen. Der Release-Benutzer verwendet seinerseits keine Skripte, Caches oder Werkzeugverzeichnisse wieder, in die der Prüfbenutzer schreiben kann.
Die Trennung durch Systembenutzer ist keine Grenze wie bei einer virtuellen Maschine. Sie verringert deutlich das Risiko, dass gewöhnliche Skripte versehentlich Anmeldedaten lesen oder Dateien verunreinigen. Systemaktualisierungen, minimale Berechtigungen und eine manuelle Prüfung nicht vertrauenswürdigen Codes kann sie jedoch nicht ersetzen.
Empfohlen werden folgende Grenzen:
| Aspekt | Schlüssellose Prüfung | Vertrauenswürdiger Release |
|---|---|---|
| Codequelle | Festes Commit eines externen PR | Festes Commit eines geprüften Branches |
| Codesignierung | Deaktiviert | Gemäß Release-Prozess aktiviert |
| Arbeitsbereich | Für jeden Job neu erstellt | Separates Verzeichnis |
| Cache | Verwerfbar, nicht über Vertrauensdomänen hinweg nutzbar | Nur von vertrauenswürdigen Jobs gemeinsam genutzt |
| Build-Artefakte | Ausschließlich für Prüfungen | Nach erneutem Build ausgeliefert |
| Anmeldedaten | Werden nicht bereitgestellt | Bei Bedarf kurzzeitig bereitgestellt |
Arbeitsbereich mit minimalen Berechtigungen einrichten
Der Runner sollte nicht dauerhaft im Stammverzeichnis des Benutzerverzeichnisses bauen. Erstellen Sie für Prüfjobs ein eigenes Stammverzeichnis und stellen Sie sicher, dass andere lokale Benutzer es nicht lesen können. Verwenden Sie zu Beginn jedes Jobs ein temporäres Verzeichnis und entfernen Sie es am Ende unabhängig davon, ob der Job erfolgreich war oder fehlschlug.
set -euo pipefail
umask 077
RUN_ROOT="$HOME/ci-untrusted"
mkdir -p "$RUN_ROOT"
chmod 700 "$RUN_ROOT"
WORK_DIR="$(mktemp -d "$RUN_ROOT/job.XXXXXX")"
cleanup() {
chmod -R u+rwX "$WORK_DIR" 2>/dev/null || true
rm -rf "$WORK_DIR"
}
trap cleanup EXIT INT TERM
cd "$WORK_DIR"
printf 'user=%s\nworkspace=%s\n' "$(id -un)" "$WORK_DIR"
Vor rm -rf muss sichergestellt sein, dass der Pfad auf das vom aktuellen Job erstellte temporäre Verzeichnis verweist. Branch-Namen dürfen nicht direkt in den Pfad eingesetzt werden. Sie können Schrägstriche, Leerzeichen oder absichtlich präparierte Zeichen enthalten und eignen sich deshalb als Anzeigetext, aber nicht zur ungefilterten Verwendung in Dateipfaden.
Protokollieren Sie nach dem Auschecken des Quellcodes den Commit-Hash und geben Sie ihn im Log aus. Der nachfolgende vertrauenswürdige Pfad muss den Code anhand dieser unveränderlichen Kennung erneut abrufen. Er darf sich nicht ausschließlich auf einen Branch-Namen verlassen, der sich später noch ändern kann.
Vor dem Build nachweisen, dass die Umgebung keine Schlüssel enthält
„Schlüssel werden nicht aktiv verwendet“ bedeutet nicht, dass der Job sie nicht lesen kann. Der Runner-Dienst kann Variablen aus seiner Startumgebung erben, und Shell-Initialisierungsdateien können zusätzliche Token setzen. Prüfen Sie deshalb vor dem Aufruf von Projektskripten die Variablennamen und übergeben Sie dem Build-Befehl ausschließlich die erforderlichen Variablen.
blocked='TOKEN|SECRET|PASSWORD|PRIVATE|SIGNING|AUTH|CREDENTIAL'
if env | cut -d= -f1 | grep -Eiq "$blocked"; then
printf '%s\n' "blocked environment variable detected" >&2
exit 70
fi
env -i \
HOME="$HOME" \
PATH="/usr/bin:/bin:/usr/sbin:/sbin" \
TMPDIR="$WORK_DIR/tmp" \
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-configuration Debug \
-derivedDataPath "$WORK_DIR/DerivedData" \
CODE_SIGNING_ALLOWED=NO \
build
Wenn das Projekt Paketmanager oder benutzerdefinierte Werkzeuge benötigt, sollten deren geprüfte Verzeichnisse ausdrücklich zu PATH hinzugefügt werden, anstatt die vollständige Anmeldeumgebung zu übernehmen. Die Prüfung von Variablennamen ist nur eine erste Hürde: Vertrauliche Werte können sich außerdem in Konfigurationsdateien, Schlüsselbunden, Proxy-Einstellungen und Git-Anmeldedatenhilfen befinden. Der Prüfbenutzer sollte deshalb von Anfang an frei von solchen Daten bleiben, statt vor jedem Build lediglich einige Variablen zu löschen.
Abhängigkeiten und Caches handhaben
Externer Code kann Inhalte in einen Cache schreiben. Greift ein vertrauenswürdiger Job später auf denselben Cache zu, kann er dadurch manipuliert werden. Am sichersten ist es, Caches nach Vertrauensdomäne zu trennen und sie für externe Pull Requests zusätzlich nach Repository oder Jobnummer aufzuteilen. Der Prüfpfad darf nicht in die SwiftPM-, Kompilierungsmodul- oder benutzerdefinierten Binärwerkzeug-Caches des Release-Benutzers schreiben.
Prüfen Sie nach der Wiederherstellung eines Caches dessen Eigentümer und Berechtigungen und weisen Sie symbolische Links zurück, die aus dem Arbeitsbereich herausführen. Ausführbare Werkzeuge sollten möglichst aus einem schreibgeschützten Basisverzeichnis stammen. Skripte, die das Projekt während des Builds erzeugt, dürfen nur im aktuellen temporären Arbeitsbereich ausgeführt werden.
Artefakte nicht über die Vertrauensgrenze übernehmen
Ein erfolgreicher Build eines externen Pull Requests belegt lediglich, dass das betreffende Commit unter den Prüfbedingungen bestanden hat. Er sagt nichts darüber aus, ob das erzeugte Archiv veröffentlicht werden kann. Ein Prüfjob könnte Build-Phasen ersetzen, Ausgaben verändern oder zusätzliche Dateien in das Archiv einschleusen. Anwendungspakete, Archive und Exportverzeichnisse aus dem schlüssellosen Pfad dürfen daher nicht in den vertrauenswürdigen Release-Job übernommen werden.
Der vertrauenswürdige Pfad sollte folgende Schritte ausführen:
- Den vom Prüfsystem bestätigten Commit-Hash übernehmen.
- Dieses Commit in einem neuen Arbeitsbereich des Release-Benutzers erneut auschecken.
- Prüfen, ob das aktuelle Commit exakt dem freigegebenen Wert entspricht.
- Abhängigkeiten erneut aus einem vertrauenswürdigen oder leeren Cache auflösen.
- Tests ausführen und das Archiv direkt aus dem Quellcode neu erstellen.
- Anmeldedaten nur für den kurzen Zeitraum bereitstellen, in dem sie zum Signieren benötigt werden.
- Commit-Hash, Prüfsumme der Abhängigkeits-Lockdatei und Prüfsumme des endgültigen Artefakts protokollieren.
Falls Testergebnisse aus dem Prüfpfad übertragen werden müssen, dürfen ausschließlich nicht ausführbare Daten weitergegeben und auf der vertrauenswürdigen Seite nach einem streng definierten Format verarbeitet werden. Shell-Skripte, Plug-ins, Kompilierungs-Caches oder Dateien, die direkt in ein Anwendungspaket gelangen können, dürfen nicht übertragen werden.
Fehlerbereinigung und Audit in die Abnahme einbeziehen
Isolationskonzepte versagen am häufigsten in Fehlerpfaden. Nach einem abgebrochenen Build, einer Zeitüberschreitung oder einem Neustart des Rechners können temporäre Verzeichnisse, Hintergrundprozesse und Protokolle zurückbleiben. Der Runner sollte zu Beginn jedes Jobs nach Resten des vorherigen Jobs suchen und in der Abschlussphase alle untergeordneten Prozesse des aktuellen Jobs beenden.
Vor der Inbetriebnahme kann die Konfiguration anhand der folgenden Liste abgenommen werden:
- Der Prüfbenutzer kann den Inhalt des Benutzerverzeichnisses des Release-Benutzers nicht auflisten.
- Die Protokolle externer Pull Requests enthalten weder Token noch private Schlüssel oder Pfade zu Anmeldedaten.
- Im Prüfpfad ist
CODE_SIGNING_ALLOWED=NOaktiviert. - Die beiden Pfade verwenden weder beschreibbare DerivedData noch Werkzeug-Caches gemeinsam.
- Nach dem Abbruch eines Jobs wird der temporäre Arbeitsbereich weiterhin gelöscht.
- Der vertrauenswürdige Release-Pfad checkt das feste Commit erneut aus, statt das Prüfverzeichnis wiederzuverwenden.
- Release-Artefakte lassen sich auf Commit-Hash, Prüfsumme der Lockdatei und Build-Protokoll zurückführen.
- Das Runner-Konto verfügt über keine Administratorrechte, die im normalen Betrieb nicht benötigt werden.
Führen Sie abschließend einen aktiven Test durch: Reichen Sie einen Test-PR ein, dessen Build-Skript versucht, gängige vertrauliche Variablen zu lesen, auf Release-Verzeichnisse zuzugreifen und Dateien in einen gemeinsam genutzten Cache zu schreiben. Das richtige Ergebnis lautet nicht, dass das Skript „nichts gefunden“ hat, sondern dass diese Pfade aufgrund des Berechtigungs- und Prozessdesigns grundsätzlich nicht erreichbar sind. Erst danach ist die Prüfung externer Beiträge tatsächlich vom produktiven Release-Prozess entkoppelt.
Häufig gestellte Fragen
Reicht es, die Codesignierung für externe Pull Requests abzuschalten?
Nein. Zusätzlich müssen geheime Variablen, Anmeldedaten, beschreibbare gemeinsame Caches und der Zugriff auf das Veröffentlichungskonto ausgeschlossen werden.
Darf ein im Prüfpfad erzeugtes Archiv direkt veröffentlicht werden?
Nein. Der Vertrauenspfad sollte das freigegebene Commit anhand seiner festen Kennung erneut abrufen und vollständig aus dem Quellcode bauen.
Wählen Sie einen dauerhaft verfügbaren Cloud-Mac für Ihren nächsten Xcode-Build
Prüfen Sie Chip, Arbeitsspeicher, Speicher, Abrechnungszeitraum und Knotenregion, bevor Sie mit der Konfiguration beginnen. Der tatsächlich verfügbare Status wird in Echtzeit von der Konsole angezeigt.