Lorsqu’un Mac cloud exécuté en continu enchaîne les builds de plusieurs dépôts, le principal risque n’est pas toujours qu’un jeton apparaisse directement dans un script. Le danger vient souvent d’un job qui laisse discrètement ses identifiants au suivant. Les résidus les plus courants se trouvent dans la configuration Git globale, les URL distantes contenant des données d’authentification, le trousseau de session et les scripts temporaires qui n’ont pas été supprimés. L’objectif n’est donc pas simplement de « supprimer une variable à la fin du build », mais de faire en sorte que les identifiants ne puissent exister que dans les limites d’un seul job.
Commencer par repérer où les identifiants sont stockés
Ne modifiez pas immédiatement le pipeline. Commencez par déterminer quelles configurations Git lit réellement, sans afficher la valeur des secrets. L’option --show-origin indique également la source de chaque configuration, ce qui permet de distinguer les réglages système, utilisateur et propres au dépôt.
set +x
git config --show-origin --get-all credential.helper || true
git config --show-origin --get-regexp '^(credential|http)\.' || true
git config --show-origin --get-regexp '^url\..*\.insteadof$' || true
Recherchez en priorité trois types de signaux :
- si
credential.helperpointe vers un trousseau persistant ; - si
http.extraHeadera été enregistré dans la configuration globale ou celle du dépôt ; - si
url.*.insteadOftransforme une URL ordinaire en une URL contenant des données d’authentification.
Les URL distantes doivent également être contrôlées, mais jamais consignées telles quelles dans les journaux. Il suffit de détecter la présence d’une structure de type protocole://informations-utilisateur@hôte et de faire échouer immédiatement le job, sans afficher l’adresse complète.
remote_url="$(git remote get-url origin)"
case "$remote_url" in
*://*@*)
printf '%s
' "Remote URL contains embedded credentials" >&2
exit 1
;;
esac
La première règle d’un script d’audit consiste à signaler uniquement que des identifiants peuvent être présents. Il ne faut jamais afficher les identifiants eux-mêmes dans les journaux de build pour démontrer l’existence du problème.
Créer un HOME isolé pour chaque job
Git déduit de HOME le chemin de la configuration utilisateur. Si tous les jobs partagent le HOME du compte qui exécute le runner, ils partagent aussi .gitconfig, les réglages des credential helpers et de nombreux états d’outils. Une approche plus sûre consiste à créer pour chaque job un HOME temporaire doté des permissions 700, puis à désigner explicitement le fichier de configuration Git globale.
set -eu
ORIGINAL_HOME="$HOME"
JOB_HOME="$(mktemp -d "${TMPDIR%/}/git-job.XXXXXX")"
chmod 700 "$JOB_HOME"
export HOME="$JOB_HOME"
export XDG_CONFIG_HOME="$JOB_HOME/.config"
export GIT_CONFIG_GLOBAL="$JOB_HOME/.gitconfig"
export GIT_TERMINAL_PROMPT=0
mkdir -p "$XDG_CONFIG_HOME"
Cette méthode n’isole pas la configuration Git système. Il faut donc toujours réinitialiser explicitement la chaîne des helpers. Git accepte plusieurs valeurs credential.helper. L’ajout initial d’une valeur vide neutralise les helpers hérités des configurations de priorité inférieure, avant d’ajouter l’implémentation propre au job en cours.
Ne pas placer le dépôt dans le HOME temporaire
HOME définit la frontière des identifiants et de l’état des outils ; il n’a pas à servir également d’espace de travail. Les sources doivent rester dans l’espace de travail géré par le runner afin de faciliter le contrôle de l’espace disque et la collecte des artefacts. En séparant les deux, la suppression de HOME n’efface pas les résultats du build, tandis que le nettoyage de l’espace de travail ne laisse pas de configuration d’authentification utilisateur.
Si le pipeline exécute plusieurs jobs en parallèle, chaque emplacement concurrent doit appeler mktemp séparément. Il ne faut pas réutiliser un répertoire fixe construit à partir du nom du dépôt. Après un arrêt anormal, ce répertoire pourrait être récupéré par le job suivant. Deux jobs pourraient aussi modifier simultanément le même fichier .gitconfig.
Fournir le jeton uniquement lorsque Git le demande
Le jeton doit être injecté dans l’environnement par le mécanisme de variables secrètes du système CI, et le script ne doit faire référence qu’au nom de la variable. N’intégrez pas le jeton dans l’URL de clone et n’enregistrez pas durablement un en-tête d’authentification avec git config --global http.extraHeader.
Le helper ci-dessous répond uniquement aux requêtes get de Git. Le fichier de configuration contient la logique de référence aux variables, et non la valeur du jeton :
: "${CI_GIT_USER:?CI_GIT_USER is required}"
: "${CI_GIT_TOKEN:?CI_GIT_TOKEN is required}"
git config --global credential.helper ""
git config --global --add credential.helper \
'!f() {
if [ "$1" = get ]; then
printf "username=%s
password=%s
" \
"$CI_GIT_USER" "$CI_GIT_TOKEN"
fi
}; f'
Désactiver la trace des commandes et le repli interactif
Exécutez impérativement set +x avant d’accéder aux variables secrètes. Sinon, le shell peut écrire dans le journal les commandes après expansion des variables. GIT_TERMINAL_PROMPT=0 est tout aussi important : si le jeton est absent ou invalide, le job doit échouer explicitement au lieu de rester bloqué sur une invite interactive invisible.
Vérifiez également si les wrappers de build exportent automatiquement le contenu des variables d’environnement. Les diagnostics doivent seulement indiquer si une variable est définie, par exemple en vérifiant que sa longueur est supérieure à zéro. Ils ne doivent afficher ni son contenu, ni les en-têtes d’authentification, ni les URL distantes complètes.
Pour les automatisations nécessitant des droits d’écriture, utilisez des identifiants distincts pour lire le code source et pousser les artefacts. Un job limité au checkout n’a pas besoin de droits d’écriture. De même, un job de publication ne doit disposer d’aucune autorisation au-delà du dépôt cible et des opérations strictement nécessaires.
Utiliser trap pour couvrir tous les chemins de sortie
Un simple rm -rf placé à la fin du script ne couvre ni les échecs intermédiaires, ni les arrêts dus à un délai d’attente, ni les annulations manuelles. Enregistrez un trap immédiatement après la création du répertoire temporaire. Dans la fonction de nettoyage, commencez par supprimer les variables, puis effacez le HOME du job.
cleanup_git_credentials() {
set +e
unset CI_GIT_TOKEN CI_GIT_USER
if [ -n "${JOB_HOME:-}" ] && [ -d "$JOB_HOME" ]; then
rm -rf "$JOB_HOME"
fi
}
trap cleanup_git_credentials EXIT HUP INT TERM
La fonction de nettoyage doit pouvoir être exécutée plusieurs fois et ne pas échouer si le répertoire n’existe plus. La cible de suppression doit également respecter deux conditions : la variable ne doit pas être vide et elle doit réellement désigner un répertoire. N’utilisez pas de commande générale de suppression du trousseau et ne videz pas tout le trousseau de session, car celui-ci peut contenir d’autres éléments nécessaires au même compte d’exécution.
Si un ancien pipeline utilisait un helper persistant, commencez par en dresser un inventaire précis par hôte et par compte, puis planifiez une migration ponctuelle. Tant que les anciennes et nouvelles méthodes coexistent, chaque job doit vérifier l’origine des helpers actifs. Cela évite qu’un wrapper système recopie l’ancienne configuration alors que le HOME temporaire est déjà en place.
Faire de la détection des résidus une condition du build
L’absence d’erreur dans le script ne suffit pas à prouver que le nettoyage a réussi. Ajoutez un contrôle après la sortie autour du runner, ou demandez à l’exécuteur de vérifier les éléments suivants lors de la récupération du job :
- le HOME temporaire a été supprimé ;
- le fichier
.git/configde l’espace de travail ne contient ni en-tête d’authentification ni URL distante avec des informations utilisateur ; - le journal du job ne contient aucune empreinte connue des variables secrètes ;
- les sources de configuration Git se limitent à la configuration système attendue et à la configuration temporaire du job en cours ;
- un job vierge exécuté ensuite ne peut pas lire les identifiants de dépôt du job précédent.
Pour les tests, attribuez au jeton une valeur de marquage fixe et sans aucune permission, exécutez des scénarios d’échec dans un environnement isolé, puis analysez les journaux et le système de fichiers. Le but est de vérifier si le marqueur a fui, et non de valider un véritable jeton. Les scénarios d’échec doivent au minimum couvrir un échec du clone, l’arrêt d’une commande de build, la réception d’un signal de terminaison et l’exécution répétée de la fonction de nettoyage.
Lorsque plusieurs équipes partagent un même nœud physique, le compte d’exécution doit constituer une deuxième frontière. Le HOME temporaire traite les résidus au niveau du job ; des comptes distincts isolent les processus, les permissions de fichiers et les trousseaux entre différents domaines de confiance. Ces deux niveaux sont nécessaires pour que le nettoyage par script ne soit pas l’unique ligne de défense.
Les critères d’acceptation finaux sont simples : aucun identifiant ne doit pouvoir être hérité avant le démarrage du job ; pendant l’exécution, ils ne sont fournis qu’à la demande ; tous les chemins de sortie déclenchent le nettoyage ; et le job suivant ne peut pas démontrer que le jeton précédent a existé. Ce n’est qu’à ces conditions que les identifiants Git appartiennent réellement au job, et non au Mac cloud exécuté en continu.
Questions fréquentes
Pourquoi supprimer la variable du jeton en fin de job ne suffit-il pas ?
Le jeton peut déjà se trouver dans une URL distante, un helper, la configuration globale ou le trousseau. Ces emplacements doivent être contrôlés et nettoyés séparément.
Un worker CI Mac permanent peut-il partager le trousseau de session ?
Il vaut mieux séparer les dépôts et niveaux de confiance. Utilisez des jetons courts à privilèges minimaux, puis des utilisateurs ou trousseaux dédiés si une persistance est nécessaire.
Choisissez un serveur physique dédié pour le développement, les builds et le bureau à distance
Comparez trois configurations Apple Silicon et choisissez le nœud, la durée de location et les options de stockage lors de la commande.