Le deuxième projet lit encore les fichiers, caches ou processus laissés par le premier, alors que les étiquettes GitLab CI orientent correctement les tâches.

La solution la plus rapide consiste à réserver le Mac Runner partagé aux projets de confiance, puis à envoyer la signature de production, les domaines de confiance différents et les demandes de fusion non fiables vers un Mac Runner dédié. Les étiquettes, les exécuteurs protégés et la séparation des répertoires améliorent le routage, mais ne constituent pas une isolation complète de l’hôte.

Qui doit suivre cette chronologie de déploiement ?

Ce guide s’adresse aux responsables plateforme qui fournissent des ressources de compilation macOS à plusieurs dépôts GitLab CI. Il concerne aussi les responsables sécurité et publication qui doivent limiter l’accès aux certificats, aux clés privées et aux dépendances internes.

Les équipes IT et achats y trouveront enfin une méthode pour décider entre un pool partagé, des nœuds dédiés ou une capacité Mac distante ajoutée progressivement, sans acheter plusieurs machines avant d’avoir validé les frontières de confiance.

Première étape : séparer le pool de compilation du pool de publication

Avant d’enregistrer le premier GitLab Runner, dessinez deux niveaux clairement distincts :

Projets internes de confiance
          │
          ▼
Pool Mac partagé ── compilation, tests, artefacts intermédiaires
          │
          └── transfert contrôlé de l’artefact
                            │
                            ▼
Pool de publication dédié ── signature, archivage, envoi officiel

Cette architecture répond à une confusion fréquente : partager une machine ne signifie pas partager les identités, les secrets ou les droits de publication. Un même Mac peut accueillir plusieurs projets de confiance pour des compilations ordinaires, mais un script exécuté par le même compte local peut potentiellement examiner les fichiers accessibles à ce compte.

GitLab indique que l’exécuteur Shell offre une isolation limitée et qu’un travail peut accéder au code d’autres projets présents sur la même machine. Il doit donc être réservé aux tâches et aux dépôts de confiance, conformément à la documentation officielle de l’exécuteur Shell.

Classez chaque projet avant toute mise en commun :

Type de tâche ou de projet Pool partagé autorisé Compte local séparé Mac Runner dédié
Compilation et tests d’un dépôt interne de confiance Oui Selon le niveau de risque Non
Projet d’une autre direction avec accès réseau différent À examiner Oui Souvent préférable
Demande de fusion provenant d’un code non approuvé Non Insuffisant seul Oui
Signature de production et publication officielle Non Insuffisant seul Oui
Accès à des certificats, clés privées ou secrets sensibles Non Insuffisant seul Oui

Le critère déterminant n’est donc pas le nombre de dépôts, mais la combinaison du code exécuté, des droits GitLab, de l’accès au réseau interne et des actifs de signature. Une étiquette comme macos-build permet de choisir un nœud ; elle ne transforme pas le Shell executor en bac à sable.

Le délai de décision avant l’enregistrement

Consacrez la première phase à quatre questions :

  • Le projet est-il administré par la même équipe que les autres dépôts ?
  • Les demandes de fusion peuvent-elles exécuter un code non examiné ?
  • Le travail doit-il atteindre un service interne ou un dépôt de secrets ?
  • Le pipeline utilise-t-il un certificat, une clé privée ou un compte de publication ?

Si une réponse crée une frontière de confiance différente, ne tentez pas de la compenser uniquement par une nouvelle étiquette. Placez le projet dans une file dédiée et documentez la raison dans l’inventaire des runners.

Deuxième étape : choisir la portée GitLab Runner et verrouiller le routage

Un GitLab Runner peut être associé à un projet, à un groupe ou à l’instance. La portée détermine qui peut l’utiliser ; elle ne change pas, à elle seule, la capacité d’isolation du compte macOS qui exécute les commandes.

Pour plusieurs projets contrôlés par une même équipe, commencez par un group runner administré par le groupe concerné. Un project runner convient à un dépôt qui doit rester strictement séparé. Un runner d’instance élargit la surface de décision et ne devrait pas devenir le choix par défaut pour un pool Mac partagé.

GitLab décrit ces différences dans sa documentation sur la portée des runners. Avant de faire circuler une tâche réelle, créez une liste d’autorisation comprenant les projets, les branches et les responsables habilités.

Première heure : établir les règles de routage

Attribuez des étiquettes dont le sens est opérationnel, par exemple :

