Quelque part dans votre environnement, il y a un script, un runner CI ou une station d’administration « temporaire » avec un mot de passe root Proxmox intégré. Ça fonctionne. Cela signifie aussi qu’une seule chaîne divulguée peut redémarrer la production, supprimer des sauvegardes et reconfigurer les réseaux avant que le café de l’astreignant ne refroidisse.
Les jetons API sont la manière d’arrêter de traiter le mot de passe root comme une clé principale que tout le monde emprunte. Bien faits, ils sont limités, révocables, traçables et ennuyeux. L’objectif est d’être ennuyeux.
Ce que vous protégez vraiment (et pourquoi les mots de passe échouent)
Proxmox n’est pas « juste un hyperviseur ». C’est votre plan de contrôle : actions d’alimentation, câblage de stockage, changements réseau, consoles VM, instantanés, réplication, sauvegardes, règles de pare-feu et identités utilisateurs. Si quelqu’un peut atteindre l’API Proxmox avec des permissions larges, il n’a pas besoin de « pirater » vos VM. Il peut devenir votre équipe plateforme.
Les mots de passe root échouent de façon prévisible :
- Ils se répandent. Un mot de passe devient un secret partagé entre humains et machines.
- Ils ne sont pas limités. Vous ne pouvez pas dire « cette automation peut seulement démarrer/arrêter ces VM et lire ces métriques » avec un mot de passe.
- Ils sont difficiles à faire tourner. La rotation casse des scripts. Les gens repoussent. Les attaquants, eux, ne le font pas.
- Ils estompent la responsabilité. « root l’a fait » n’est pas une piste d’audit, c’est un haussement d’épaules.
Les jetons API ne résoudront pas automatiquement la culture, mais ils rendent le bon comportement moins coûteux que le mauvais. Les jetons peuvent être limités et tournés sans que des humains retapent des secrets à 2h du matin. Les jetons peuvent être créés par application, par pipeline, par intégration. Les jetons peuvent mourir seuls sans entraîner toute l’organisation dans une spirale de réinitialisation de mot de passe.
Faits et contexte qui influencent les décisions
Voici des faits concrets — certains historiques, d’autres architecturaux — qui devraient impacter la façon dont vous concevez l’accès à Proxmox :
- Proxmox VE centralise le contrôle via une API REST. L’interface Web est essentiellement un client API. Si vous pouvez le faire dans l’interface, vous pouvez généralement le faire via l’API — et les scripts le feront.
- Les jetons API sont par utilisateur, pas des identités indépendantes. Dans Proxmox, les jetons sont attachés à une identité utilisateur ; votre conception RBAC reste importante.
- Le RBAC dans Proxmox s’appuie sur des rôles et des chemins ACL. Si vous ne comprenez pas le scoping par chemin (comme
/vms/100vs/), vous casserez l’automatisation ou lui donnerez trop de droits. - Le « moindre privilège » est devenu la norme parce que la sécurité périmétrique a échoué à plusieurs reprises. Une fois que les réseaux ont cessé d’être « de confiance en interne », des identifiants limités sont la seule hypothèse raisonnable.
- Le credential stuffing est ancien, fiable et ennuyeux. Les attaquants réutilisent des mots de passe divulgués parce que ça marche encore. Les mots de passe root sont particulièrement réutilisables car les humains les réutilisent de façon très humaine.
- La réponse aux incidents fonctionne par révocation. La rotation de mot de passe est lente ; la révocation d’un jeton est rapide. Le rapide gagne pendant la contention.
- L’auditabilité est une fonctionnalité, pas du théâtre conformité. Quand une VM est détruite, vous voulez « quel jeton » et « quel service » pas « quel admin peut-être ».
- L’automatisation augmente la surface d’impact si vous ne la réduisez pas délibérément. Les systèmes CI fonctionnent souvent en « cluster admin » parce que c’est facile. Facile est la manière dont les pannes deviennent multi-site.
Blague #1 : Un mot de passe root partagé, c’est comme une brosse à dents communautaire — techniquement fonctionnel, moralement discutable, et vous ne voulez pas savoir où elle a été.
Modèle de menace : les trois attaquants que vous avez réellement
Les conseils de sécurité deviennent étranges quand ils sont pensés pour des méchants de cinéma. Restons opérationnels. Dans des environnements réels, l’accès à Proxmox échoue généralement à cause d’un des cas suivants :
1) L’ingénieur bien intentionné avec trop d’accès
Ce n’est pas un défaut de caractère ; c’est un défaut de système. Si la façon la plus simple de résoudre un problème est d’utiliser root, les gens utiliseront root. Le résultat est des dommages accidentels qui ressemblent exactement à de la malveillance : disques supprimés, mauvaise règle de pare-feu poussée, migration à chaud déclenchée pendant une maintenance sur le mauvais nœud.
2) Le runner d’automatisation compromis
Votre runner CI, agent GitOps ou « contrôleur de sauvegarde » est un ordinateur sur Internet (ou du moins sur un réseau). Il finira par être compromis ou mal configuré. Quand cela arrive, la question n’est pas « peut-il accéder à Proxmox », mais « que peut-il faire une fois à l’intérieur ? »
3) L’attaquant externe qui a obtenu une porte d’entrée ailleurs
La plupart des attaquants ne commencent pas par Proxmox. Ils commencent par l’e-mail, des identifiants VPN, un portable de développeur, une application web ou une dépendance de la chaîne d’approvisionnement. Puis ils pivotent. Proxmox est un jackpot de pivot : l’accès au plan de contrôle est le chemin le plus court vers la persistance et la destruction.
Votre travail est de construire un système où la compromission d’un jeton ou d’un runner n’égalera pas la compromission du cluster.
Jetons API Proxmox : modèle, limites et points durs
Les jetons API Proxmox sont des identifiants que vous créez sous un utilisateur Proxmox. Ils peuvent se voir accorder des privilèges via le même système RBAC et ACL que les utilisateurs. Les jetons peuvent aussi être marqués « séparation de privilèges » (privilege separation), ce qui signifie qu’ils n’héritent pas automatiquement de tout ce que possède l’utilisateur. Ce réglage est là où la posture de sécurité se gagne ou se perd.
Les jetons ne sont pas magiques ; le RBAC est la magie
Si vous créez des jetons pour root@pam et leur donnez des permissions larges sur le cluster, vous n’avez rien amélioré. Vous avez juste changé la forme du problème et facilité l’exfiltration : les jetons sont conçus pour être utilisés par des machines, donc les machines les stockeront.
Le périmètre est basé sur le chemin, et les erreurs de chemin sont fréquentes
Les ACL Proxmox s’appliquent à des chemins comme :
/(à l’échelle du cluster)/nodes/<node>(spécifique au nœud)/vms/<vmid>(spécifique à la VM)/storage/<storage-id>(spécifique au stockage)/pool/<poolname>(pools de ressources)
La plupart des incidents « oups » proviennent d’un octroi au / parce que quelqu’un voulait qu’un jeton fasse une tâche et n’a pas su trouver le bon chemin ou rôle. Alors ils ont tout rendu global. Félicitations, vous venez de créer un bouton d’arrêt.
Le cycle de vie du jeton compte plus que sa création
Créer des jetons est facile. Les gérer est là où les programmes meurent :
- Où les jetons sont-ils stockés ? (secrets CI, Vault, variables d’environnement, fichiers sur disque.)
- À quelle fréquence sont-ils tournés ?
- Comment détectez-vous des anomalies d’usage ?
- Comment révoquez-vous en quelques minutes ?
Idée paraphrasée (Gene Kim) : « Améliorer les systèmes consiste à raccourcir les boucles de rétroaction et à rendre les changements sûrs à répéter. » Cela s’applique aussi à la rotation des identifiants.
Principes de conception : le principe du moindre privilège qui survit à la vraie vie
1) Les jetons sont par intégration, pas par équipe
Si « le jeton DevOps » existe, vous avez construit un destin partagé. Créez des jetons par outil et par environnement : terraform-prod, backup-controller, monitoring-readonly, ci-staging. Les humains ne doivent pas partager de jetons ; les machines non plus.
2) Utilisez la « séparation de privilèges » sauf si vous faites délibérément un jeton admin
Par défaut, un jeton peut hériter des privilèges de l’utilisateur (selon la manière dont vous le créez). Vous voulez que le jeton n’ait que les ACLs que vous lui attachez explicitement. L’utilisateur parent peut être admin pour une utilisation d’urgence, tandis que le jeton reste limité pour l’automatisation.
3) Préférez les pools de ressources à la prolifération d’ACL par VM
Accorder des ACL sur des dizaines de VM individuelles est la manière dont vous finissez par dire « donnez / ». Les pools vous permettent de regrouper les ressources et d’accorder des permissions au chemin du pool. Votre futur vous remerciera avec moins d’éditions à 2h du matin.
4) Séparez les responsabilités : le provisioning n’est pas l’exploitation
Les outils de provisioning ont souvent besoin de droits pour créer des VM, attacher du stockage, définir des tags et configurer le réseau. Les opérateurs et systèmes de sauvegarde ont besoin d’autres droits. Ne regroupez pas tout parce que c’est « un pipeline ». Les pipelines ne sont pas des identités.
5) Concevez pour des exercices de révocation
La révocation n’est pas une manœuvre réservée aux urgences. Entraînez-vous. Vous devez pouvoir révoquer un jeton et observer une défaillance contrôlée dans le système dépendant, avec un message d’erreur clair et un plan de retour arrière.
6) Concevez autour du mode d’échec le plus courant : quelqu’un accorde trop
Rendez l’octroi excessif plus difficile :
- Restreignez qui peut modifier les ACL au
/. - Conservez un petit nombre de rôles prédéfinis (et révisez-les).
- Exigez un contrôle de changement pour les permissions à l’échelle du cluster.
Blague #2 : « Accès admin temporaire » a la même durée de vie qu’un sac en plastique dans l’océan.
Tâches pratiques (commandes, sorties et décisions)
Voici des tâches pratiques que vous pouvez exécuter sur un nœud Proxmox ou via les outils CLI. Chacune inclut ce que signifie la sortie et la décision à en tirer. L’objectif est la clarté opérationnelle, pas la sécurité cochée en case.
Tâche 1 : Confirmer l’état du cluster avant de modifier l’auth
cr0x@server:~$ pvecm status
Cluster information
-------------------
Name: prod-pve
Config Version: 17
Transport: knet
Secure auth: on
Quorum information
------------------
Date: Tue Feb 4 11:12:21 2026
Quorum provider: corosync_votequorum
Nodes: 3
Node ID: 0x00000001
Ring ID: 1.24
Quorate: Yes
Ce que cela signifie : Vous êtes en quorom et les changements de configuration du cluster devraient se répliquer correctement. Si Quorum est No, évitez la turbulence RBAC ; vous pourriez créer une dérive entre les nœuds.
Décision : Procédez uniquement si le cluster est quorate. Sinon, réparez d’abord la santé du cluster.
Tâche 2 : Lister les utilisateurs pour repérer « l’automatisation déguisée en humains »
cr0x@server:~$ pveum user list
┌──────────────┬───────────┬───────────┬────────────────────────────┐
│ userid │ enable │ expire │ firstname │
╞══════════════╪═══════════╪═══════════╪════════════════════════════╡
│ root@pam │ 1 │ 0 │ │
│ alice@pve │ 1 │ 0 │ Alice │
│ ci@pve │ 1 │ 0 │ CI Runner │
│ monitor@pve │ 1 │ 0 │ Monitoring │
└──────────────┴───────────┴───────────┴────────────────────────────┘
Ce que cela signifie : Des utilisateurs locaux existent. Si vous voyez des « comptes de service » sous des noms humains ou l’inverse, vous avez probablement une prolifération de jetons et une propriété floue.
Décision : Créez des utilisateurs de service dédiés (ou des identités backées par un realm) pour chaque intégration. N’utilisez pas d’utilisateurs humains pour l’automatisation.
Tâche 3 : Inspecter les jetons API et repérer les erreurs d’héritage
cr0x@server:~$ pveum user token list ci@pve
┌──────────────┬───────────────┬────────┬──────────────┐
│ tokenid │ expire │ enable │ privsep │
╞══════════════╪═══════════════╪════════╪══════════════╡
│ terraform │ 0 │ 1 │ 1 │
│ ansible │ 0 │ 1 │ 0 │
└──────────────┴───────────────┴────────┴──────────────┘
Ce que cela signifie : privsep=0 suggère que le jeton peut hériter des privilèges de l’utilisateur. C’est souvent là que le « moindre privilège » meurt en silence.
Décision : Activez la séparation de privilèges pour les jetons sauf si vous avez une raison documentée de ne pas le faire. Ensuite attachez explicitement les ACLs au jeton.
Tâche 4 : Créer un utilisateur de service dédié (pas de shell, pas d’extras)
cr0x@server:~$ sudo pveum user add backup@pve --comment "Backup controller service user"
Ce que cela signifie : Vous avez créé une identité qui peut porter des jetons et des ACLs. Ce n’est pas un humain ; elle ne doit pas être utilisée en interaction directe.
Décision : Utilisez un utilisateur de service par domaine d’intégration (backup, monitoring, provisioning). Évitez « un utilisateur de service pour tout gouverner ».
Tâche 5 : Créer un jeton avec séparation de privilèges et le récupérer une seule fois
cr0x@server:~$ sudo pveum user token add backup@pve restic --privsep 1
┌──────────────┬──────────────────────────────────────────────┐
│ key │ value │
╞══════════════╪══════════════════════════════════════════════╡
│ full-tokenid │ backup@pve!restic │
│ value │ 7c9dbbb2-3e76-4b3b-8d9f-0c8af2c5d2a1 │
└──────────────┴──────────────────────────────────────────────┘
Ce que cela signifie : C’est la seule fois où vous verrez la valeur du jeton en clair. Stockez-la immédiatement dans votre gestionnaire de secrets.
Décision : Si vous ne pouvez pas stocker les secrets correctement, arrêtez-vous et corrigez cela d’abord. Les jetons amplifient l’hygiène des secrets que vous avez déjà.
Tâche 6 : Créer un rôle personnalisé au lieu de réutiliser « Administrator »
cr0x@server:~$ sudo pveum role add BackupOperator -privs "VM.Audit VM.Backup Datastore.Audit"
Ce que cela signifie : Vous avez défini un rôle avec des privilèges explicites. Les chaînes de privilèges exactes doivent correspondre à votre version de Proxmox et aux opérations prévues.
Décision : Préférez des rôles restreints avec des noms correspondant à une fonction métier. Si vous ne pouvez pas expliquer un privilège en une phrase, ne l’accordez pas encore.
Tâche 7 : Appliquer des ACLs à un chemin contraint (pool ou stockage spécifique)
cr0x@server:~$ sudo pveum acl modify /pool/prod-vms -token 'backup@pve!restic' -role BackupOperator
Ce que cela signifie : Le jeton reçoit des permissions seulement pour les ressources dans le pool prod-vms (selon ce qui s’y trouve et comment vous faites les sauvegardes).
Décision : Si vous ne pouvez pas scoper par pool, il vous manque probablement une bonne organisation des ressources. Corrigez l’organisation au lieu d’accorder /.
Tâche 8 : Vérifier les ACLs et détecter les octrois globaux accidentels
cr0x@server:~$ pveum acl list
┌───────────────┬───────────────────────┬───────────────┬─────────────┐
│ path │ ugid │ roleid │ propagate │
╞═══════════════╪═══════════════════════╪═══════════════╪═════════════╡
│ / │ root@pam │ Administrator │ 1 │
│ /pool/prod-vms│ backup@pve!restic │ BackupOperator│ 1 │
│ / │ ci@pve!ansible │ Administrator │ 1 │
└───────────────┴───────────────────────┴───────────────┴─────────────┘
Ce que cela signifie : Le jeton ci@pve!ansible a le rôle Administrator sur /. C’est un problème sauf si c’est un risque consciemment accepté avec des contrôles compensatoires.
Décision : Supprimez les ACLs larges pour les jetons d’automatisation. Créez des rôles et chemins restreints.
Tâche 9 : Supprimer une ACL surpuissante (en sécurité)
cr0x@server:~$ sudo pveum acl delete / -token 'ci@pve!ansible' -role Administrator
Ce que cela signifie : Vous avez révoqué l’admin cluster pour ce jeton au chemin racine.
Décision : Confirmez immédiatement ce dont l’automatisation a encore besoin ; réaccordez seulement le minimum requis sur des chemins étroits.
Tâche 10 : Valider l’authentification du jeton contre l’API locale
cr0x@server:~$ curl -k -s \
-H "Authorization: PVEAPIToken=backup@pve!restic=7c9dbbb2-3e76-4b3b-8d9f-0c8af2c5d2a1" \
https://127.0.0.1:8006/api2/json/version | jq
{
"data": {
"release": "8.1",
"repoid": "c6d7f9a0",
"version": "8.1.4"
}
}
Ce que cela signifie : Le jeton est valide et peut atteindre l’API. C’est un test de vivacité/auth basique.
Décision : Si cela échoue, ne recommencez pas à modifier les rôles aveuglément. Confirmez d’abord la synchronisation temporelle, le format du jeton et que vous tapez le bon nœud/realm.
Tâche 11 : Confirmer l’autorisation en tentant une action qui devrait être refusée
cr0x@server:~$ curl -k -s \
-H "Authorization: PVEAPIToken=backup@pve!restic=7c9dbbb2-3e76-4b3b-8d9f-0c8af2c5d2a1" \
https://127.0.0.1:8006/api2/json/nodes | jq
{
"errors": {
"permission": "permission denied - invalid privileges"
}
}
Ce que cela signifie : Bien. Votre jeton de sauvegarde ne peut pas énumérer les nœuds à l’échelle du cluster. C’est le moindre privilège qui fonctionne.
Décision : Gardez-le refusé sauf si votre système de sauvegarde a vraiment besoin d’un inventaire de nœud. Si c’est le cas, accordez une lecture seule à un périmètre plus restreint, pas admin.
Tâche 12 : Vérifier l’accès SSH root (parce que les jetons API ne vous sauveront pas de la dérive SSH)
cr0x@server:~$ sudo sshd -T | egrep 'permitrootlogin|passwordauthentication'
permitrootlogin yes
passwordauthentication yes
Ce que cela signifie : Root peut SSHer avec un mot de passe. C’est un contournement des identifiants autour de votre programme de jetons.
Décision : Désactivez l’auth par mot de passe et SSH root direct. Utilisez sudo depuis des comptes nommés ou l’accès console pour les urgences.
Tâche 13 : Faire respecter des valeurs SSH plus sûres (et savoir ce que vous avez changé)
cr0x@server:~$ sudo sh -c 'cat >/etc/ssh/sshd_config.d/99-hardening.conf <
Ce que cela signifie : SSH root est désactivé ; les mots de passe sont désactivés. Si quelqu’un vole un mot de passe, il est moins utile. Si quelqu’un vole un jeton, cela ne l’aidera pas à SSHer.
Décision : Assurez-vous d’avoir testé l’accès par clé pour les comptes break-glass avant d’activer cela à l’échelle du cluster.
Tâche 14 : Vérifier la synchronisation temporelle (les échecs d’auth aiment le décalage horaire)
cr0x@server:~$ timedatectl
Local time: Tue 2026-02-04 11:15:10 UTC
Universal time: Tue 2026-02-04 11:15:10 UTC
RTC time: Tue 2026-02-04 11:15:10
Time zone: UTC (UTC, +0000)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
Ce que cela signifie : L’horloge est synchronisée. Quand elle ne l’est pas, vous obtenez des comportements d’auth et TLS intermittents qui ressemblent à « Proxmox est instable ». Ce n’est pas lui. C’est votre temps.
Décision : Si non synchronisé, corrigez NTP avant de déboguer les jetons ou TLS.
Tâche 15 : Surveiller l’auth et les erreurs API dans les logs
cr0x@server:~$ sudo journalctl -u pveproxy -u pvedaemon --since "30 min ago" | tail -n 20
Feb 04 10:58:22 pve1 pveproxy[1832]: authentication failure; rhost=10.20.1.55 user=backup@pve msg=invalid token value
Feb 04 11:02:17 pve1 pvedaemon[1711]: api call failed: permission denied - invalid privileges
Feb 04 11:07:44 pve1 pveproxy[1832]: successful auth for user 'monitor@pve' from 10.20.2.20
Ce que cela signifie : Vous pouvez distinguer une mauvaise valeur de jeton d’un manque de privilèges. Cette différence est importante : l’une concerne la distribution des secrets, l’autre la conception RBAC.
Décision : Si « invalid token value », faites une rotation et corrigez la gestion des secrets. Si « invalid privileges », ajustez les chemins/roles ACL, pas le stockage du jeton.
Tâche 16 : Trouver quel processus écoute sur le port API Proxmox
cr0x@server:~$ sudo ss -ltnp | grep ':8006'
LISTEN 0 4096 0.0.0.0:8006 0.0.0.0:* users:(("pveproxy",pid=1832,fd=6))
Ce que cela signifie : pveproxy écoute sur 8006. Si vous voyez autre chose, vous avez un problème de packaging ou de processus.
Décision : Si pveproxy n’écoute pas, ne blâmez pas les jetons. Réparez d’abord le service.
Tâche 17 : Faire tourner un jeton sans downtime (pattern)
cr0x@server:~$ sudo pveum user token add backup@pve restic-v2 --privsep 1
┌──────────────┬──────────────────────────────────────────────┐
│ key │ value │
╞══════════════╪══════════════════════════════════════════════╡
│ full-tokenid │ backup@pve!restic-v2 │
│ value │ 5c1a4e61-9b9c-4f1f-9c7f-9d7a1b4a8d20 │
└──────────────┴──────────────────────────────────────────────┘
cr0x@server:~$ sudo pveum acl modify /pool/prod-vms -token 'backup@pve!restic-v2' -role BackupOperator
cr0x@server:~$ sudo pveum user token list backup@pve
┌──────────────┬───────────────┬────────┬──────────────┐
│ tokenid │ expire │ enable │ privsep │
╞══════════════╪═══════════════╪════════╪══════════════╡
│ restic │ 0 │ 1 │ 1 │
│ restic-v2 │ 0 │ 1 │ 1 │
└──────────────┴───────────────┴────────┴──────────────┘
Ce que cela signifie : Vous avez maintenant deux jetons valides avec des ACL identiques. Mettez à jour le système dépendant pour utiliser v2, vérifiez, puis révoquez v1.
Décision : Faites toujours la rotation par chevauchement, pas par basculement sec, sauf si vous aimez vous alerter vous-même.
Tâche 18 : Révoquer un jeton proprement pendant un incident
cr0x@server:~$ sudo pveum user token remove backup@pve restic
cr0x@server:~$ sudo pveum user token list backup@pve
┌──────────────┬───────────────┬────────┬──────────────┐
│ tokenid │ expire │ enable │ privsep │
╞══════════════╪═══════════════╪════════╪══════════════╡
│ restic-v2 │ 0 │ 1 │ 1 │
└──────────────┴───────────────┴────────┴──────────────┘
Ce que cela signifie : Le jeton compromis est mort. Les appels l’utilisant devraient échouer immédiatement.
Décision : Après révocation, vérifiez les logs pour tentatives continues. Si elles persistent, vous avez trouvé le runner compromis ou l’emplacement de fuite du secret.
Fiche de diagnostic rapide
Quand « le jeton ne fonctionne pas » ou « l’automatisation est cassée », ne vous éparpillez pas. Suivez une séquence qui isole le goulot en quelques minutes.
Premier point : l’API est-elle joignable et saine ?
- Vérifiez
ss -ltnp | grep :8006pour s’assurer quepveproxyécoute. - Vérifiez
systemctl status pveproxy pvedaemonpour des crash loops. - Depuis l’hôte runner, testez la connectivité vers
node:8006(pare-feu/routage).
Interprétation : Si le port est down ou si les services sont malsains, les changements de jeton n’y changeront rien. Réparez la plateforme d’abord.
Deuxième point : l’auth échoue ou l’autorisation échoue ?
- Consultez
journalctl -u pveproxy -u pvedaemonpour « invalid token value » vs « invalid privileges ». - Essayez un appel API simple comme
/api2/json/version.
Interprétation : Invalid token value = gestion/format des secrets. Invalid privileges = conception RBAC/ACL.
Troisième point : avez‑vous scoppé l’ACL au bon chemin ?
- Listez les ACLs et regardez où le jeton est accordé.
- Confirmez que la ressource est réellement sous ce pool/chemin.
- Vérifiez que les privilèges du rôle sont suffisants mais pas larges.
Interprétation : La plupart des échecs du moindre privilège viennent d’un mauvais chemin. Le second plus fréquent est une mauvaise chaîne de privilèges du rôle.
Quatrième point : la « séparation de privilèges » fait‑elle ce que vous pensez ?
- Listez les jetons et confirmez
privsep=1. - Vérifiez si vous avez accidentellement accordé à l’utilisateur parent trop au
/et compté sur l’héritage.
Interprétation : Si un jeton est surpuissant, c’est généralement parce qu’il a hérité ou a été accordé sur /.
Cinquième point : l’heure/TLS provoque-t-il des problèmes intermittents d’auth ?
- Vérifiez
timedatectlsur les nœuds et les runners. - Si les runners valident TLS, confirmez les chemins de confiance des certificats et la correspondance des noms d’hôte.
Interprétation : Le décalage horaire et la mésentente TLS se font passer pour « jeton invalide », surtout pendant des reconstructions de nœud.
Erreurs courantes : symptômes → cause racine → correction
1) Symptôme : « Tout fonctionne dans l’interface web, mais le jeton obtient permission denied »
Cause racine : Vous avez testé en tant qu’utilisateur humain (qui a des privilèges larges) et supposé que le jeton hérite d’eux. Le jeton est privsep=1 sans ACLs, ou les ACLs sont sur le mauvais chemin.
Correction : Attachez des ACLs explicitement au jeton au chemin correct ; validez avec un appel API minimal qui nécessite exactement ce privilège.
2) Symptôme : « Le jeton marche un jour, puis échoue au hasard »
Cause racine : La valeur du jeton a été tournée dans le magasin de secrets mais pas déployée partout ; ou plusieurs runners ont des valeurs obsolètes ; ou le décalage horaire déclenche des comportements limites d’auth.
Correction : Mettez en place la rotation par chevauchement ; ajoutez une vérification de déploiement qui confirme que le jeton fonctionne avant le rollout ; imposez NTP partout.
3) Symptôme : « L’automatisation peut supprimer des VM alors qu’elle ne devrait pas »
Cause racine : Le jeton a Administrator à / (souvent parce que quelqu’un l’a accordé pour du debug et ne l’a jamais retiré), ou héritage du parent admin est activé.
Correction : Retirez l’ACL racine ; recréez le jeton avec --privsep 1 ; construisez des rôles étroits comme « VM.PowerMgmt » uniquement.
4) Symptôme : « La révocation du jeton n’a pas arrêté le comportement »
Cause racine : L’acteur n’utilise pas ce jeton (mauvaise hypothèse), ou il y a plusieurs jetons, ou ils utilisent une route mot de passe/SSH à la place.
Correction : Greppez les logs pour l’identité user/token ; inventorie ttes les jetons ; désactivez les routes d’accès par mot de passe ; révoquez systématiquement.
5) Symptôme : « On ne peut pas scoper les permissions sans casser le pipeline »
Cause racine : Le pipeline fait trop de choses : provisioning, firewall, stockage et opérations VM sous une même identité. Ou l’organisation des ressources est manquante (pas de pools, noms incohérents).
Correction : Séparez les identités par fonction et étape ; créez des pools ; refondez les étapes du pipeline pour que chacune utilise un jeton spécifique.
6) Symptôme : « Les jetons fuient dans les logs »
Cause racine : Les outils impriment des headers, des variables d’environnement ou des sorties de debug ; les ingénieurs copient-collent des commandes en échec dans le chat.
Correction : Désactivez le logging HTTP verbeux ; épurez les logs CI ; utilisez des secrets masqués ; imposez une politique « pas de secrets dans les tickets » avec support outillé.
Trois mini-récits du monde de l’entreprise (tous plausibles)
Mini-récit 1 : L’incident causé par une mauvaise hypothèse
Ils avaient un cluster Proxmox supportant des services internes : agents de build, stockage d’artifacts, quelques petites bases de données que tout le monde jurait être « temporaires ». L’équipe plateforme a décidé de « faire la bonne chose » et a déplacé l’automatisation d’un mot de passe root vers un jeton API.
La mauvaise hypothèse : « Si l’utilisateur peut le faire dans l’UI, le jeton pourra aussi. » Ils ont créé un jeton sous un utilisateur admin, activé la séparation de privilèges parce que cela semblait plus sûr, et n’ont jamais attaché d’ACLs au jeton. L’utilisateur avait tout. Le jeton n’avait presque rien.
À minuit, leur système CI a essayé de lancer une VM pour un déploiement et a reçu permission denied. La logique de retry était enthousiaste. Elle a martelé l’API, rempli les logs, et l’astreignant a vu des alertes « Proxmox authentication failure » et a supposé une compromission. Ils ont commencé à révoquer des identifiants et ont cassé d’autres intégrations, parce que le premier modèle mental était « attaque », pas « jeton mal scoppé ».
Ce qui a résolu le problème n’a pas été de l’héroïsme. C’était lire les logs attentivement : « invalid privileges », pas « invalid token value ». Ils ont attaché le rôle correct au chemin du pool, puis limité le débit des retries du pipeline. La plus grande leçon a été culturelle : quand vous changez l’auth, vous devez le traiter comme un déploiement en production avec rollout par étapes, pas comme un tweak UI.
Mini-récit 2 : L’optimisation qui s’est retournée contre eux
Une autre entreprise voulait un provisioning plus rapide. Leur pipeline Terraform créait des VM, posait des tags, attachait des ISOs, configurait des règles de pare-feu et même faisait des tâches de maintenance de nœud. Tout ça via un seul jeton. C’était rapide parce que c’était effectivement cluster-admin.
Ils ont « optimisé » davantage en cachant le jeton dans une image de runner partagée afin que les jobs n’aient pas besoin de récupérer les secrets à l’exécution. Ça économisait quelques secondes sur les builds. Cela signifiait aussi que chaque runner éphémère avait une copie du jeton sur disque, et les anciennes images restaient dans les registres et caches plus longtemps que quiconque n’osait l’admettre.
Des mois plus tard, une revue sécurité a trouvé le jeton dans une couche d’image runner. Pas grâce à une forensique avancée — parce que quelqu’un a exécuté strings sur une image lors d’un scan de routine et il en est sorti comme une confession. Ils ont révoqué le jeton. La moitié de leur automatisation a cessé de fonctionner. L’autre moitié a continué parce qu’elle utilisait un second jeton codé en dur dans un autre endroit, laissé par une migration antérieure.
Le retour de bâton n’a pas été seulement l’exposition ; c’était la perte de contrôle. Ils ne savaient pas où étaient les secrets. Ils ne pouvaient pas tourner proprement. Ils ne pouvaient même pas inventorier. Après le nettoyage, leur pipeline était un peu plus lent, mais leur temps de réponse incident s’est drastiquement amélioré. La vitesse, c’est bien ; la révocabilité prévisible, c’est mieux.
Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Une autre organisation exploitait Proxmox pour des workloads edge. Rien de glamour. Beaucoup de petits clusters. Beaucoup d’automatisation. Ils faisaient une chose ennuyeuse de façon cohérente : chaque jeton avait un propriétaire, un but, un périmètre et une date de rotation. Ils stockaient ces métadonnées dans un registre interne simple, et pratiquaient la rotation trimestriellement.
Un matin, la surveillance a montré une activité API inhabituelle : tentatives répétées permission denied depuis un hôte qui ne devrait pas parler à Proxmox. Les logs avaient un ID de jeton dans l’en-tête d’auth. Ils ont retrouvé le jeton exact dans le registre : il appartenait à un runner CI de staging, scoppé sur un pool staging, et il n’était pas censé être utilisé depuis ce segment réseau.
Ils l’ont révoqué immédiatement, et rien en production n’a cassé parce que le jeton était scoppé et réservé au staging. Ensuite ils ont tracé l’hôte runner : il avait été ré-imagé et placé par erreur dans un réseau plus large. C’était le vrai bug. La conception des jetons a transformé un signal inquiétant en incident contenu.
Leur pratique « ennuyeuse » n’était pas coûteuse. Elle était disciplinée. Elle a empêché qu’une erreur de staging devienne une panne de production, et elle leur a offert un mardi calme au lieu d’un appel exécutif.
Listes de contrôle / plan pas-à-pas
Pas-à-pas : déplacer une intégration du mot de passe root vers un jeton scoppé
- Inventoriez l’accès actuel. Identifiez où le mot de passe root est utilisé (variables CI, scripts, cron jobs, gestion de config).
- Créez un utilisateur de service dédié. Nommez‑le d’après l’intégration, pas une personne (ex.
backup@pve). - Créez un jeton avec séparation de privilèges. Stockez la valeur du jeton une fois dans un gestionnaire de secrets.
- Créez ou réutilisez un rôle étroit. Seuls les privilèges requis pour l’intégration.
- Scopez les ACLs par pool/stockage/chemin. Évitez
/sauf si c’est vraiment une intégration admin cluster (rare). - Testez avec un appel API minimal. Validez l’auth (
/version), puis un appel autorisé, puis un appel refusé. - Déployez en canari. Un runner, un environnement, un job.
- Activez la rotation par chevauchement dès le jour un. Ajoutez le jeton v2, déployez, vérifiez, révoquez v1.
- Supprimez l’usage de l’ancien mot de passe. Effacez‑le des secrets, scripts, images ; ne laissez pas des identifiants « backup » traîner.
- Documentez la propriété et la date d’expiration/rotation. Si ce n’est pas possédé, il deviendra immortel.
Checklist : à quoi ressemble le « bon »
- Le mot de passe root n’est pas utilisé par l’automatisation.
- La connexion SSH root est désactivée ; l’authentification par mot de passe est désactivée quand c’est possible.
- Les jetons utilisent la séparation de privilèges par défaut.
- Les jetons sont scoppés aux pools, VM, IDs de stockage ou nœuds — pas à
/. - Les rôles sont petits, nommés et revus périodiquement.
- Un inventaire des jetons existe (propriétaire, but, création, dernière rotation).
- La rotation est pratiquée, pas promise.
- Les logs distinguent rapidement mauvais secrets vs mauvaises permissions.
Checklist : containment d’incident pour compromission suspectée d’identifiants
- Identifiez le jeton/utilisateur depuis les logs (pveproxy/pvedaemon).
- Révoquez le jeton immédiatement (ne « pas attendre la confirmation »).
- Recherchez des tentatives continues d’utilisation du jeton révoqué.
- Localisez le point de distribution du secret (CI, config, disque).
- Tournez les jetons adjacents utilisés sur le même runner ou magasin de secrets.
- Confirmez qu’il n’existe pas d’ACL larges à
/pour les jetons d’automatisation. - Revoyez les actions API récentes (qui a fait quoi, depuis où) et validez l’intégrité des VM et du stockage.
FAQ
1) Dois‑je créer des jetons sous root@pam ?
Non pour l’automatisation. Utilisez des utilisateurs de service dédiés et la séparation de privilèges. Root doit rester pour l’accès humain d’urgence, pas pour le CI.
2) Qu’est‑ce que la « séparation de privilèges » m’apporte réellement ?
Elle empêche le jeton d’hériter automatiquement des permissions de l’utilisateur. Ça vous force à accorder explicitement ce dont le jeton a besoin, et c’est bien l’objectif.
3) Puis‑je restreindre un jeton à une VM spécifique seulement ?
Oui, en appliquant des ACLs sur /vms/<vmid> avec un rôle restreint. En pratique, les pools montent mieux en charge, mais le scoping par VM est utile pour des systèmes à haut risque.
4) Comment puis‑je tourner des jetons sans casser la production ?
Utilisez la rotation par chevauchement : créez un second jeton avec les mêmes ACLs, déployez‑le, vérifiez‑le, puis révoquez l’ancien. Ne faites pas de « flip » sec à moins de pouvoir tolérer une interruption.
5) Les jetons remplacent‑ils l’authentification à deux facteurs (2FA) ?
Non. Les jetons servent pour machine‑à‑machine. Le 2FA est pour les humains. Vous voulez les deux : un contrôle fort des connexions humaines et des identifiants machines scoppés.
6) Quel est l’endroit le plus sûr pour stocker les jetons ?
Un gestionnaire de secrets approprié avec des politiques d’accès et des logs d’audit. Si vous devez utiliser des variables CI, assurez‑vous que le masquage est forcé et que les logs de debug ne peuvent pas imprimer les headers.
7) Pourquoi ne pas simplement placer Proxmox derrière un VPN et continuer d’utiliser des mots de passe ?
Parce que les réseaux internes et les VPN ne sont plus des frontières de confiance. Vous avez besoin d’identifiants scoppés et révoquables même lorsque le réseau est « privé ». Le VPN est une couche, pas une stratégie.
8) Combien de rôles devrions‑nous avoir ?
Moins que vous ne le pensez, mais plus qu’un seul. Commencez avec 5–10 rôles focalisés métier (monitoring lecture seule, contrôle alimentation VM, provisioning, sauvegarde, admin réseau). Élargissez seulement quand vous ne pouvez pas exprimer un besoin proprement.
9) Et si une intégration a besoin d’un accès large sur beaucoup de VM ?
Groupez ces VM dans un pool et scopez sur /pool/<name>. Si elle a vraiment besoin de permissions cluster‑wide, traitez ce jeton comme un identifiant admin production : contrôles supplémentaires, rotation plus fréquente, stockage plus strict et approbation explicite.
10) Comment savoir quels jetons existent et qui les possède ?
Proxmox peut lister les jetons, mais la propriété est un problème de processus. Maintenez un registre interne (même un tableau simple) mappant les IDs de jetons aux propriétaires, systèmes et cadence de rotation.
Conclusion : prochaines étapes qui tiennent
Les jetons API ne sont pas un trophée de sécurité. Ils servent à rendre la compromission survivable et les opérations sensées. Si vous continuez à utiliser le mot de passe root comme identifiant par défaut pour l’automatisation, vous dites en pratique aux attaquants — et aux ingénieurs fatigués — que le plan de contrôle tient à un secret divulgué.
Prochaines étapes pratiques :
- Choisissez une intégration (CI, sauvegarde, monitoring) et migrez‑la cette semaine vers un utilisateur de service dédié + jeton en séparation de privilèges.
- Créez un rôle personnalisé étroit qui correspond au vrai travail de cette intégration, pas à vos peurs.
- Scopez l’ACL à un pool ou des chemins spécifiques. Retirez tout
Administratorà/pour les jetons d’automatisation. - Désactivez la connexion SSH root et l’authentification par mot de passe quand c’est possible, et vérifiez que vous avez toujours une voie break‑glass fonctionnelle.
- Mettez en place la rotation par chevauchement et faites un exercice de rotation. Traitez‑le comme un déploiement : par étapes, testé, réversible.
- Commencez un inventaire des jetons avec propriétaires et dates de rotation. Si vous ne pouvez pas nommer le propriétaire, ce n’est pas un jeton — c’est une dette.
Si vous ne faites qu’une chose : cessez de laisser « root partout » être le chemin le plus simple. Remplacez‑le par des jetons scoppés et une piste d’audit qui dit la vérité.