Les coulisses du passage en open source de Cap Collectif #2

6 min de lecture.

Si vous n'avez pas encore lu la partie 1, rendez-vous ici.

Maintenant que le dernier commit du dépôt Git ne remonte plus aucun problème de sécurité, on a bien avancé mais on n’est pas encore tout à fait prêt à open sourcer !

Étape 2 : retirer les données sensibles

Il n'y a pas que les secrets qui peuvent poser problème avant de rendre un code public. Certains commits peuvent contenir des informations qu'on n'a pas envie de rendre visible par tous. Pour Cap Collectif, il s'agissait d'environ 20 000 commits contenant potentiellement des informations sensibles…

Par le passé, pour réaliser un import de données nous ajoutions au dépôt Git des fichiers CSV contenant les données des clients à intégrer en production… Cela nous facilitait le développement et les tests. Identifier toutes les données sensibles de l'historique est une étape assez hasardeuse. Il nous a fallu rechercher un tous les fichiers commités par gain de temps ou par erreur… De quoi avoir pas mal de (mauvaises) surprises !

Pour être honnête, on a retrouvé un peu de tout dans l'historique du code :

  • des fichiers de données clients en csv, xls ;
  • des dumps de base de données en SQL ;
  • des données personnelles, comme des emails et des pseudos ;
  • des fichiers éxécutables, des binaires ;
  • le code complet de certaines dépendances…

Je n'en suis pas forcément fier, mais les bonnes pratiques que nous avons mises en place depuis, n'étaient pas appliquées en 2014, au tout début de l'aventure. En effet, à l'époque nous étions une équipe de développeurs juniors avec un seul objectif : aller le plus vite possible…

Pour faciliter le travail de recherche dans l'historique, nous avons utilisé plusieurs commandes Git.

Afin de vérifier la présence d'un fichier avec une certaine extension dans le dépôt Git, il suffit d'utiliser :

git ls-files '*.csv'

On a ainsi cherché si des fichiers avec les extensions les plus problématiques (csv,exe,sql,zip,xls,xlsx…) avaient déjà été commités.

Ensuite, les fichiers sensibles sont en général plus lourds que les fichiers de code : nous avons donc cherché les plus gros fichiers présents dans l'historique du dépôt Git :

git rev-list --objects --all |
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' |
  sed -n 's/^blob //p' |
  sort --numeric-sort --key=2 |
  cut -c 1-12,41- |
  $(command -v gnumfmt || echo numfmt) --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest

Cela nous a permis d'identifier des fichiers qui avaient été commités puis retiré dans la même Pull Request, sans que nous ne nous en rendions compte lors des revues de code.

Pour éviter que cela ne vous arrive, je recommande fortement de toujours fusionner les commits d'une Pull Request lors du merge, ou de faire une revue de code commit, après commit lorsque ceux-ci sont atomiques.

Malgré une recherche minutieuse, nous ne serons jamais 100% sûrs d'avoir identifié tous les fichiers sensibles, mais nous pensons malgré tout avoir listé l'essentiel des fichiers problématiques.

Maintenant que nous avons identifié secrets et fichiers sensibles, nous allons procéder à la réécriture de l'historique Git, afin de nettoyer définitivement tout ça…

Étape 3 : réécrire l'historique de notre dépôt Git

Réécrire l'historique d'un dépôt Git n'est pas simple, alors on va utiliser BFG repo cleaner, un outil très complet et performant, qui va bien nous faciliter la tâche.

La première chose à faire pour démarrer une réécriture d'historique, c'est d'utiliser un dépôt miroir Git. Pour cela, clonez le dépôt Git dans un nouveau dossier avec l'option --mirror :

git clone --mirror [email protected]:cap-collectif/platform.git platform-mirror

Ce dépôt miroir ne contient pas réellement les fichiers sources, mais permet quand même la réécriture de l'historique. On va utiliser quelques commandes de l'outil au sein du dépôt miroir.

Pour supprimer des dossiers complets, on utilise l'option --delete-folders. C'était le cas, par exemple, des dossiers qui ne contenaient que des secrets comme cert et jwt :

bfg --delete-folders "{cert,jwt}" platform-mirror

Ensuite, on supprime les fichiers, avec l'option --delete-files en se basant sur l'extension lorsqu'il n'y a aucune raison de les conserver dans l'historique :

bfg --delete-files '*.{exe,sql,zip,xls,xlsx}' platform-mirror

Enfin on supprime les mots de passe et autres secrets identifiés plus tôt dans le fichier passwords.txt :

bfg --replace-text passwords.txt platform-mirror

Chaque ligne du fichier passwords.txt sera remplacée dans l'historique par ***REMOVED*** dans l'intégralité du code source !

Une fois que toutes vos opérations de réécriture ont été effectuées, chaque commit de l'historique a été nettoyé. Il faut confirmer la suppression en lançant :

git reflog expire --expire=now --all
git gc --prune=now --aggressive

On va maintenant vérifier que la réécriture c'est bien passée. Pour cela, on modifie le fichier platform-mirror/config afin de changer origin par un nouveau dépôt Git, qu'on utilisera pour les tests comme platform-mirror :

[remote "origin"]
	url = [email protected]:cap-collectif/platform-mirror.git
	fetch = +refs/*:refs/*
	mirror = true

Après un git push vous pourrez explorer le résultat de la réécriture et lancer GitGuardian à nouveau pour vérifier que plus aucun problème n'est détecté !

Le jour de la réécriture

Pour réaliser cette réécriture nous avons mis en pause les envois de code sur notre dépôt Git le temps d'une demi-journée. En effet, le script complet de la réécriture prenait un temps assez conséquent, vu le nombre de commits…

Une fois celle-ci terminée et envoyée sur GitHub, chaque développeur a fait les manipulations suivantes avant de reprendre le travail :

# On backup l'environnement de dev
mv platform platform-old-backup

# On fait un clone de 0 du dépôt après la réécriture
git clone [email protected]:cap-collectif/platform.git

# On réinstalle tous les fichiers non-comités…
cp platform-old-backup/.env.local platform/.env.local
yarn install
composer install

# On relance l'infra et tout est bon !

Après la réécriture, toutes les branches ont été mises à jour. Les développeurs peuvent donc reprendre leur travail normalement et à nouveau envoyer des modifications sur le dépôt Git.

Si des modifications avaient été effectuées par des développeurs pendant la réécriture, elles n'auraient pas été prises en compte. Il aurait alors fallu les appliquer sur le nouveau dépôt réécrit.

Malgré de nombreux tests, nous avons eu besoin de plusieurs réécritures complètes de notre dépôt Git (suite à des oublis) mais globalement le processus (bien qu'assez stressant) n'a pas été si gênant pour le quotidien des développeurs.

Maintenant qu'on a un dépôt Git tout propre, on est presque prêt pour l'open source… Nous verrons dans l'article suivant la publication d'un dépôt open source.

Voir la partie 3, publication d'un dépôt open source

Publié le