Ein gemeinsam genutzter Mac Runner für mehrere GitLab-CI-Projekte ist vertretbar, wenn nur vertrauenswürdige Projekte in einen kontrollierten Build-Pool gelangen. Für Produktionssignaturen, unterschiedliche Vertrauensdomänen und nicht vertrauenswürdige Merge Requests sollten Sie dagegen einen separaten Mac Runner einsetzen. Ihre umsetzbare Zielarchitektur für diese Woche lautet daher: gemeinsamer Build-Pool für gewöhnliche Kompilierung und Tests, dedizierter Release-Pool für Zertifikate, private Schlüssel und App-Store-Veröffentlichungen.

Diese Anleitung richtet sich an:

  • Plattform- und IT-Verantwortliche, die mehreren Repositories einheitliche macOS-Build-Ressourcen bereitstellen.
  • Sicherheits- und Release-Verantwortliche, die Zertifikate, private Schlüssel und interne Abhängigkeiten begrenzen müssen.
  • Technische Leiter, die zwischen gemeinsamem Runner, dediziertem Mac und elastisch zugeschalteten Remote-Mac-Knoten entscheiden.

Zielarchitektur: gemeinsamer Build-Pool gegen dedizierten Release-Pool

Die Architektur besteht aus zwei Vertrauensstufen. Im gemeinsamen Build-Pool laufen Projekte, deren Quellcode, Abhängigkeiten und interne Netzwerkzugriffe derselben kontrollierten Vertrauensdomäne angehören. Dieser Pool darf kompilieren, testen und nicht-produktive Artefakte erzeugen.

Der dedizierte Release-Pool nimmt ausschließlich geschützte Jobs an. Dort liegen die Produktionszertifikate, privaten Schlüssel, Provisioning-Profile und App-Store-Connect-Zugangsdaten. Ein Projekt wird nicht deshalb vertrauenswürdig, weil es ein passendes Runner-Tag besitzt. Tags steuern die Auswahl des Runners; sie schaffen keine Sandbox auf dem Mac.

GitLab dokumentiert, dass der Shell executor nur eine begrenzte Isolation bietet und für vertrauenswürdige Builds gedacht ist. Ein Auftrag kann unter Umständen auf Dateien anderer Projekte auf demselben Host zugreifen. Diese Grenze ist für die zentrale Architekturentscheidung wichtiger als die Anzeige „Runner online“ in GitLab. Lesen Sie dazu die Sicherheitsgrenzen des Shell executors.

Vertrauensprüfung vor der Aufnahme

Ordnen Sie jedes Projekt vor der technischen Aufnahme anhand von vier Fragen ein:

  1. Stammt der Code ausschließlich aus kontrollierten und bekannten Repositories?
  2. Haben die Projektmitglieder vergleichbare Berechtigungen für interne Systeme?
  3. Benötigt der Build Zugriff auf interne Netzwerke, private Paketquellen oder Unternehmensdaten?
  4. Werden Produktionssignaturen oder andere nicht ersetzbare Geheimnisse benötigt?

Wenn alle Antworten in den unkritischen Bereich fallen, kann das Projekt in den gemeinsamen Build-Pool gelangen. Wenn nur das Benutzerkonto oder der interne Netzwerkzugriff abweicht, ist ein eigener Runner-Dienstbenutzer oder ein separater Knoten zu prüfen. Sobald private Produktionsschlüssel, fremde Vertrauensdomänen oder nicht vertrauenswürdige Merge Requests beteiligt sind, sollte der Job nicht auf dem gemeinsamen Mac laufen.

Woche 1, Meilenstein 1: Runner-Bereich und Routen festlegen

Beginnen Sie nicht mit einer möglichst großen Freigabe. Legen Sie zuerst fest, welche Projekte den Runner überhaupt sehen dürfen. GitLab unterscheidet zwischen project runner, group runner und instance runner. Ein project runner ist an ein Projekt gebunden. Ein group runner kann mehreren Projekten innerhalb einer Gruppe dienen. Ein instance runner steht grundsätzlich auf einer breiteren GitLab-Instanz zur Verfügung.

