Aller au contenu

Sécuriser un monorepo avec des permissions fines

Un monorepo concentre le code de plusieurs composants et équipes dans un même dépôt Git. Cette centralisation accélère les changements transverses, mais elle transforme chaque droit d’écriture, pipeline et fichier de configuration en frontière de sécurité potentielle.

→ Lire l’explication complète de la marmotte

Monorepo : intégrité des écritures, pas confidentialité des lectures

Section intitulée « Monorepo : intégrité des écritures, pas confidentialité des lectures »

Le compromis central est brutal : un monorepo gagne en cohérence, en refactorings atomiques et en réutilisation de code ; il paie cette cohérence par une frontière de lecture commune. Git distribue des objets, pas des compartiments étanches. Toute identité capable de cloner un dépôt peut généralement lire les objets accessibles dans son historique, y compris des fichiers retirés d’une branche récente.

Les permissions par chemin protègent donc l’intégrité des modifications, pas la confidentialité du contenu. Un sparse-checkout réduit les fichiers matérialisés dans le répertoire de travail, mais ne réduit pas les objets téléchargeables. C’est une optimisation de poste, pas un contrôle d’accès. Appeler cela une segmentation revient à repeindre une porte coupe-feu sur un mur en carton.

La recommandation est nette : conserver un monorepo seulement si toutes les personnes disposant d’un accès en lecture sont autorisées à connaître l’ensemble du code et de son historique. Séparer dans un autre dépôt les secrets, les connecteurs clients, les règles de détection sensibles, les clés publiques d’infrastructure non publiables et le code soumis à une restriction contractuelle ou réglementaire distincte.

Le contrôle à grain fin doit ensuite porter sur trois objets différents :

  1. les droits directs sur le dépôt, qui déterminent lecture, écriture et administration ;
  2. la propriété des chemins, qui impose des relecteurs sur certains répertoires ;
  3. les droits d’exécution des pipelines, qui déterminent ce qu’une modification peut déployer ou exfiltrer.

Confondre ces trois couches produit la fausse sécurité habituelle : un fichier CODEOWNERS impeccablement rangé, puis un pipeline modifiable par n’importe quel contributeur qui possède un jeton de production. Le fichier est alors décoratif, comme un extincteur dessiné au feutre.

Prérequis de plateforme et modèle d’autorisation

Section intitulée « Prérequis de plateforme et modèle d’autorisation »

Le forge logicielle doit fournir des règles de protection de branche ou des règles de dépôt capables d’exiger une revue, d’empêcher l’auto-validation et de bloquer la fusion tant que les propriétaires de chemin n’ont pas approuvé. GitHub Rulesets, GitLab Protected Branches et les mécanismes équivalents conviennent, à condition de vérifier leur sémantique exacte sur l’édition réellement exploitée.

Git 2.25 ou supérieur est recommandé pour les équipes utilisant sparse-checkout en mode cône. Cette version ne renforce pas l’autorisation, mais réduit la friction de travail sur les grands arbres. Les exécuteurs de pipeline doivent être éphémères, isolés par projet ou par niveau de confiance, et supprimés après exécution. Un agent permanent partagé entre dépôts est un buffet à volonté pour jetons oubliés.

Les groupes d’accès doivent venir d’un fournisseur d’identité central, avec authentification multifacteur, ou MFA (Multi-Factor Authentication), imposée pour les rôles d’écriture et d’administration. La gestion des identités, des groupes et de leur révocation relève de la gouvernance des identités, pas d’une collection de collaborateurs invités manuellement dans la forge.

Éviter un modèle Role-Based Access Control, ou RBAC, trop large du type developers avec droit d’écriture global. Préférer des groupes d’équipe ayant un droit de contribution, combinés à des règles de fusion par chemin. Le droit d’écriture direct sur la branche protégée doit rester réservé aux comptes d’automatisation strictement nécessaires. Une personne peut proposer une modification partout où son travail l’exige ; elle ne doit pas pouvoir la faire entrer seule partout où son compte passe.

Les administrateurs de dépôt restent un cas particulier. Si une identité peut désactiver les règles, elle contourne les règles. Les droits d’administration doivent être rares, séparés des comptes de développement quotidiens, protégés par MFA et journalisés. Les exemptions temporaires doivent être limitées dans le temps et faire l’objet d’une alerte. Le compte administrateur utilisé pour corriger un incident ne doit pas devenir le raccourci permanent de la semaine.

CODEOWNERS associe un motif de chemin à une équipe ou à une identité responsable. La règle utile n’est pas « chaque dossier a un propriétaire », mais « chaque changement à impact de sécurité possède au moins un approbateur indépendant de son auteur ».