build_ios:
  tags:
    - macos-build
  script:
    - xcodebuild -scheme App -configuration Debug build

Le fragment ne constitue pas une politique de sécurité. Il indique seulement qu’un job recherche un runner possédant l’étiquette correspondante. Dans la configuration du runner, activez l’option qui limite son utilisation aux tâches étiquetées. Utilisez ensuite un runner protégé pour les tâches de publication et associez-le uniquement aux branches ou étiquettes protégées.

La documentation GitLab sur la configuration des runners précise le fonctionnement du routage par étiquettes et des runners protégés. Conservez comme preuves :

  • la portée du runner ;
  • les étiquettes réellement enregistrées ;
  • la liste des projets autorisés ;
  • une exécution acceptée par le bon nœud ;
  • une exécution refusée lorsqu’elle ne possède pas l’étiquette ou la protection requise.

Les trois tests de refus à effectuer

Ne validez pas seulement un pipeline qui réussit. Vérifiez aussi que :

  • un projet non autorisé ne peut pas utiliser le runner de groupe ;
  • une branche non protégée ne peut pas atteindre le runner de publication ;
  • un job sans étiquette ne part pas par défaut sur le Mac partagé.

Ces tests donnent une preuve de contrôle d’accès. Ils ne prouvent pas que deux tâches exécutées par le même compte local ne peuvent pas lire leurs fichiers respectifs. Cette seconde question doit être traitée pendant la validation du système de fichiers.

Le Shell executor macOS : quel niveau d’isolation entre projets ?

Le Shell executor macOS est adapté à un environnement de confiance, mais il ne fournit pas une frontière hôte complète entre les projets. Vous pouvez réduire les erreurs avec des répertoires distincts, des nettoyages et des comptes séparés ; vous ne devez pas présenter ces mesures comme une protection absolue contre un script malveillant exécuté dans le même contexte.

La procédure GitLab d’installation d’un Runner sur macOS doit être suivie dans le contexte du véritable service GitLab Runner. Une commande qui réussit dans un terminal administrateur ne valide pas le comportement du pipeline.

Troisième étape : valider le compte, les répertoires et les artefacts

Lancez une première tâche de diagnostic avec le compte de service réellement utilisé par GitLab Runner. Relevez le compte effectif, le répertoire personnel, le répertoire de travail, le chemin temporaire et les variables visibles par le job.

Séparez au minimum les catégories suivantes :

  • code source extrait pour le job ;
  • cache recréable des dépendances ;
  • données de compilation, notamment DerivedData ;
  • artefacts destinés au téléchargement ;
  • journaux ;
  • certificats, profils et autres secrets persistants.

La séparation des chemins réduit le risque de confusion et facilite l’effacement. Elle ne bloque pas un script qui dispose déjà des droits du même compte local. C’est précisément pourquoi le test doit chercher une lecture inter-projets au lieu de supposer qu’un chemin différent suffit.

Pour les caches, définissez des clés qui ne mélangent pas les projets ou les branches sensibles. GitLab rappelle dans sa documentation sur la mise en cache CI/CD que le cache sert à accélérer des dépendances recréables ; il ne doit pas devenir un coffre pour des secrets ou des données qui exigent une confidentialité forte.

Validation dans le contexte CI

Ajoutez temporairement des contrôles non sensibles :

diagnostic_runner:
  tags:
    - macos-build
  script:
    - id
    - pwd
    - printf '%s\n' "$HOME"
    - find "$CI_PROJECT_DIR" -maxdepth 2 -type f -print
    - xcodebuild -version
  after_script:
    - rm -rf "$CI_PROJECT_DIR"

N’affichez jamais de valeur secrète dans les journaux. Le but est de confirmer l’identité et les chemins, non de faire sortir des informations confidentielles.

Après le job, recherchez les restes dans le répertoire de travail, les fichiers temporaires, les caches, les processus enfants et les données de compilation. Enregistrez l’état avant et après le nettoyage. Un runner « en ligne » dans l’interface ne fournit aucune preuve sur ces éléments.

Quatrième étape : intégrer le deuxième projet avec un test de contamination

Le deuxième dépôt est le meilleur moment pour interrompre un déploiement trop permissif. N’ajoutez pas immédiatement tous les projets au groupe. Faites exécuter au second projet une séquence destinée à révéler les résidus du premier.

Comparez les éléments suivants avant et après la tâche :

