Format En passant

A chaque fois j’oublie le paramètre me permettant de cloner un dépôt git en ignorant les vérifications du certificat serveur, lorsqu’on se connecte en HTTP. Je mets donc ici la bonne commande, afin de pouvoir la retrouver facilement :p

git clone --bare -c http.sslVerify=false https://server-git/project-group/project-name.git

 

git 4 windows avec un certificat SSL non-reconnu

Une histoire de magasin

J’ai de temps en temps besoin d’utiliser git en ligne de commande lorsque le GUI, en l’occurrence Visual Studio, ne suffit pas. Mais dans ce cas, la ligne de commande peut refuser de faire un pull ou un push si le dépôt git commun est accessible via HTTPS et qu’il utilise un certificat interne. C’est à dire que le certificat présenté par le serveur n’est pas auto-signé, mais signé par une autorité de certification non reconnue, comme c’est souvent le cas en entreprise (la vente de certificat n’est rien de plus qu’une grosse arnaque).

Ce problème existe aussi pour les autres outils graphiques qui sont basés sur git for windows, comme l’excellent TortoiseGit.

On est alors confronté au soucis suivant : même si Windows, Visual Studio, Internet Explorer ou Chrome reconnaissent ce certificat, ce n’est pas le cas de git en ligne de commande sous Windows. La raison est que le magasin des certificats racines n’est pas le même. Ce problème avec git n’existe pas sous un système *NIX. Cela arrive typiquement dans les cas suivants :

  • le navigateur est Firefox
  • l’application est écrite en Java
  • l’application embarque mingw ou similaire

Chacune de ces applications ou runtime utilisent leur propre magasin de certificat, ce qui explique qu’une négociation SSL qui passe sous VStudio ne passe plus avec git.

Faire marcher git en SSL avec un certificat interne

J’indique dans ce billet comment ajouter un certificat racine pour git 4 windows. La commande est extrêmement simple, mais la difficulté a résidé à savoir quel est le fichier à modifier. En effet, si l’on regarde le packaging de git, on peut voir des certificats trainer un peu partout, ce n’est pas très propre:

  • ~/usr/ssl/certs/ca-bundle.crt
  • ~/usr/ssl/certs/ca-bundle.trust.crt
  • ~/usr/ssl/cert.pem
  • ~/etc/pki/ca-trust/extracted/java/cacerts
  • ~/etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt
  • ~/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
  • ~/mingw64/etc/pki/ca-trust/extracted/java/cacerts
  • ~/mingw64/etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt
  • ~/mingw64/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
  • ~/mingw64/share/pki/ca-trust-source/ca-bundle.trust.crt
  • ~/mingw64/ssl/cert.pem
  • ~/mingw64/ssl/certs/ca-bundle.crt
  • ~/mingw64/ssl/certs/ca-bundle.trust.crt

Alors lequel est-ce ? Et bien c’est l’avant dernier.

Une fois qu’on l’a identifié, la manipulation est enfantine : il suffit d’éditer ce fichier et de lui coller tous les certificats racines supplémentaires au format PEM, comme le fait cette commande avec un shell type bash :

cat ca.crt >> /c/Program\ Files/Git/mingw64/ssl/certs/ca-bundle.crt

Cette manipulation sera à refaire après chaque mise à jour de git for windows.

Il existe un paramètre permettant d’indiquer à l’outil en ligne de commande de ne pas vérifier le certificat du serveur. Cela n’est pas du tout équivalent à ma solution en ce sens que ma solution permet bien d’authentifier le serveur, et que cette solution, si elle fonctionne pour git, ne fonctionne pas forcément pour les outils basés sur git comme TortoiseGit.

Extraire un dossier d’un dépôt git avec suivi du renommage

git contient une commande permettant d’extraire un dossier d’un dépôt existant. C’est pratique lorsque le dépôt contenait initialement un seul projet, qui a évolué au cours du temps, grossit, et est devenu un truc trop gros pour rester dans un dépôt unique. Cela se fait avec la commande git –subdirectory , mais cette commande ne suivait pas le renommage du dossier à extraire et l’on se retrouvait donc avec un historique tronqué.

Le script ci-dessous fonctionne (c’est déjà ça), en travaillant sur le dépôt lui-même plutôt que l’espace de travail, lequel ne reflète qu’une version spécifique. Il :

  • ne garde du dépôt que tout ce qui se trouve ou bien dans Kveer.MyAwesomeProject, ou bien dans MyAwesomeProject, qui sont un même dossier que j’ai renommé à un moment donné
  • il fait remonter à la racine le contenu des dossiers susmentionnés
  • quitte à casser tout l’historique, j’en profite pour unifier le nom et l’email utilisé dans les commits (on pourra ajouter une condition pour ne réécrire que ses propres commits)
  • je supprime le dossier package pour réduire la taille du dépôt
  • plus qu’à nettoyer
  • le nouveau dépôt est prêt à être chargé.
# extracts the subdirectory
# i take this opportunity to rewrite author name and email of my commits
git filter-branch --prune-empty --env-filter '
GIT_AUTHOR_EMAIL=my-awesome-email@kveer.fr
GIT_AUTHOR_NAME=Veovis' --index-filter '
    # Delete files which are NOT needed
    git ls-files -z | egrep -zv  "^(Kveer.MyAwesomeProject|MyAwesomeProject)" |
        xargs -0 -r git rm --cached -q
    # Move files to root directory
    git ls-files -s | sed -e "s-\t\(Kveer.MyAwesomeProject\|MyAwesomeProject\)/-\t-" |
        GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
        git update-index --index-info &&
        ( test ! -f "$GIT_INDEX_FILE.new" \
            || mv -f "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE" )
'

# deletes the backup ref
git update-ref -d refs/original/refs/heads/master

# more cleaning by removing a directory that should not be tracked
git filter-branch --index-filter \
  'git rm --ignore-unmatch --cached packages'

# deletes the backup ref
git update-ref -d refs/original/refs/heads/master

# cleans the repo
git gc
git repack -Ad


La commande subdirectory ne doit pas servir à modifier un dépôt existant. En effet il réécrit tous les commits, tous les hash sont différents. Le nouveau dépô n’aura donc plus aucun lien avec l’ancien dépôt et il sera impossible de faire un push autrement que sur un dépôt vierge ou bien en forçant le passage.

Sources