Tokens d'API Jenkins : créer, restreindre, renouveler et révoquer
En quoi les tokens d'API Jenkins diffèrent des mots de passe, comment en créer un, ce qu'on peut — et ne peut pas — restreindre, et comment les stocker et les renouveler sans exposer ton serveur de CI.
Écrit par Loris Siegenthaler · Créateur de BuildCaptainUn token d'API est l'identifiant que tu confies à tout ce qui n'est pas un humain devant un navigateur : un script, un pipeline de déploiement, un outil de supervision, un client de bureau. Jenkins les rend faciles à créer, et c'est pourquoi tant d'entre eux finissent collés dans un script shell au fond d'un dépôt, encore valides trois ans plus tard.
Voici le cycle de vie complet — ce qu'est vraiment un token, comment en créer un, la limite de périmètre dont personne ne te prévient, et comment renouveler et révoquer sans rien casser.
Tokens contre mots de passe
Les deux fonctionnent pour l'authentification HTTP basique auprès de Jenkins, donc sur le réseau ils sont identiques. Ce qui diffère, c'est tout le reste.
- Les tokens se révoquent individuellement. Un token par consommateur veut dire que tu peux tuer celui qui a fuité sans changer le mot de passe que tous les autres consommateurs utilisent.
- Les tokens survivent au SSO. Si ton Jenkins s'authentifie via SAML, OIDC, LDAP ou GitHub OAuth, il n'y a souvent aucun mot de passe à envoyer — le token est le seul identifiant qui fonctionne pour un accès non interactif.
- Les tokens se passent du crumb CSRF. Jenkins protège les requêtes qui modifient l'état avec un crumb (son token CSRF), et la documentation est explicite : les requêtes authentifiées par token d'API en sont exemptées. C'est pourquoi
curl -X POSTavec un token marche du premier coup, alors que la même requête avec un cookie de session prend un 403 "No valid crumb was included" à moins d'aller d'abord en chercher un sur/crumbIssuer/api/json. Les recommandations d'accès distant de Jenkins préfèrent les tokens au jonglage de crumbs exactement pour cette raison. - Les tokens sont traçables. Chacun porte le nom que tu lui as donné, une date de création et un compteur d'utilisation, si bien que tu vois lesquels servent vraiment et lesquels ont été créés pour un script qui n'existe plus.
Ce qu'un token n'est pas, c'est un identifiant à privilèges réduits. Voir la section sur le périmètre plus bas — c'est là que beaucoup se font avoir.
Créer un token
Clique sur ton nom en haut à droite, puis sur Security dans la barre latérale (les versions plus anciennes appellent la page Configure). /me/security est le raccourci sur n'importe quel Jenkins. Sous API Token, choisis Add new token, donne-lui un nom qui dit qui l'utilise — deploy-script, grafana, buildcaptain-laptop, pas token1 — et clique sur Generate.
Copie la valeur maintenant. Jenkins le dit lui-même : « Copy this token now, because it cannot be recovered in the future. » Il n'en stocke qu'un hash.
Un administrateur peut faire la même chose pour un compte de service dont il n'a pas le mot de passe, sur /user/<username>/security (via Manage Jenkins → Security → Users). C'est comme ça qu'on provisionne un compte de bot sans jamais se connecter avec.
On peut aussi créer des tokens sans passer par l'UI, ce que veulent les scripts de provisionnement :
curl -X POST -u "alice:$JENKINS_ADMIN_TOKEN" \
"https://jenkins.example.com/user/jenkins-bot/descriptorByName/jenkins.security.ApiTokenProperty/generateNewToken" \
--data "newTokenName=deploy-script"
L'endpoint n'accepte que POST. La réponse est du JSON avec un objet data contenant tokenName, tokenUuid et tokenValue. Garde l'UUID — c'est lui que prend l'appel de révocation correspondant :
curl -X POST -u "alice:$JENKINS_ADMIN_TOKEN" \
"https://jenkins.example.com/user/jenkins-bot/descriptorByName/jenkins.security.ApiTokenProperty/revoke" \
--data "tokenUuid=$TOKEN_UUID"
Utilise /me/… à la place de /user/<id>/… pour ton propre compte. Dans les deux cas, il faut être cet utilisateur ou un administrateur Jenkins.
Utiliser un token
Tout dans Jenkins a une représentation JSON — ajoute /api/json à presque n'importe quelle URL que tu peux ouvrir dans un navigateur.
# Who am I, and what can I see?
curl -u "jenkins-bot:$JENKINS_TOKEN" \
"https://jenkins.example.com/api/json?pretty=true"
# The last five builds of one job, only the fields you need
curl -u "jenkins-bot:$JENKINS_TOKEN" \
"https://jenkins.example.com/job/my-app/api/json?tree=builds[number,result,timestamp]{0,5}"
# The console log of a specific build
curl -u "jenkins-bot:$JENKINS_TOKEN" \
"https://jenkins.example.com/job/my-app/42/consoleText"
Le paramètre tree vaut la peine d'être appris tôt. Sans lui, /api/json sur un job chargé renvoie une payload qui se mesure en mégaoctets ; avec lui, tu obtiens les quatre champs que tu as demandés.
Déclencher un build devrait être un POST. Le cœur de Jenkins accepte en fait GET sur les deux endpoints de build, mais POST est ce que la documentation utilise et ce que chaque proxy, log et relecteur attend d'un appel qui modifie l'état — et un simple GET sur le /build d'un job paramétré affiche le formulaire de paramètres au lieu de lancer quoi que ce soit — de quoi rester perplexe cinq bonnes minutes.
# A job with no parameters
curl -X POST -u "jenkins-bot:$JENKINS_TOKEN" \
"https://jenkins.example.com/job/my-app/build"
# A parameterised job
curl -X POST -u "jenkins-bot:$JENKINS_TOKEN" \
"https://jenkins.example.com/job/my-app/buildWithParameters" \
--data-urlencode "BRANCH=release/2.4" \
--data-urlencode "DEPLOY=false"
Un déclenchement réussi renvoie 201 Created avec un header Location qui pointe vers l'élément de file d'attente, pas vers un build. Le numéro de build n'existe pas encore — le build doit d'abord sortir de la file. Polle le /api/json de l'élément de file jusqu'à ce qu'un objet executable apparaisse, et lis le numéro là.
-u user:token met le token dans la liste des processus, où n'importe quel autre utilisateur de la machine peut le lire. Sur un hôte partagé, utilise curl --netrc-file avec un fichier en 0600, ou passe l'identifiant par un pipe, au lieu de l'interpoler dans la ligne de commande.Le périmètre : la partie qui surprend
Un token d'API Jenkins n'a pas de périmètre. Ce n'est pas un identifiant restreint avec une liste de permissions attachée, comme un token fine-grained de GitHub. Il s'authentifie en tant que toi, et il porte donc chaque permission de ton compte.
Si tu es administrateur Jenkins et que tu colles un token dans un script shell, ce script peut désormais reconfigurer des jobs, installer des plugins et exécuter du Groovy arbitraire sur le contrôleur. Peu importe que tout ce que tu voulais, c'était lire un statut de build.
Puisqu'il n'y a pas de périmètre par token, restreins l'utilisateur à la place. Crée un compte de service dédié par consommateur et donne-lui le minimum :
- Supervision en lecture seule —
Overall/Read,Job/ReadetView/Read. Assez pour lister les jobs, lire les résultats de builds et récupérer les logs console. - Déclencher des builds — ajoute
Job/Build, etJob/Cancelsi l'outil doit pouvoir arrêter des runs. - Jamais —
Overall/Administer,Job/ConfigureouRun/Replaysur un compte de service.Run/Replayen particulier signifie exécution de code de pipeline arbitraire.
Les deux plugins qui rendent ça praticable sont Matrix Authorization Strategy (matrix-auth — une grille utilisateurs contre permissions, au besoin par dossier ou par job) et Role-based Authorization Strategy (role-strategy — des rôles nommés appliqués à des utilisateurs et à des motifs de noms de jobs). Matrix est plus simple et suffit jusqu'à quelques dizaines de comptes ; la stratégie par rôles passe mieux à l'échelle dès que tu as de vraies équipes. Dans les deux cas, l'objectif est le même : le rayon d'impact d'un token qui fuite est ce que ce seul compte de service pouvait faire.
Les permissions par dossier sont l'astuce sous-estimée ici. Donne au compte de service Job/Read sur le seul dossier platform/, et un token qui fuite ne peut même pas énumérer le reste de tes jobs.
Renouvellement et révocation
Les tokens Jenkins n'expirent pas. Rien ne te le rappellera, alors inscris-le au calendrier — un rythme trimestriel comme base raisonnable, immédiatement au moindre soupçon, et chaque fois que quelqu'un quitte l'équipe.
Les tokens nommés font du renouvellement une simple formalité, à condition de suivre cet ordre :
- Crée un nouveau token sur le même compte, nommé d'après le même consommateur plus une date :
deploy-script-2026-08. - Déploie-le chez le consommateur et confirme qu'il fonctionne.
- Révoque l'ancien token depuis Security → API Token.
Ne révoque jamais en premier. C'est précisément pour cette fenêtre de recouvrement que les tokens nommés multiples existent.
Le compteur d'utilisation à côté de chaque token te dit si un renouvellement a vraiment pris : après une journée avec le nouveau token en place, le compteur de l'ancien doit avoir cessé de bouger. C'est aussi le moyen le plus rapide de trouver les tokens morts — tout ce qui n'a pas servi récemment est soit un script que plus personne ne lance, soit un identifiant que quelqu'un a copié puis oublié, et les deux doivent être révoqués.
Si un token a fuité, révoque d'abord — le reste vient après. Vérifie ensuite Manage Jenkins → System Log et le log d'accès de ton reverse proxy pour voir ce que ce compte a fait, et renouvelle aussi les autres tokens de ce compte — une fuite depuis un laptop partagé ou un dépôt public implique rarement exactement un seul secret.
Un administrateur peut révoquer les tokens d'un autre utilisateur depuis la page de configuration de cet utilisateur, ce qu'il te faut le jour où quelqu'un s'en va. Supprimer ou désactiver le compte lui-même invalide tous ses tokens d'un coup.
Où garder le token
Les règles, version la plus courte :
- Pas dans un dépôt. Ni dans un script, ni dans un
.env, ni dans un fichier YAML, ni dans un commentaire. Git se souvient ; réécrire l'historique après coup est un calvaire, et de toute façon un correctif incomplet. - Pas dans les logs de CI. Ne fais jamais
echod'un token, et méfie-toi deset -xdans un step shell — il imprimera joyeusement toute la commande curl. - Pas dans une note de mots de passe partagée. Tout le mécanisme repose sur le token par consommateur ; en partager un entre quatre outils le jette par la fenêtre.
Sur macOS, le bon coffre est le Keychain, et il est scriptable :
# Store it once (the -w flag prompts, so it never lands in your shell history)
security add-generic-password -a jenkins-bot -s jenkins-api-token -w
# Read it back when you need it
JENKINS_TOKEN=$(security find-generic-password -a jenkins-bot -s jenkins-api-token -w)
Dans Jenkins lui-même, un token va dans le magasin d'identifiants comme identifiant « Username with password » et en ressort avec withCredentials, qui le masque dans le log de build :
withCredentials([usernamePassword(
credentialsId: 'jenkins-bot-api',
usernameVariable: 'JENKINS_USER',
passwordVariable: 'JENKINS_TOKEN'
)]) {
sh 'curl -sS -u "$JENKINS_USER:$JENKINS_TOKEN" "$JENKINS_URL/api/json"'
}
Note les guillemets simples autour du script sh. Avec des guillemets doubles, Groovy interpole le secret dans la chaîne de commande avant même que Jenkins ne la voie, le masquage ne s'applique pas, et ton token apparaît en clair dans le log console.
Une courte checklist
- Un token nommé par consommateur, nommé d'après le consommateur.
- Des comptes de service, pas ton propre compte, pour tout ce qui est automatisé.
- Des permissions minimales via Matrix ou Role Strategy — par dossier quand tu peux.
- Un stockage dans un vrai coffre à secrets : le Keychain, le magasin d'identifiants de Jenkins ou ton gestionnaire de secrets.
- Un renouvellement au calendrier, le nouveau avant l'ancien.
- Une révocation dès qu'un consommateur est retiré ou qu'un laptop disparaît.
Si tu connectes notre propre app, la version courte de tout ceci vit dans le centre d'aide : Connecter ton serveur Jenkins détaille la création du token et sa saisie. Et pour énoncer une évidence : BuildCaptain est notre app, un client Jenkins natif pour la barre des menus macOS. L'app suit ses propres conseils — le token va dans le Keychain macOS, jamais dans un fichier de config, et un compte de service en lecture seule avec Job/Read suffit pour surveiller tes pipelines.
BuildCaptain