Élément contrôlé Vérification à réaliser Décision en cas d’échec
Code source Rechercher des fichiers ou répertoires du premier projet Suspendre le partage et revoir le compte local
Variables d’environnement Vérifier qu’aucune valeur du premier job ne persiste Revoir l’initialisation et la destruction du processus
Processus Contrôler les processus laissés après l’exécution Isoler le projet ou corriger l’arrêt des outils
Cache et dépendances Comparer les clés et les chemins entre projets Séparer les caches, puis refaire le test
DerivedData et simulateurs Vérifier les chemins et l’état des ressources Xcode Définir des espaces distincts ou réserver un nœud
Artefacts Confirmer qu’un projet ne peut pas reprendre ceux d’un autre Utiliser un transfert explicite et contrôlé

Exécutez aussi des tâches concurrentes si le pool doit réellement servir plusieurs projets en même temps. Observez les collisions sur les simulateurs, les fichiers temporaires, Homebrew et les répertoires de compilation. Une compilation correcte en série ne prouve pas que la concurrence est sûre.

Si les projets appartiennent à des directions différentes ou disposent de niveaux de confiance différents, arrêtez l’intégration à cette étape. Un compte macOS séparé peut réduire l’exposition accidentelle, mais un Mac Runner séparé reste préférable dès que le projet peut exécuter du code non approuvé ou accéder à un réseau interne distinct.

Cinquième étape : réserver la signature iOS à un Mac Runner dédié

La signature de production ne doit pas être une tâche ordinaire ajoutée au pool de compilation. Les certificats, les clés privées, les profils de provisioning, les identifiants App Store Connect et les jetons d’accès aux dépendances doivent posséder des frontières de stockage et d’utilisation distinctes.

macOS Keychain apporte des contrôles d’accès utiles, mais il ne neutralise pas automatiquement le risque d’un script lancé par le même utilisateur local. Consultez les règles d’accès de la documentation Apple sur Keychain Services, puis examinez les listes de contrôle d’accès Apple et les règles de restriction d’accès aux éléments Keychain.

Le pipeline de publication doit donc cumuler plusieurs protections :

  • runner dédié avec étiquette de publication ;
  • runner protégé ;
  • branches et étiquettes protégées ;
  • projet autorisé explicitement ;
  • compte macOS réservé à cette fonction ;
  • secrets injectés uniquement pendant la tâche ;
  • suppression ou verrouillage après utilisation ;
  • rotation documentée des certificats et jetons.

L’artefact signé doit provenir d’un transfert contrôlé depuis le pool de compilation. Le nœud de publication ne doit pas récupérer librement le dépôt de tous les projets du groupe. Faites un test minimal de signature, puis tentez le même job depuis un projet non autorisé : le résultat attendu est un refus, pas une signature partielle.

Après un redémarrage, répétez le contrôle. Vérifiez que le service revient avec le bon compte, que le Keychain ne s’ouvre pas implicitement pour une tâche non protégée et que l’accès doit être rétabli selon la procédure prévue. La rotation des identifiants doit également être testée, et non seulement décrite dans un document.

Quand un GitLab Runner de groupe est-il préférable à un runner de projet ?

Choisissez un runner de groupe lorsque plusieurs dépôts appartiennent au même périmètre de confiance, utilisent les mêmes règles de compilation et peuvent partager un environnement macOS après validation. Ce choix réduit l’administration répétée, mais augmente l’importance de l’inventaire des projets autorisés.

Choisissez un runner de projet lorsque le dépôt possède un cycle de publication particulier, un accès réseau spécifique, une équipe différente ou des secrets qui ne doivent pas cohabiter avec ceux d’un autre projet. Le coût opérationnel est plus élevé, mais la frontière est plus facile à expliquer et à auditer.

Ne confondez pas portée et isolation :

  • la portée indique quels projets peuvent demander le runner ;
  • l’étiquette indique quel type de tâche il accepte ;
  • la protection limite certaines branches ou étiquettes ;
  • le compte macOS détermine les fichiers et processus accessibles ;
  • le Keychain contrôle certains éléments protégés ;
  • le Mac physique ou distant constitue la frontière la plus nette pour les domaines de confiance différents.

Sixième étape : décider après la première semaine d’exploitation