Les chemins suivants exigent presque toujours une propriété dédiée :

  • fichiers de pipeline, modèles de jobs et images d’exécution ;
  • manifestes de déploiement, politiques réseau et identités de charge ;
  • code d’authentification, autorisation et gestion de sessions ;
  • modules partagés de cryptographie et gestion des secrets ;
  • définitions d’infrastructure et droits du fournisseur cloud ;
  • dépendances internes publiées à d’autres composants.

Le dernier motif correspondant l’emporte dans de nombreuses implémentations de CODEOWNERS. Cette règle permet des exceptions, mais ouvre aussi un contournement par ordre de fichier : une règle générique ajoutée en bas peut absorber un répertoire sensible. La revue du fichier CODEOWNERS doit donc appartenir à l’équipe sécurité ou plateforme, jamais seulement à l’équipe qui souhaite modifier la frontière.

Un déplacement de fichier est une modification sur la source et la destination. Une règle qui ne couvre que la destination laisse passer une extraction de code sensible vers un chemin moins protégé. Les règles de propriété doivent couvrir les répertoires critiques, les fichiers racine, les fichiers cachés de la forge et les variantes de casse si la plateforme les traite différemment.

La revue par propriétaire ne remplace pas le nombre minimal de validations. Exiger deux approbations distinctes sur les chemins les plus sensibles, dont une provenant du propriétaire, réduit le risque de compte compromis et de collusion accidentelle. En revanche, imposer deux validations pour une correction de documentation dans tous les répertoires fabrique une file d’attente, puis une dérogation permanente. La granularité doit suivre le niveau de conséquence, pas l’organigramme dessiné sur une slide.

L’exemple suivant structure les responsabilités dans un dépôt contenant plusieurs services. Il suppose que la protection de la branche principale exige les revues des propriétaires et interdit la fusion par l’auteur de la demande.

# .github/CODEOWNERS
# Règle par défaut : l'équipe de la zone concernée valide le changement.
/services/paiement/ @organisation/equipe-paiement
/services/catalogue/ @organisation/equipe-catalogue
# Les frontières de déploiement appartiennent à la plateforme.
/infra/ @organisation/plateforme
/kubernetes/ @organisation/plateforme
/.github/workflows/ @organisation/plateforme @organisation/securite
# Les composants à portée transversale demandent une validation sécurité.
/libraries/authz/ @organisation/securite
/libraries/crypto/ @organisation/securite
/CODEOWNERS @organisation/securite @organisation/plateforme
# Les manifestes de dépendances peuvent modifier la chaîne de livraison.
/package-lock.json @organisation/plateforme
/pnpm-lock.yaml @organisation/plateforme

Cette configuration ne suffit pas si la branche principale accepte les poussées directes. Il faut activer simultanément l’obligation de revue des propriétaires, l’interdiction de contourner les règles pour les administrateurs lorsque la forge le permet, l’annulation des validations dès qu’un nouveau commit arrive, et une fusion uniquement après succès des contrôles automatisés.

Les groupes référencés dans CODEOWNERS doivent être non vides et maintenus par un processus distinct du dépôt. Un groupe sans membre bloque tout. Un groupe comprenant un ancien prestataire ouvre une porte silencieuse. Les deux cas apparaissent souvent après une réorganisation, ce mammifère administratif qui mange les tableaux de responsabilités.

Surface d’attaque du pipeline et des dépendances

Section intitulée « Surface d’attaque du pipeline et des dépendances »

Le pipeline d’intégration continue et livraison continue, ou CI/CD, est le point de rupture le plus fréquent. Modifier un fichier de workflow peut changer l’image exécutée, récupérer des secrets, publier un artefact ou déployer une charge. Une règle de revue sur le code applicatif ne vaut rien si le pipeline peut être modifié sans la même contrainte.

Les demandes de fusion issues d’un dépôt dérivé ne doivent jamais recevoir de secrets, de jetons d’écriture ou de droits de déploiement. Le code proposé est contrôlé par une identité extérieure jusqu’à fusion. Exécuter ce code avec un jeton privilégié revient à confier le coffre au facteur avec une note disant « merci de ne pas regarder ».

Les jetons statiques de forge ou de cloud doivent être remplacés, lorsque possible, par une fédération OpenID Connect, ou OIDC (OpenID Connect). Le pipeline présente alors une preuve d’identité courte durée au fournisseur cible, qui délivre un jeton limité au dépôt, à la branche, au workflow et à l’environnement attendus. La politique doit vérifier ces attributs. Vérifier seulement le nom du dépôt autorise un pipeline hostile issu d’une branche non protégée.

