Adoptez une architecture à deux couches : un LaunchDaemon pour les contrôles système et la récupération de l’hôte, puis un LaunchAgent exécuté par un compte CI dédié pour le build, la signature et le simulateur. Cette séparation convient dès qu’un CI Agent dépend d’un Keychain utilisateur, d’un répertoire personnel ou d’une session graphique ; un service système seul ne doit pas être considéré comme une machine de build opérationnelle.
Plan d’action cette semaine : vérifiez d’abord le contexte d’exécution de l’agent, puis testez séparément le redémarrage, le déverrouillage FileVault, l’ouverture de session, l’enregistrement auprès du serveur CI et un build signé réel. Si l’un de ces maillons dépend d’une intervention manuelle, la machine n’est pas encore prête pour un fonctionnement sans surveillance.
Cet article s’adresse aux responsables IT qui doivent maintenir une machine de build Mac disponible après un redémarrage, aux équipes chargées de l’intégration iOS et macOS qui utilisent la signature ou le simulateur, ainsi qu’aux responsables des achats qui évaluent un nœud distant avec des droits d’administration, une capacité de redémarrage et une procédure de reprise vérifiable.
Le contexte d’exécution comme premier critère
launchd ne décrit pas une seule manière de démarrer un processus. Le domaine dans lequel le service est chargé détermine son identité, son environnement, son moment de démarrage et les ressources auxquelles il peut accéder. La documentation Apple distingue notamment les services système, exécutés dans le contexte de l’ordinateur, et les agents associés à une session utilisateur dans la documentation officielle de création des tâches launchd.
| Mode | Contexte | Déclenchement habituel | Usage adapté | Limite principale |
|---|---|---|---|---|
| LaunchDaemon | Système | Démarrage du système ou événement prévu par la configuration | Contrôle de santé, surveillance, préparation et récupération de l’hôte | Ne fournit pas automatiquement une session utilisateur exploitable |
| LaunchAgent global | Session utilisateur, selon le chargement du système | Ouverture de session des utilisateurs concernés | Agent partagé lorsque la politique d’entreprise le justifie | Le compte effectif et les droits doivent être vérifiés avec soin |
| LaunchAgent utilisateur | Session d’un compte déterminé | Ouverture de session du compte CI | Build signé, accès au Keychain, simulateur et outils liés à l’utilisateur | Ne démarre pas comme un service utilisateur complet avant l’ouverture de session |
Cette distinction impose de séparer trois états souvent confondus :
- machine démarrée : le système répond et certains services réseau sont disponibles ;
- session utilisateur établie : le compte prévu a ouvert une session et son environnement est exploitable ;
- build de production réussi : le CI Agent est enregistré, les secrets sont accessibles selon la politique prévue et un pipeline représentatif aboutit.
Un serveur SSH accessible prouve seulement le premier état, parfois une partie du second. Il ne prouve ni la disponibilité du Keychain, ni l’autorisation de signature, ni la présence du simulateur, ni la réception d’une tâche par l’agent.
Le choix launchd pour une machine de build Mac
Pour une machine exclusivement consacrée aux compilations non signées, un service système peut parfois suffire, à condition que l’outil utilisé documente précisément ce mode. Pour une chaîne iOS comportant signature, profils, Keychain temporaire ou simulateur, le mode utilisateur est généralement nécessaire. Le choix ne doit toutefois pas être déduit du seul nom du logiciel : le guide d’installation du CI Agent concerné doit être relu avant le déploiement.
Le cas documenté de GitLab Runner est utile comme point de contrôle, sans devenir une règle universelle : son installation macOS s’appuie sur un mode utilisateur LaunchAgent, et non sur une conversion automatique en LaunchDaemon selon la documentation officielle d’installation sur macOS. Un autre agent peut avoir un installeur, un compte de service ou un modèle de session différent.
Mac construit-il après un redémarrage si SSH répond ?
Non. SSH confirme que l’hôte est joignable et que le service SSH accepte une connexion autorisée. Il ne confirme pas que le compte CI a ouvert une session, que l’agent est rattaché au bon utilisateur ou qu’une opération de signature peut être exécutée.
Les dépendances de session qui font échouer le build
Le choix de LaunchAgent ne tient pas à une préférence de configuration. Il découle des dépendances réelles du pipeline. Une équipe doit inventorier ces dépendances avant de choisir le domaine launchd.
Keychain et signature
La signature de code nécessite un accès cohérent aux certificats, aux clés privées et aux profils utilisés par le pipeline. Le Keychain temporaire créé pendant le build peut également devoir être déverrouillé, rendu accessible au processus concerné, puis supprimé après utilisation. Apple décrit plusieurs modèles de Keychain et leurs contraintes dans la note technique officielle sur les Keychains macOS.
Un LaunchDaemon lancé dans le contexte système ne doit donc pas être présenté comme équivalent à un processus exécuté dans la session du compte CI. Même lorsque le fichier de certificat est présent sur le disque, les autorisations, l’identité du processus, l’état de déverrouillage et les règles d’accès aux clés peuvent différer. Le diagnostic doit porter sur une signature effective, pas sur la simple présence d’un fichier .p12.
La procédure de signature doit également être rapprochée des règles Apple relatives à la création de code signé pour la distribution dans la documentation Xcode correspondante. Le point important pour l’architecture est le suivant : le service système peut orchestrer ou contrôler, mais la lecture des actifs de signature doit rester dans le périmètre du compte et du processus qui en ont réellement besoin.
Simulateur et services graphiques
Les tests iOS peuvent dépendre du simulateur, de services graphiques, de répertoires propres à l’utilisateur et d’un état cohérent des runtimes installés. Un service démarré avant toute session peut compiler une partie du projet tout en échouant dès qu’une étape lance le simulateur ou demande un service lié à l’utilisateur.
Les équipes audio, vidéo et design doivent prêter la même attention à ce point lorsqu’un pipeline génère des aperçus, rend des médias ou contrôle une application avec une interface graphique. Un processus techniquement « en ligne » peut rester inutilisable si le contexte de session requis pour l’outil créatif n’existe pas.
Pourquoi un LaunchDaemon n’accède-t-il pas correctement au Keychain ?
Parce qu’il s’exécute dans un contexte système qui n’est pas nécessairement celui du compte ayant ouvert le Keychain et possédant les clés. Le problème peut aussi venir des autorisations accordées à la clé, de l’absence de déverrouillage après le démarrage ou d’une politique de sécurité. Il faut vérifier l’identité effective, le contexte de session et une signature réelle, au lieu d’ajouter simplement des droits root.
Configuration minimale et identité du processus
La configuration doit rester suffisamment petite pour que chaque propriété puisse être expliquée. Un fichier de démonstration peut ressembler à ceci, sans prétendre remplacer la configuration du CI Agent :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.exemple.ci-agent</string>
<key>ProgramArguments</key>
<array>
<string>/chemin/vers/ci-agent</string>
<string>run</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/chemin/vers/logs/ci-agent.out.log</string>
<key>StandardErrorPath</key>
<string>/chemin/vers/logs/ci-agent.err.log</string>
</dict>
</plist>
Ce fragment illustre le label, le programme, le lancement et les journaux ; il ne résout ni l’ouverture de session, ni la gestion des secrets, ni les contraintes de l’agent choisi. KeepAlive ne transforme pas un processus défaillant en service sain : il peut relancer indéfiniment un agent mal configuré et masquer la cause du problème.
Point de sécurité : un compte CI dédié limite le périmètre d’un jeton, d’un espace de travail et d’une clé de signature. Un compte administrateur partagé rend plus difficile l’attribution des actions et élargit les conséquences d’une compromission.
La reprise après redémarrage comme chaîne de preuves
Une reprise fiable ne se résume pas à la présence d’un processus dans la liste système. La validation doit suivre la chaîne complète : hôte accessible, disque disponible, session établie, agent enregistré, secrets utilisables et pipeline réussi.
FileVault et intervention au démarrage
Lorsque FileVault protège le volume de démarrage, le redémarrage peut introduire une étape de déverrouillage avant que le système ne puisse poursuivre normalement. Les conditions d’authentification et de récupération doivent être examinées dans la documentation Apple sur la récupération d’un mot de passe FileVault et sur la sécurité du démarrage et du volume.
La conclusion opérationnelle ne doit pas être « FileVault empêche toujours l’automatisation » ou « FileVault se déverrouille toujours seul ». Elle dépend de la politique de l’entreprise, du matériel, du mode de récupération et du contrôle autorisé au moment du redémarrage. Une machine qui attend une saisie locale ne peut pas être vendue comme un nœud totalement autonome sans procédure documentée.
Comment restaurer un build sans surveillance lorsque FileVault est activé ?
Il faut d’abord déterminer si le parcours de déverrouillage peut être réalisé par la politique et l’outillage retenus, sans exposer une clé de récupération dans un script. Si une intervention humaine est obligatoire, l’architecture doit l’indiquer comme une étape de reprise ; il faut alors prévoir une astreinte, un accès distant approuvé ou un nœud de secours, plutôt que de déclarer la reprise automatique acquise.
Les scénarios à exécuter
Chaque scénario doit produire une preuve conservée avec la version du système, de l’agent et de la configuration :
- démarrage à froid avec le disque déjà déverrouillé ;
- redémarrage planifié depuis une session administrée ;
- redémarrage inattendu ou perte d’alimentation simulée selon la politique autorisée ;
- fermeture de la session du compte CI ;
- déverrouillage FileVault suivi d’une ouverture de session ;
- relance après arrêt volontaire du CI Agent ;
- rotation contrôlée d’un certificat ou d’un profil de signature.
Après chaque scénario, la commande de diagnostic doit être adaptée au domaine chargé et au compte concerné. Par exemple :
launchctl print system/com.exemple.ci-health
launchctl print gui/$(id -u)/com.exemple.ci-agent
ps -axo user,pid,command | grep '[c]i-agent'
La sortie attendue doit permettre de répondre à quatre questions : quel domaine a chargé le service, sous quelle identité il tourne, quand il a été relancé et quel journal explique son dernier état. Le statut « online » affiché par une console CI ne suffit pas si aucun build représentatif n’a été accepté.
Le contrôle d’un agent réellement opérationnel
Le test final doit utiliser un projet ou une branche de validation qui reproduit les étapes de production sans publier un artefact réel. Il doit au minimum :
- récupérer le code avec les mêmes mécanismes que la production ;
- sélectionner la version d’Xcode et le runtime du simulateur requis ;
- créer ou ouvrir le Keychain selon la procédure approuvée ;
- signer un artefact de test ;
- exécuter un test de simulateur si le pipeline en dépend ;
- transmettre les journaux et le résultat au serveur CI ;
- nettoyer les certificats, profils et fichiers temporaires.
Quels redémarrages faut-il valider avant de mettre un CI Agent Mac en production ?
Il faut valider au moins un redémarrage planifié, un redémarrage après perte inattendue, une fermeture de session et un parcours avec FileVault lorsque cette protection est activée. Chaque résultat doit inclure l’accessibilité de l’hôte, la session, l’enregistrement de l’agent, l’accès contrôlé au Keychain et la réussite d’un build réel.
La séparation des privilèges et des secrets
Le modèle recommandé comporte deux responsabilités distinctes.
Le LaunchDaemon peut surveiller l’espace disque, la connectivité, la présence de certains processus, l’état des journaux et la disponibilité générale de l’hôte. Il peut déclencher une action de récupération documentée, sans lire les certificats de distribution ni les jetons de publication.
Le LaunchAgent du compte CI exécute le travail qui exige le contexte utilisateur. Il doit disposer du minimum nécessaire pour son espace de travail, son Keychain et les outils de compilation. Les accès réseau sortants, les répertoires persistants et les secrets doivent être définis par la politique de l’entreprise, puis testés avec un compte qui n’est pas administrateur partagé.
Cette séparation réduit la portée d’un incident, mais elle ne remplace pas une analyse de menace. Un agent compromis peut toujours utiliser les permissions de son compte pendant la durée de son exécution. Les jetons doivent donc être limités au projet et à la durée utile, les journaux doivent éviter les secrets, et les espaces de travail doivent être nettoyés après un job sensible.
L’observabilité et la maintenance continue
Une équipe d’exploitation doit pouvoir distinguer trois causes qui produisent le même symptôme « pipeline hors ligne » :
- le processus a quitté ou boucle sous l’effet de
KeepAlive; - la session utilisateur n’existe plus ou n’a pas été créée ;
- l’agent fonctionne mais ne peut pas joindre le serveur, récupérer le code ou accéder à son Keychain.
Les journaux doivent donc conserver le label launchd, le compte d’exécution, l’heure du dernier démarrage, le code de sortie, le nombre de relances, la version de l’agent et le résultat du dernier build de contrôle. Une rotation doit empêcher qu’un incident ancien soit écrasé par des relances répétées.
Les mises à niveau du système, de Xcode, du CI Agent et des fichiers de configuration doivent passer par un nœud isolé. Le test ne doit pas seulement vérifier le lancement du processus : il doit reprendre la signature, le simulateur, le nettoyage et l’enregistrement auprès du serveur CI. Si un changement exige une ouverture de session manuelle, cette condition doit être inscrite dans le plan de maintenance.
La grille d’acceptation avant production
La liste suivante peut être copiée dans un ticket de changement. Chaque case doit être associée à une preuve : sortie de commande, journal, identifiant de pipeline ou compte rendu d’intervention.
- [ ] Le domaine
launchdutilisé par le CI Agent est documenté et conforme au guide officiel de l’agent. - [ ] Le compte d’exécution est un compte CI dédié, non un administrateur partagé.
- [ ] La machine répond après un redémarrage planifié et après un redémarrage inattendu simulé.
- [ ] Le parcours FileVault est documenté, y compris l’intervention humaine éventuelle.
- [ ] La session utilisateur requise est effectivement établie après la reprise.
- [ ] Le CI Agent s’enregistre automatiquement auprès du serveur.
- [ ] Un Keychain de validation est accessible sans exposer de secret dans les journaux.
- [ ] Une signature de test réussit avec les mêmes contraintes que la production.
- [ ] Le simulateur requis démarre et exécute un test représentatif.
- [ ] Les journaux indiquent le compte, le domaine, le code de sortie et la version de l’agent.
- [ ] Un arrêt volontaire de l’agent déclenche le comportement prévu et reste observable.
- [ ] Une procédure de retour arrière a été testée sur un nœud isolé.
- [ ] Un nœud de secours ou une capacité de remplacement est prévu lorsque la reprise locale exige une intervention.
- [ ] La suppression des certificats, profils et fichiers temporaires est vérifiée après le job.
Règle d’acceptation : si la console indique « agent connecté » mais qu’un build signé échoue après redémarrage, le nœud doit rester hors production. La disponibilité de l’agent est un indicateur intermédiaire, pas la preuve du service rendu.
La décision d’architecture et de capacité
Les trois modèles suivants permettent de qualifier une architecture existante avant d’acheter ou de louer un nouveau nœud.
| Architecture | Cas acceptable | Motif de refus | Exigence de reprise |
|---|---|---|---|
| LaunchAgent seul | Petit nœud dont la session utilisateur et la reprise sont garanties | Surveillance système faible, intervention difficile ou absence de contrôle externe | Vérifier session, agent, Keychain et build signé |
| LaunchDaemon seul | Contrôles système, supervision et builds ne dépendant d’aucun contexte utilisateur | Signature, simulateur, Keychain ou répertoire utilisateur requis | Démontrer que l’outil supporte officiellement ce mode |
| Double couche | Environnement de production avec signature, simulateur, surveillance et besoin de reprise | Complexité supérieure si les responsabilités ne sont pas documentées | Tester la récupération système puis le démarrage utilisateur |
| Indicateur | Preuve minimale | Décision |
|---|---|---|
| Hôte joignable | Connexion réseau et accès d’administration approuvé | Nécessaire, mais insuffisant |
| Session CI | Processus et environnement du compte attendu | Requis pour un build utilisateur |
| Agent enregistré | Journal local et réception d’un job de validation | Requis avant la mise en service |
| Keychain | Signature effective avec journal sans secret | Bloquant si absent |
| Simulateur | Test représentatif exécuté après reprise | Bloquant pour les pipelines concernés |
| Build de production simulé | Artefact de validation et statut transmis au serveur CI | Preuve finale de fonctionnement |
| Secours | Procédure de remplacement ou capacité indépendante | Requis si une intervention locale est nécessaire |
| Situation constatée | Choix recommandé | Justification d’achat ou d’exploitation |
|---|---|---|
| Build sans signature ni simulateur | LaunchDaemon possible après validation du fournisseur de l’agent | Le contexte utilisateur n’est pas nécessairement requis |
| Signature et Keychain utilisateur | LaunchAgent d’un compte CI dédié | Les secrets restent dans le périmètre du compte de build |
| Tests iOS avec simulateur | LaunchAgent, avec session contrôlée | Le simulateur et les outils graphiques doivent être testés dans ce contexte |
| Surveillance et récupération de l’hôte | LaunchDaemon en complément | Le système peut contrôler sans lire les secrets de publication |
| FileVault nécessitant une intervention | Double couche plus procédure d’astreinte ou nœud de secours | L’automatisation complète n’est pas démontrée |
| Nœud distant sans redémarrage vérifiable | Ne pas le retenir comme capacité critique | Le contrat ou le fournisseur ne couvre pas toute la chaîne de reprise |
Un Mac existant peut être conservé si les deux couches sont séparables, si le parcours FileVault est maîtrisé et si les preuves de build sont reproductibles. Il faut plutôt prévoir un nouveau nœud lorsque l’accès dépend d’un compte administrateur partagé, que le redémarrage exige une intervention locale non planifiée, que les secrets sont stockés dans un contexte système trop large ou qu’aucune capacité de secours n’est disponible.
Pour une équipe qui compare plusieurs implantations, la question ne porte donc pas uniquement sur la puissance de la machine. Elle porte sur la possibilité d’obtenir un accès administrateur contrôlé, un redémarrage distant vérifiable, une session utilisateur indépendante et une procédure de remplacement. Les options de Mac distant pour infrastructure de développement peuvent servir de point de comparaison, tandis qu’un Mac mini cloud avec accès distant doit être évalué avec cette même grille de preuves, et non avec la seule promesse d’une machine en ligne.
Une infrastructure achetée en interne conserve des avantages lorsque l’entreprise exige une présence physique, une intégration réseau particulière ou une charge stable sur une longue période. Elle impose toutefois l’achat initial, le remplacement matériel, la gestion de l’alimentation, la capacité de secours et le temps d’intervention lors d’un blocage FileVault. Une solution distante mal documentée peut, à l’inverse, déplacer le problème vers le fournisseur si elle ne garantit ni les droits, ni le redémarrage, ni l’isolement des comptes. Pour un besoin temporaire, un pilote CI ou une capacité de débordement, louer un Mac auprès de NodeMini devient pertinent si l’équipe peut vérifier ces éléments avant l’intégration en production.
La décision finale doit donc rester conditionnelle : conservez ou achetez un Mac lorsque le contrôle physique et la charge durable justifient l’investissement ; choisissez une ressource distante lorsque la priorité est une capacité rapidement disponible, remplaçable et testable, avec accès complet et preuves de reprise. Dans les deux cas, la règle ne change pas : un processus lancé ne vaut pas un build signé réussi après redémarrage.