Für einen kontrollierten Mehrprojektbetrieb ist ein begrenzter group runner meist der geeignete Ausgangspunkt. Ein instance runner vergrößert die potenzielle Reichweite und sollte nur gewählt werden, wenn die Governance, Projektstruktur und Vertrauensbewertung das ausdrücklich rechtfertigen. Die Dokumentation zu Runner-Bereichen beschreibt diese Reichweiten und ihre organisatorischen Folgen.

GitLab Runner kann mehrere Projekte bedienen – aber nicht automatisch sicher

Ja, ein GitLab Runner kann mehreren Projekten zugeordnet werden. Daraus folgt jedoch nicht, dass die Projekte voneinander isoliert sind. Der Runner-Bereich beantwortet die Frage „Welche Projekte dürfen Jobs an diesen Runner senden?“. Er beantwortet nicht die Frage „Welche Dateien, Prozesse, Umgebungsvariablen oder Schlüssel kann ein Job auf dem Host lesen?“.

Für den ersten Aufbau definieren Sie deshalb getrennte Tags, beispielsweise für normale Builds und geschützte Releases. Aktivieren Sie die Option, dass der Runner nur Jobs mit passenden Tags ausführt. Verwenden Sie zusätzlich Protected Runner und geschützte Branches oder Tags für den Release-Pool. GitLab erklärt die Zuordnung von Tags und geschützten Runnern in der offiziellen Konfigurationsdokumentation.

Eine minimale Routing-Prüfung sollte zwei Fälle abdecken:

build_ios:
  tags:
    - macos-build
  script:
    - xcodebuild -version
    - ./ci/build.sh

release_ios:
  tags:
    - macos-release
  rules:
    - if: '$CI_COMMIT_TAG'
  script:
    - ./ci/sign-and-upload.sh

Das Beispiel beweist noch keine Host-Isolation. Es zeigt nur, dass ein gewöhnlicher Build und ein Release unterschiedliche Zielrouten anfordern. Prüfen Sie zusätzlich in den Jobprotokollen, welcher Runner den Auftrag angenommen hat. Ein absichtlich falsch markierter Job muss abgewiesen oder auf einem nicht privilegierten Pool ausgeführt werden.

Woche 1, Meilenstein 2: Dienstkonto, Arbeitsbereiche und Wiederherstellung

Der erste erfolgreiche Job muss unter dem echten GitLab-Runner-Dienstkonto laufen. Ein Build, der in einer Administratorsitzung funktioniert, beweist nichts über die Berechtigungen des CI-Kontexts. Prüfen Sie deshalb Benutzer, Home-Verzeichnis, Arbeitsverzeichnis, temporäre Dateien und benutzerspezifische Konfiguration direkt aus dem Pipeline-Job.

Bei macOS Shell executor gehören mindestens diese Bereiche in die Prüfung:

  • Quellcode und Checkout-Verzeichnis des aktuellen Projekts.
  • Abgeleitete Xcode-Daten und wiederherstellbare Build-Caches.
  • Temporäre Dateien und lokale Protokolle.
  • Artefakte, die nach dem Job archiviert oder gelöscht werden.
  • Benutzerbezogene Konfigurationen von Xcode, Homebrew und Paketmanagern.
  • Keychain-Inhalte und Profile, sofern der Job Signaturfunktionen ausführt.

Eine getrennte Verzeichnisstruktur reduziert versehentliche Verwendung, ist aber keine Sicherheitsgrenze. Wenn mehrere Jobs unter demselben lokalen macOS-Benutzer laufen, kann ein bösartiges oder fehlerhaftes Skript weiterhin Dateien dieses Benutzers untersuchen. Die GitLab-Hinweise zum Shell executor und zur Bereinigung sind deshalb gemeinsam mit Ihren lokalen Dateirechten zu bewerten.

macOS Shell executor für Projekte begrenzen

Für den gemeinsamen Build-Pool sollten Sie zuerst rekonstruierbare Daten behandeln. Quellcode, DerivedData, Paket-Caches und temporäre Build-Ergebnisse dürfen nach einer definierten Aufbewahrungsregel entfernt oder neu erzeugt werden. Dauerhafte Geheimnisse gehören nicht in diese Verzeichnisse und nicht in frei lesbare Cache-Archive.

GitLab-Caches beschleunigen nachfolgende Jobs, können aber bei falschen Pfaden oder zu weit gefassten Cache-Schlüsseln Daten zwischen Projekten vermischen. Verwenden Sie projektspezifische Schlüssel und prüfen Sie, ob ein Cache wirklich nur wiederherstellbare Abhängigkeiten enthält. Die GitLab-Dokumentation zu CI/CD-Caches beschreibt deren Lebenszyklus und Grenzen.