Les actions ou extensions tierces doivent être référencées par identifiant immuable, idéalement un hachage de commit, pas par une étiquette mouvante telle que v4. Une étiquette peut être déplacée après revue. Le contrôle de dépendances appartient aussi à la sécurité du développement : les manifestes verrouillés, les registres autorisés et la vérification d’intégrité limitent la substitution de paquet.

Enfin, ne jamais stocker un secret dans Git, même chiffré sans dispositif de déchiffrement séparé. Chiffrer un secret avec une clé accessible au pipeline qui lit le dépôt ne crée pas une séparation ; cela crée un secret encodé avec davantage de cérémonial.

Exploitation : révocation, journalisation et reprise

Section intitulée « Exploitation : révocation, journalisation et reprise »

La sécurité du monorepo dépend d’événements d’exploitation mesurables. Journaliser au minimum les changements de permissions, d’appartenance aux groupes, de règles de branche, de propriétaires de chemins, de secrets de pipeline et de configurations de fédération. Les journaux doivent être exportés hors de la forge, avec conservation et accès séparés. Un attaquant administrateur ne doit pas pouvoir effacer tranquillement les traces de son passage.

Déclencher des alertes sur l’ajout d’un administrateur, la désactivation d’une protection, la modification d’un workflow privilégié, la création d’un jeton personnel et l’usage d’un compte de service hors fenêtre attendue. Une alerte sans propriétaire ni procédure d’investigation reste une notification décorative, catégorie de produit très populaire dans les consoles de sécurité.

La révocation d’un membre doit supprimer ses groupes, ses clés de signature, ses jetons personnels et ses sessions actives selon les capacités de la forge. Révoquer un groupe sans invalider un jeton d’accès existant laisse une période d’exposition. Les jetons doivent être courts, dédiés à un usage et renouvelés automatiquement ; les clés privées de signature doivent être stockées hors du dépôt, avec sauvegarde chiffrée et procédure de récupération testée.

Sauvegarder la configuration de la forge ne signifie pas seulement archiver Git. Inclure règles de branche, groupes, mappings d’identité, configurations de pipeline, variables protégées, registres d’artefacts et journaux d’audit. Restaurer le code sans ses garde-fous remet un château en place avec les portes déjà ouvertes.

Sans permissions fines sur le dépôt commun, chaque droit d’écriture, pipeline et fichier de configuration devient une frontière ouverte que plusieurs équipes franchissent sans contrôle. Un accès trop large permet à quiconque de modifier le code, les workflows d’intégration ou les secrets d’un composant qui ne le concerne pas, y compris ceux qui déclenchent des déploiements. Un contributeur compromis, ou simplement imprudent, peut alors altérer la chaîne de construction d’un autre projet, injecter une étape malveillante dans un pipeline partagé ou modifier une configuration critique sans qu’aucune limite ne l’arrête. La concentration du code multiplie ainsi la portée d’un seul jeton volé : au lieu de toucher un dépôt, il touche l’ensemble. Sans cloisonnement des droits par composant, l’inventaire des personnes capables de changer quoi devient impossible à tenir, et la traçabilité des modifications sensibles se perd dans un accès uniforme.

Un dépôt commun contient les fichiers de plusieurs équipes au même endroit. Une personne autorisée à lire ce dépôt peut souvent récupérer tout son contenu et une partie de son historique. Les restrictions par dossier ne cachent donc pas vraiment les fichiers : elles servent surtout à décider qui doit valider une modification avant son ajout.

Le fonctionnement commence par l’identité de la personne. La plateforme vérifie son compte, son appartenance à un groupe et son droit de proposer une modification. Ensuite, elle regarde quels fichiers changent. Si un fichier appartient à une équipe donnée, une personne de cette équipe doit approuver le changement. La branche principale refuse la fusion tant que les validations demandées et les contrôles automatiques ne sont pas présents.

Les fichiers qui commandent les déploiements, les accès au cloud ou les règles de sécurité reçoivent une protection renforcée, car les modifier peut donner accès à des données ou à des machines. Les programmes automatiques ne doivent recevoir des secrets que pour les changements déjà validés. Un programme lancé depuis une modification non approuvée ne doit pas pouvoir publier, déployer ou lire des secrets.

Quand une personne quitte une équipe, ses accès, clés et jetons doivent être retirés. Les changements de droits et de règles doivent être enregistrés et surveillés. Si la plateforme tombe ou doit être reconstruite, restaurer seulement les fichiers ne suffit pas : il faut aussi restaurer les règles qui empêchent les modifications dangereuses.

À lire aussi

Pages voisines