Pendant la période d’observation, consignez les files d’attente, les conflits de concurrence, les résultats du nettoyage, les processus persistants, la reprise après redémarrage et l’occupation du nœud de publication. Ne transformez pas ces observations en durée, capacité ou coût générique : ces conclusions doivent provenir de vos journaux d’entreprise ou d’une mesure explicitement documentée.

Utilisez ensuite cette liste de décision :

  • Si tous les projets partagent le même domaine de confiance, n’exécutent que du code contrôlé et réussissent les tests de nettoyage, alors maintenez le pool Mac partagé pour les compilations et les tests.
  • Si un projet possède un accès réseau ou des responsables différents, alors déplacez-le vers un compte de service distinct et réévaluez l’usage d’un Mac Runner dédié.
  • Si une demande de fusion peut exécuter du code non approuvé, alors refusez le pool partagé et utilisez un nœud isolé, ou empêchez ce type de pipeline de rejoindre le Mac.
  • Si la signature, l’archivage officiel ou la publication sont concernés, alors utilisez le pool de publication dédié, même si la compilation ordinaire reste partagée.
  • Si la file d’attente du pool partagé retarde régulièrement les projets critiques selon vos propres journaux, alors ajoutez une capacité distante temporaire avant de procéder à un achat matériel.
  • Si la demande est stable, prévisible et durable, alors comparez l’achat d’un Mac dédié avec la location ; sinon, privilégiez une capacité louée et ajustable pendant la phase de validation.

Pour une équipe qui ne possède pas encore de capacité Mac interne, vous pouvez examiner les options de Mac distant de VMSPIN comme banc d’essai séparé. La page de tarification VMSPIN doit être utilisée pour établir le coût réel de votre scénario, sans remplacer la mesure de vos files d’attente, de vos cycles de signature et de vos exigences de conservation.

Le contrôle final doit couvrir toute la chaîne, pas seulement le runner en ligne

Avant l’ouverture à tous les projets, faites passer un flux complet :

  1. le dépôt autorisé est récupéré par le bon runner ;
  2. le projet est compilé par xcodebuild dans le pool prévu ;
  3. les tests et les artefacts intermédiaires sont produits ;
  4. l’artefact est transmis au nœud de publication sans exposer inutilement le dépôt ;
  5. la signature est exécutée uniquement par une tâche protégée ;
  6. l’artefact final est envoyé vers la destination prévue ;
  7. les répertoires, processus, caches temporaires et secrets sont traités après le job ;
  8. un redémarrage permet de répéter la chaîne avec les mêmes contrôles.

La syntaxe GitLab doit rester minimale et lisible ; référez-vous à la documentation YAML officielle de GitLab pour éviter de transformer des règles de routage en logique difficile à auditer. La preuve finale est constituée des journaux de pipeline, des résultats de refus, des contrôles de nettoyage, des essais de signature et des traces de récupération après incident.

Partagé ou dédié : le choix matériel doit suivre la frontière de confiance

Une seule machine qui compile, traite des demandes de fusion non fiables et conserve des clés de production concentre trois risques : lecture de données entre projets, persistance de processus ou de caches, et accès excessif aux identifiants de signature. Les étiquettes peuvent orienter les jobs, mais elles ne corrigent pas cette concentration.

Un pool partagé est pertinent pour des compilations internes répétitives et contrôlées. Un nœud dédié est justifié pour la publication, les projets de confiance différents et les secrets dont la compromission aurait une portée commerciale ou réglementaire. La formule mixte — capacité partagée pour le volume et Mac distant dédié pour la signature — évite de traiter tous les jobs comme s’ils avaient le même risque.

Si votre solution actuelle repose sur une seule machine Mac, elle cumule souvent des files d’attente difficiles à distinguer, des nettoyages insuffisamment prouvés et une frontière fragile entre compilation et publication. L’achat de plusieurs Mac règle une partie de la séparation, mais impose aussi immobilisation, maintenance et capacité parfois inutilisée. Pour un pilote ou une charge variable, louer auprès de VMSPIN des Mac distants distincts permet de tester séparément le pool de compilation et le pool de publication avant de figer votre architecture ou de lancer un achat en volume.

Pour commencer, demandez deux environnements d’essai correspondant à vos deux niveaux de risque, puis faites passer les mêmes tests de routage, de contamination, de signature et de reprise. C’est cette preuve, plutôt que le simple état « en ligne » du GitLab Runner, qui doit décider si votre équipe peut continuer à partager le pool ou doit renforcer l’isolation.