Ihre Abnahme sollte mindestens diese Prüfungen enthalten:

  1. Ein Job schreibt eine eindeutig erkennbare Testdatei in sein Arbeitsverzeichnis.
  2. Ein nachfolgender Job eines anderen Projekts sucht gezielt nach dieser Datei.
  3. Der Runner bereinigt Arbeits- und temporäre Verzeichnisse nach dem Abschluss.
  4. Ein zweiter Test prüft, ob Umgebungsvariablen und Artefaktpfade des ersten Projekts sichtbar bleiben.
  5. Die Pipeline-Protokolle dokumentieren Ergebnis, Dienstkonto und Bereinigungsstatus.

Wenn der zweite Job Dateien des ersten Projekts lesen kann, ist die gemeinsame Nutzung für diese Vertrauensdomäne beendet. Wechseln Sie dann zu einem eigenen Dienstkonto oder zu einem eigenen Mac-Knoten. Eine neue Ordnerstruktur allein beseitigt den Befund nicht.

Woche 1, Meilenstein 3: Zweites Projekt kontrolliert aufnehmen

Nehmen Sie das zweite Projekt bewusst als Angriffstest auf. Der Zweck ist nicht, den normalen Build ein weiteres Mal zu bestätigen, sondern mögliche Rückstände nach dem ersten Job sichtbar zu machen. Testen Sie dabei vier Kategorien: Quellcode, Umgebungsdaten, Hintergrundprozesse und gemeinsam genutzte Build-Ressourcen.

Achten Sie insbesondere auf folgende Fehlerbilder:

  • Ein Prozess des ersten Jobs läuft weiter und hält Ports, Dateien oder Simulatorressourcen offen.
  • DerivedData wird von zwei Projekten über denselben Pfad genutzt.
  • Homebrew- oder Paketmanager-Zustände verändern die Umgebung des nächsten Jobs.
  • Cache-Schlüssel erlauben das Wiederverwenden von Artefakten aus einem anderen Projekt.
  • Ein vorheriger Job hinterlässt temporäre Dateien, Profile oder Shell-Konfigurationen.
  • Ein Projekt kann auf private Abhängigkeiten oder Netzwerkziele eines anderen Projekts zugreifen.

Führen Sie diese Prüfung mit parallelen und aufeinanderfolgenden Jobs aus. Die genaue Anzahl der Parallelaufträge sollten Sie aus Ihren eigenen Pipeline-Protokollen ableiten; ohne Unternehmensmessung wäre eine feste Kapazitätszahl nicht belastbar. Prüfen Sie nach jedem Test den Zustand vor und nach der Bereinigung. Speichern Sie die Nachweise als Pipeline-Artefakte oder in Ihrem internen Abnahmeprotokoll, sofern darin keine Geheimnisse enthalten sind.

Bei Projekten aus unterschiedlichen Geschäftsbereichen ist nicht nur die technische, sondern auch die organisatorische Vertrauensgrenze relevant. Wenn ein Team interne Kundendaten, abweichende Zugriffsrechte oder eigene Compliance-Vorgaben besitzt, sollte dieses Projekt nicht aus Bequemlichkeit im gemeinsamen Pool bleiben.

Entscheidungspunkt: gemeinsame Nutzung oder eigener Mac Runner

Die folgende Liste bildet die operative Entscheidung ab:

  • Wenn Quellcode, Projektmitglieder, interne Abhängigkeiten und Berechtigungen derselben Vertrauensdomäne angehören, dann wählen Sie den kontrollierten gemeinsamen Build-Pool.
  • Wenn nur die Build-Identität oder der lokale Dateibereich getrennt werden muss, dann testen Sie zuerst einen eigenen Dienstbenutzer und eine strengere Bereinigung. Wenn der Cross-Project-Test weiterhin Zugriff zeigt, dann wechseln Sie zu einem eigenen Mac.
  • Wenn ein Projekt nicht vertrauenswürdige Merge Requests ausführt, dann darf es nicht auf den gemeinsamen Shell-executor-Host gelangen.
  • Wenn ein Job Produktionszertifikate, private Schlüssel oder App-Store-Connect-Zugangsdaten verwendet, dann routen Sie ihn auf einen dedizierten Release-Runner.
  • Wenn die gemeinsame Warteschlange regelmäßig konkurrierende Builds erzeugt, dann erweitern Sie den Build-Pool oder ergänzen einen elastischen Remote-Mac-Knoten, statt Signaturjobs in denselben Pool zu drängen.
  • Wenn der Mac nach einem Neustart nicht reproduzierbar in denselben Dienst-, Keychain- und Runner-Zustand zurückkehrt, dann stoppen Sie die Produktionsfreigabe und führen Sie zuerst eine Wiederherstellungsabnahme durch.

Woche 2, Meilenstein 4: Keychain und Signatur vom Build trennen

Die macOS Keychain schützt gespeicherte Schlüssel und Zertifikate über Zugriffsregeln. Sie macht jedoch nicht automatisch jeden Prozess auf demselben lokalen Benutzer sicher. Ein CI-Skript, das unter diesem Benutzer läuft, kann je nach Keychain-Konfiguration und Freigaberegeln weiterhin eine relevante Angriffsfläche darstellen. Apple beschreibt die Funktionsweise der Keychain Services und der Access-Control-Listen in den offiziellen Sicherheitsdokumenten.

Behandeln Sie diese Objekte getrennt:

  • Produktionszertifikate und private Schlüssel.
  • Provisioning-Profile.
  • App-Store-Connect-Schlüssel oder vergleichbare Veröffentlichungstoken.
  • Zugangsdaten zu privaten Paketquellen.
  • Kurzlebige Build-Geheimnisse und nicht-produktive Testzertifikate.

Der Release-Runner sollte ausschließlich geschützte Jobs akzeptieren. Ein normaler Projektbranch darf keine Route zu diesem Knoten erhalten. Für den Nachweis benötigen Sie einen erfolgreichen Minimaltest mit einem kontrollierten Testzertifikat, einen absichtlich abgewiesenen Job aus einem nicht geschützten Projekt und eine dokumentierte Rotation der verwendeten Zugangsdaten.

Prüfen Sie außerdem den Zustand nach einem Neustart. Eine Keychain-Regel, die nur in einer interaktiven Sitzung funktioniert, ist für einen unbeaufsichtigten CI-Knoten nicht ausreichend. Die Apple-Dokumentation zur Einschränkung des Keychain-Zugriffs sollte dabei neben den internen Sicherheitsvorgaben berücksichtigt werden.

Entscheidungstabelle für die Infrastruktur

Situation Geeignete Route Begründung Abnahmebeleg
Vertrauenswürdige Projekte, normale Builds und Tests Gemeinsamer Build-Pool Routing und Bereinigung reichen für rekonstruierbare Build-Daten, sofern die Projekte derselben Vertrauensdomäne angehören Tag-Nachweis, Cross-Project-Test, Bereinigungsprotokoll
Unterschiedliche lokale Berechtigungen oder Geschäftsbereiche Eigener Dienstbenutzer oder eigener Mac Ein gemeinsamer Shell-Kontext kann Dateien und Prozesse des anderen Projekts erreichen Dateizugriffstest, Rechteprüfung, Prozesskontrolle
Nicht vertrauenswürdige Merge Requests Separater, nicht privilegierter Mac Runner Der gemeinsame Shell executor bietet keine vollständige Host-Sandbox Abgewiesene Route und fehlende Geheimnisse im Job
Produktionssignatur und Veröffentlichung Dedizierter Release-Pool Zertifikate, private Schlüssel und Veröffentlichungstoken brauchen eine engere Vertrauensgrenze Signaturtest, geschützte Route, Rotationsnachweis
Schwankende Build-Last Gemeinsame Grundkapazität plus elastischer Remote Mac Zusätzliche Knoten verhindern, dass Signatur- und Testjobs um dieselbe Ressource konkurrieren Warteschlangen- und Wiederherstellungsprotokolle

Die Tabelle ersetzt keine Messung. Sie verhindert jedoch, dass Sie einen sichtbaren Runner-Status mit einer ausreichenden Sicherheits- oder Kapazitätszusage verwechseln.

Woche 2, Meilenstein 5: Xcode-Build und Fehlerwiederherstellung abnehmen

Die letzte Abnahme muss den vollständigen Weg prüfen: Quellcode abrufen, Abhängigkeiten auflösen, mit Xcode bauen, Artefakt erzeugen, gegebenenfalls signieren, hochladen und einen kontrollierten Fehler wiederherstellen. Ein isolierter Aufruf von xcodebuild -version zeigt nur, dass ein Kommando vorhanden ist. Er bestätigt weder die richtige Projektumgebung noch die Signatur- und Uploadkette.

Verwenden Sie eine kleine, reproduzierbare Pipeline und halten Sie die Konfiguration in einer versionierten YAML-Datei. Die GitLab-YAML-Dokumentation hilft dabei, Regeln, Tags und Abhängigkeiten nachvollziehbar zu halten. Trennen Sie Build- und Release-Jobs auch im YAML deutlich, damit ein späteres Tagging nicht versehentlich eine privilegierte Route öffnet.

Dokumentieren Sie für jedes Projekt:

  • Zugeordneten Runner-Bereich und verwendete Tags.
  • Lokales macOS-Dienstkonto und relevante Dateirechte.
  • Cache- und Artefaktstrategie.
  • Verhalten bei abgebrochenen Jobs und übrig gebliebenen Prozessen.
  • Neustart- und Wiederherstellungsablauf.
  • Freigabekriterien für Zertifikate und private Schlüssel.

In der ersten Betriebswoche sollten Sie Warteschlangen, fehlgeschlagene Bereinigungen, konkurrierende Simulatornutzung und Signaturbelegung aus Ihren eigenen GitLab-Protokollen auswerten. Verwenden Sie keine pauschale Parallelitäts- oder Leistungszahl aus einem fremden Umfeld. Wenn gewöhnliche Builds stabil laufen, aber Signaturjobs regelmäßig warten oder nach einem Neustart manuelle Eingriffe benötigen, ist das ein Architekturproblem und kein Anlass, weitere Jobs auf denselben Knoten zu legen.

Gemeinsamer Pool oder Remote Mac: Beschaffungsentscheidung

Ein einzelner eigener Mac als Dauerlösung wirkt zunächst übersichtlich, hat aber drei typische Nachteile: Er wird zum gemeinsamen Engpass, er verbindet unterschiedliche Vertrauensdomänen auf einem Host und er vermischt Build-, Wartungs- und Signaturverantwortung. Bei wachsender Projektzahl führt das oft zu manuellen Ausnahmen bei Tags, Berechtigungen und Bereinigung.

Ein gemieteter Remote Mac kann für einen zeitlich begrenzten Pilotbetrieb schneller eine getrennte Kapazität bereitstellen, ohne dass Sie sofort zusätzliche Hardware beschaffen, inventarisieren und physisch betreiben müssen. Über VMSPIN für Mac-Remote-Umgebungen können Sie dabei zunächst einen Build-Knoten und einen separaten Release-Knoten als getrennte Versuchsflächen bewerten. Für eine konkrete Unternehmensplanung können Sie die VMSPIN-Optionen und Mietmodelle prüfen; die Auswahl sollte sich an Ihren Pipeline-Protokollen, der benötigten Xcode-Umgebung und Ihren Sicherheitsvorgaben orientieren, nicht an einer pauschalen Leistungszusage.

Wenn Sie nur seltene, stabile Dauerlast mit physischen Schnittstellen benötigen, kann der Kauf eigener Hardware sinnvoller sein. Wenn Sie dagegen mehrere Repositories aufnehmen, Vertrauensgrenzen testen oder kurzfristig zusätzliche macOS-Kapazität für CI/CD benötigen, ist ein gemieteter Remote Mac als Pilot oft der kontrollierbarere nächste Schritt. Prüfen Sie vor einer Bestellung insbesondere Root-Zugriff, Dienstkonto, Zugriffspfad, Datenlöschung und die Möglichkeit, Build- und Release-Knoten organisatorisch getrennt zu halten.

Die wichtigste Entscheidung lautet daher nicht „ein Runner oder viele“, sondern „welche Identität und welches Geheimnis darf auf welchem Mac existieren“. Starten Sie mit einem kontrollierten gemeinsamen Build-Pool, halten Sie Produktionssignaturen auf einem dedizierten Runner und erweitern Sie erst dann, wenn Ihre eigenen Protokolle einen Kapazitäts- oder Isolationsbedarf belegen.