Conçu à partir de 2005 [1] par Linus Torvalds pour les propres besoins de l'équipe de développement du noyau Linux, le système Gitde gestion de versions de code source s'est depuis largement imposé à l'extérieur de cette communauté. Libre, rapide, distribué, très riche en fonctions et pourtant assez simple à utiliser, il est aujourd'hui devenu presque incontournable pour historiser, comparer, collaborer sur tous types de projets de développement, et même souvent pour d'autres sortes de documents.
1. Quelles solutions pour du Git privé ?
Si l'outil Git peut s'utiliser en premier lieu en local, sa puissance lui vient de ses facultés de travail collaboratif et distribué. Un serveur de dépôts sera donc au centre de tout projet de développement ayant fait le choix de Git comme système de gestion de versions. Il existe plusieurs offres SaaS, GitHub en tête, pour héberger des dépôts Git, qu'ils soient publics ou privés. Elles fournissent en général un service allant bien au-delà de l'historisation du code : on y retrouve des outils de planification de projet, de suivi de tickets, des forums de discussions, voire des CDN pour la distribution de binaires.
Mais certaines organisations préfèrent s’orienter vers des solutions dont elles gardent la complète maîtrise en ce qui concerne la propriété des informations, la topologie du réseau et l’administration.
Les solutions libres ne manquent pas pour auto-héberger un service de dépôts Git similaire au célébrissime GitHub [2]. Parmi les plus populaires, citons le projet GitLab en Ruby, déjà évoqué dans ces colonnes [3]et dont des captures sont présentées dans les figures 1 et 2, GitPrep développé en Perl (voir figure 3), ou encore Gogs en langage Go (voir figure 4). Des sociétés proposent des formules en mode SaaS autour de ces logiciels, mais le réel atout de ces projets est qu’ils sont libres et installables sur des serveurs privés.
Néanmoins, une structure qui a déjà ses outils pour la gestion des bugs, la conduite de projet, la documentation collaborative, etc., peut souhaiter s’équiper simplement d’un serveur Git interne qui ne remplisse que cette fonction. Nous présentons dans cet article le logiciel Gitolite, qui (ne) sert (qu’) à héberger et administrer ses dépôts Git chez soi.
Fig. 1 : GitLab : graphiques de temps de projet consommé.
Fig. 2 : GitLab : réseau de branches.
Fig. 3 : GitPrep.
Fig. 4 : Gogs.
2. Gitolite
Gitolite [4] est un programme proposé par l’indien Sitaram Chamarty, sous la forme d’un ensemble de scripts Perl, savamment ficelés pour offrir une gestion simplifiée de dépôts Git servis sur le protocole SSH.
L’outil s’inscrit tout à fait dans la philosophie UNIX, par le fait qu’il s’attache à résoudre une (seule) problématique, et la résout bien.
2.1 Anatomie d’un serveur Git
Dans les grandes lignes, un dépôt Git n’est autre qu’un répertoire .git contenant des fichiers dans un format particulier, qui représentent tout l’historique des changements, dans toutes les branches. La working copy du dépôt est simplement une vue sur un commit donné, qui matérialise la reconstitution de chaque fichier source comme la résultante incrémentale de tous les changements jusqu’à ce niveau de commit. Un bare clone est un clone sans working copy matérialisée.
Par sa nature distribuée, lorsqu’on fait un git clone du dépôt, c’est bien tout cet historique qui est rapatrié. Ainsi chaque clone est à son tour lui-même un dépôt intégral et il y a équivalence des rôles entre les deux, d’où le choix du terme clone (une étude plus approfondie des arcanes de git permettra de nuancer l’affirmation précédente, car il y a des subtilités telles que la configuration, les reflogs et les hooks ; mais en première approche nous pouvons nous contenter de cette représentation).
La prise en charge des permissions, la gestion des pull request, ou encore les systèmes de tickets et autres Wiki, bien que très intégrés aux plateformes de type GitHub, sont autant de services extérieurs au dépôt Git en lui-même. Les produits libres tels que GitLab, qui embarquent également ces modules, s’articulent donc généralement au minimum sur un service HTTP, un moteur de bases de données, un gestionnaire de file d’attente.
L’approche choisie par l’outil Gitolite est d’offrir le contrôle d’une couche d'habilitation SSH par clés, au-dessus du file system où se trouvent les dépôts Git à servir. L'authentification est ainsi nativement assurée par SSH, et les permissions sont gérées par Gitolite au niveau des différents dépôts : les utilisateurs ne sont pas des comptes Linux du serveur, mais des utilisateurs virtuels matérialisés par leur clé publique, comme nous allons le détailler plus bas.
Il n’y a donc pas d’éditeur Web-based, de suivi de pull request, ni de fil de discussion sur un commit ; mais il n’y a pas non plus de base de données ni de serveur d’application ! Les dépôts sont de simples dossiers sur le serveur, et le protocole git des clients est porté par SSH : c’est simple, sûr et léger.
2.2 Installation
La version de Gitolite en vigueur à la date de rédaction de cet article est la 3.6.7. Nous partons d'uneDebian Jessie ou Stretch, dont nous supposerons le hostname égal à gitserver.
Les manipulations ont également été testées sur un containerDocker. Prendre l’image corbinu/ssh-servercar, puisque Gitolite s’articule autour du service SSH, il est très préférable, pour l’envelopper facilement dans un container, de partir d’une image qui encapsule déjà ce service.
2.2.1 Préparation de la station d’administration
Gitolite n’autorise que l’authentification par clés, à l’exclusion de tout mot de passe. Pour administrer le serveur, vous devez vous munir d’une paire de clés d’administration. Sur votre station, générez-la si nécessaire avec :
$ ssh-keygen
Puis, téléversez la clé publique sur le serveur à un emplacement provisoire que nous réutiliserons dans la suite :
$ scp ~/.ssh/id_rsa.pub gitserver:/tmp/admin.pub
$ssh gitserver chmod 444 /tmp/admin.pub
La clé privée est à conserver soigneusement : c’est grâce à elle que toutes les opérations d’administration sur Gitolite sont possibles (gestion des dépôts, gestion des utilisateurs). Néanmoins si elle venait à s’égarer, vous pourrez vous référer à la section 2.5.2 ci-dessous.
2.2.2 Mise en place du serveur
Connectons-nous maintenant au serveur. L'installation des paquets se fait en root (ou en sudo). Créons le user technique git de façon non interactive, sans informations finger grâce à l'option --gecos "", et sans possibilité de login par mot de passe :
gitserver # apt-get update && apt-get install -y git-core
gitserver # adduser --disabled-password --gecos "" git
Les dépôts Git seront stockés dans l'arborescence Home du usergit (par défaut /home/git). Notez que si vous souhaitez que les fichiers de cet user technique (et donc l’ensemble des repositories) soient hébergés ailleurs, vous pouvez préciser l'option --home DIR à la commande adduser.
Puis, toujours sur le serveur, nous passons sous l'identité git pour la préparation du service Gitolite.
gitserver # su gitgitserver $gitserver $ cd ~gitserver $ mkdir bingitserver $ echo "export PATH=$HOME/bin/:$PATH" >> .bashrcgitserver $ git clone https://github.com/sitaramc/gitolitegitserver $ gitolite/install -to $HOME/bin/gitserver $ bin/gitolite setup -pk /tmp/admin.pub
console
Le script gitolite/install prépare les exécutables (qui sont en fait des scripts Perl) pour la gestion des repositories. Une fois en place, le script bin/gitolite setup prépare la structure de fichiers que votre meta-repository va utiliser.
Vous obtenez une sortie qui ressemble à ceci :
Initialized empty Git repository in /home/git/repositories/gitolite-admin.git/
Initialized empty Git repository in /home/git/repositories/testing.git/
WARNING: /home/git/.ssh missing; creating a new one
(this is normal on a brand new install)
WARNING: /home/git/.ssh/authorized_keys missing; creating a new one
(this is normal on a brand new install)
Ceci, comme indiqué dans le WARNING, est parfaitement normal.
Le script d’installation a préparé deux repositories : gitolite-admin, et testing. Ce dernier nous permettra de vérifier la bonne connectivité au serveur depuis les stations, ce que nous faisons ci-dessous. Le premier est le méta-dépôt par lequel Gitolite nous permet de gérer les dépôts et les permissions, comme nous le verrons.
Votre structure de fichiers sous /home/git(ou tout autre répertoire maison choisi pour ce compte) se présente désormais comme ceci (seules les lignes intéressantes sont rapportées ici) :
gitserver # ls -la /home/git
drwxr-xr-x 7 git git 4096 Jan 7 08:19 .
drwxr-xr-x 3 root root 4096 Jan 7 08:16 ..
drwx------ 2 git git 4096 Jan 7 08:19 .ssh
drwxr-xr-x 7 git git 4096 Jan 7 08:19 bin
drwxr-xr-x 6 git git 4096 Jan 7 08:19 gitolite
drwx------ 4 git git 4096 Jan 7 08:19 repositories
2.2.3 Vérification
C’est tout ! À ce stade votre machine gitserver est d’ores et déjà en mesure de servir le dépôt de test livré avec l’installeur. Repassons sur la station :
$ git clone git@gitserver:testingCloning into 'testing'...
warning: You appear to have cloned an empty repository.
Checking connectivity... done.
La station est capable de contacter le gitserver par SSH, au moyen de votre clé privée. Le dépôt testing fourni par l’installation est vide, mais son clonage permet de valider sa réussite.
2.3 Configuration
Une fois le serveur opérationnel, toute son administration se fait via le dépôt spécial gitolite-admin. Il est affecté à l’administrateur, c’est-à-dire l’utilisateur qui détient la clé privée correspondant à la clé publique d’administration indiquée lors de l’installation de gitolite à l’étape 2.2.2. Dans ce dépôt, c’est la branche master qui fait foi pour la configuration active.
2.3.1 Le dépôt gitolite-admin
Avant de pouvoir créer de nouveaux dépôts ou déclarer des utilisateurs, il faut donc cloner le méta-dépôt gitolite-adminsur la station :
$ git clone git@gitserver:gitolite-admin
Cloning into 'gitolite-admin'...
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 6 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (6/6), done.
Checking connectivity... done.
laptop $ cd gitolite-adminlaptop $ tree
.
|-- conf
| `-- gitolite.conf
`-- keydir
`-- admin.pub
Le fichier gitolite.conf est celui dans lequel sont déclarés les dépôts et les habilitations. Le répertoire keydir est celui dans lequel il faut déposer les clés publiques des users virtuels. Nous en détaillerons plus bas la structure.
Le gitolite-adminétant lui-même un dépôt cloné, toute modification est à faire remonter au serveur par un git push (précédé des git add et git commit comme il se doit) afin que la configuration en vigueurse mette à jour.
2.3.2 Gestion des dépôts
Pour ajouter un dépôt, vous déclarez simplement une nouvelle section dans le fichier conf/gitolite.conf dans votre clone du gitolite-admin, selon ce format :
repo mon-depot
RW+ = gabriel R = alice
Une fois le push effectué, les scripts Gitolite créent automatiquement le dépôt sur le serveur.
Pour supprimer un dépôt, il ne suffit pas d’en ôter sa déclaration dans ce fichier, car ceci se contente de rendre le dépôt inaccessible à distance, mais sans destruction des fichiers du sous-répertoire correspondant dans home/git/repositories. Vous devrez encore vous connecter en SSH sur le serveur, et effacer manuellement le répertoire du dépôt :
gitserver $ sudo rm -rf /home/git/repositories/mon-depot
Le fichier de configuration obéit à une grammaire simple, mais riche [6], qui permet entre autres de créer des groupes d’utilisateurs, d’attribuer des droits sur certaines branches et pas d’autres sur la base d’expressions rationnelles, et de segmenter les règles complexes en inclusions de plusieurs fichiers de configuration.
@devteam1 = gabriel tristan bob@devteam2 = alice bob johnrepo mon-depot RW+ = @devteam1 R = jim RW develop = jiminclude "more-repos.conf"
2.3.3 Gestion des utilisateurs
Les utilisateurs sont des identifiants déclarés dans le fichier gitolite.confcomme vu plus haut. L’authentification repose sur la paire de clé de chaque utilisateur : les clés publiques doivent être stockées (commit, push !) dans le répertoire keydir du dépôt gitolite-admin. Vous y trouverez en premier lieu la clé admin.pub fournie lors de l’installation. Les fichiers doivent porter l’extension .pub, et le nom de fichier correspond alors au nom d’utilisateur pour le dépôt git.
$ cd gitolite-admin
$ tree
.
|-- conf
| `-- gitolite.conf
`-- keydir
|-- admin.pub
|-- alice.pub
|-- laptop
| |-- admin.pub
| `-- gabriel.pub
|-- desktop1
| `-- gabriel.pub
`-- office
`-- bob.pub
Gitolite accepte plusieurs clés publiques pour un même utilisateur, afin de lui permettre de s’authentifier depuis ses différentes machines, chacune avec sa clé privée.
Puisque pour Gitolite un utilisateur correspond au nom de fichier de sa clé publique, toutes les clés publiques d’un même utilisateur doivent porter le même nom de fichier. Mais Gitolite est ouvert quant à la structure des dossiers sous keydir. Il y en a ainsi pour tous les goûts ; dans l’exemple ci-dessus, nous proposons des répertoires pour chaque hôte client ou organisation cliente, à l’intérieur desquels des clés publiques différentes se répartissent, même si leur contenu peut être différent pour un même nom de fichier. Cette hiérarchisation n’est pas incompatible avec le fait de laisser des clés publiques directement sous keydir. Lorsqu’une connexion entre, Gitolite tente de résoudre le nom d’utilisateur par rapport à la clé privée SSH fournie : pour cela, il n’hésite pas à parcourir toutes les clés des sous-répertoires de keydir, jusqu’à trouver la bonne et en déduire l’utilisateur (i.e. le nom de fichier).
Cependant, pour se connecter au gitserver via la commande git, il faut toujours spécifier l'utilisateur git SSH :
$ git clone git@gitserver:mon-depot
La clé privée que le protocole SSH sous-jacent apporte est bien celle de l’utilisateur Linux qui exécute la commande. C’est elle qui sera utilisée par Gitolite pour calculer l’utilisateur du dépôt.
Ce mécanisme est réalisé par les scripts Perl qui composent le logiciel Gitolite, et qui sont notamment activés lors de la connexion SSH via git. Voici ce qu’on trouve sur le serveur dans /home/git/.ssh/authorized-keys :
# gitolite start
command="/home/git/bin/gitolite-shell admin",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1yc2EAA...MZyWKMT23X2wHbQp gabriel@laptop
command="/home/git/bin/gitolite-shell alice",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAbQ247rCFkwWx87...5CgtALOUCCIpeQ5d alice@desktop
command="/home/git/bin/gitolite-shell gabriel",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAGDFGDFbo987BD...VJDF8G8BUCChvi45 gabriel@windauze
...
# gitolite end
Le user git est donc celui par lequel tout passe, via le protocole SSH, et c’est grâce à un shell transformé (et limité) que Gitolite effectue son travail.
Ce fichier authorized_keys du usergit est maintenu automatiquement par Gitolite lors des push sur le dépôt gitolite-admin, conformément aux clés déposées dans le répertoire keydir.
2.4 Sauvegarde
Un dépôt git contenant déjà toute sa propre histoire, il suffirait en théorie pour archiver un dépôt, d’archiver le répertoire qui le contient. On pourrait planifier en cron un naïf :
$ tar czf mon-depot.tar.gz /home/git/repositories/mon-depot
Mais cela nous exposerait au risque d’enregistrer des données corrompues. En effet, si le serveur git est sollicité (push) pendant l’opération tar, les fichiers spéciaux qui représentent le dépôt seront capturés dans l’archive dans un état incohérent.
Une solution consiste à interrompre provisoirement le service SSH sur le serveur gitolite, juste le temps de réaliser le tar, afin de garantir qu’aucun utilisateur distant ne modifie l’état du dépôt pendant son archivage. Mais cela peut s’avérer violent pour les utilisateurs, surtout si vous archivez beaucoup de gros dépôts de nombreuses fois par jour aux heures de pointe.
Une approche plus sûre consiste à effectuer localement sur le serveur une réplique propre et cohérente du dépôt à partir du chemin local du dossier d’origine, avant d’archiver le répertoire ainsi obtenu.
$ git clone --mirror mon-depot /tmp/mon-depot
$ tar czf mon-depot.tar.gz /tmp/mon-depot
Le clone --mirror est bare, c’est-à-dire qu’il n’embarque pas de working copy. La réplique présentera quelques différences avec l’original (perte des hooks comme vu plus haut, notamment), mais si la sauvegarde vise seulement la mise en sécurité des fichiers sources et de leur historique, cette méthode fera l’affaire.Les hooks étant des scripts que l’administrateur enregistre directement dans le répertoire /home/git/repositories/mon-depot/hooks du serveur, leur archivage en mode fichier depuis le serveur est inoffensif (un bon administrateur les aura d’ailleurs placés lui-même en source-control dans leur propre dépôt).
2.5 Résolution des problèmes
Parce que ce ne serait pas intéressant s’il n’y en avait pas, voyons-en deux des plus courants.
2.5.1 Paramétrage de la Locale
Vous aurez peut-être constaté, en manipulant, que toutes vos commandes git vis-à-vis du serveur gitserver émettent un warning du type :
$ git fetch
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANGUAGE = (unset),
LC_ALL = (unset),
LC_PAPER = "fr_FR.UTF-8",
LC_ADDRESS = "fr_FR.UTF-8",
LC_MONETARY = "fr_FR.UTF-8",
LC_NUMERIC = "fr_FR.UTF-8",
LC_TELEPHONE = "fr_FR.UTF-8",
LC_IDENTIFICATION = "fr_FR.UTF-8",
LC_MEASUREMENT = "fr_FR.UTF-8",
LC_TIME = "en_US.UTF-8",
LC_NAME = "fr_FR.UTF-8",
LANG = "en_US.UTF-8"
are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
Il suffit d’indiquer aux processus sur gitserver quels paramètres d’environnement utiliser concernant la Locale. Ajoutez sur le serveur en tant que root les lignes suivantes au fichier /etc/environment (à créer si besoin) :
LANGUAGE=en_US.UTF-8
LANG=en_US.UTF-8
LC_ALL=en_US.UTF-8
Puis exécutez en root :
gitserver # locale-gen en_US.UTF-8
2.5.2 Enfermé dehors ?
Qui n’a jamais perdu sa clé privée me jette la première paire.
La clé d’admin ouvre le sésame de la configuration distante de Gitolite, mais sans elle tout reste possible (à condition d’avoir toujours accès au serveur…).
Le plus simple est de se connecter au serveur, d’y cloner le méta-dépôt et de déclarer une nouvelle admin key. Commençons par générer une nouvelle clé d’administration sur la station, puis téléversons-la vers le serveur :
$ ssh-keygen -f ~/.ssh/new-admin
$ scp ~/.ssh/new-admin.pub gitserver:/tmp/
Puis, sur le serveur :
gitserver # git clone /home/git/repositories/gitolite-admin.git
gitserver # mv /tmp/new-admin.pub keydir/admin.pub
gitserver # git add keydir/admin.pub
gitserver # git commit -m "c’est pas malin de perdre sa clé d’admin"
gitserver # gitolite push
Notez la commande gitolite plutôt que git pour le push ! En effet, l'exécutable gitolite, sur le serveur, permet de contourner les vérifications (hooks) lors du push, et c'est nécessaire ici lorsque le remote est en réalité un chemin du filesystem local.
Cette opération a pour effet de mettre à jour le authorized_keys du realusergit, et vous pourrez de nouveau reprendre le contrôle à distance sur gitolite-admin.
En réalité, il est également possible de forcer gitolite à obtempérer en remplaçant simplement la clé publique directement dans le fichier authorized_keyssur le serveur, mais il faudra tout de même cloner aussitôt gitolite-adminsur la station pour y inscrire la nouvelle clé admin.pub sous keydir, sans quoi la prochaine régénération automatique du authorized_keys écraserait votre nouvelle clé publique d’administration.
2.5.3 Beaucoup de remotes et de clés
Parce que jamais deux sans trois.La faute n'est pas vraiment à Gitolite, mais ce problème-ci se rencontre suffisamment souvent pour mériter sa place ici : que faire pour instruire git d'utiliser la clé de votre choix (parmi votre trousseau bien fourni) pour sa connexion à votre gitserver ?
En effet, sans indication contraire, la commande git clone git@gitserver:mon-depot utilisera tacitement comme clé votre autre clé par défaut pour votre utilisateur (généralement le ficher ~/.ssh/id_rsa). Deux inconvénients se présentent : que faire si ma clé pour mon user (virtuel) Gitolite n'est pas ce fichier ? Que faire si je veux utiliser, depuis la même station, tantôt mon compte Gitolite nominatif, tantôt le compte d'administration ? Enfin, comment simplifier l'écriture et se dispenser du git@ ?
La solution se trouve dans la configuration ~/.ssh/config sur la station. En ajoutant une section pour le gitserver, vous pouvez instruire SSH (et donc git) quant à la clé associée :
Host gitserver
Hostname gitolite.domain.tld
User git
IdentityFile ~/.ssh/gitolite_gabriel_id_rsa
IdentitiesOnly yes
Cet extrait précise (si besoin) l'adresse complète de l'hôte pour le surnom gitserver, le compte à utiliser (pour s'affranchir de taper systématiquement git@) et la clé privée à exploiter.
En complétant avec l'extrait suivant, vous pourrez cloner le dépôt d'administration avec votre clé spéciale pour l'admin, à condition d'utiliser le nouveau surnom git-admin.
Host gitserver
Hostname gitolite.domain.tld
User git
IdentityFile ~/.ssh/gitolite_gabriel_id_rsa
IdentitiesOnly yes
Host git-admin
Hostname gitolite.domain.tld
User git
IdentityFile ~/.ssh/gitolite_admin_id_rsa
IdentitiesOnly yes
Exécution par :
$ git clone git-admin:gitolite-admin
Notez au passage que cette astuce peut très bien s'exploiter face à GitHub par exemple, si vous devez manipuler différents dépôts ayant chacun sa Deploy Keyunique [7].
Conclusion
Nous n’avons fait que survoler les fonctionnalités essentielles de l’outil, qui en offre beaucoup plus en particulier en ce qui concerne les règles de gestion des permissions au niveau des push (conditions sur le nombre de fichiers impactés, contrôle et interdiction de modifier certains fichiers, heures de travail, etc.). En outre, le logiciel est évolutif au moyen de scripts « non-core » qu’il est possible d’ajouter (tels que la prise en charge du HTTP, du LDAP, les répliques pour la haute disponibilité, ou encore l’interconnexion avec des systèmes de gestion de projet ou d’analyse de code), ce qui en fait une solution flexible à souhait, sans compromis sur la robustesse, et qui respecte la confidentialité de vos documents.
Références
[1] A Short History of Git : https://git-scm.com/book/en/v2/Getting-Started-A-Short-History-of-Git
[2] Solutions d'hébergement de dépôts Git : https://alternativeto.net/software/github/
[3] ZORES B., « GitLab : gérez vos projets open source à grande échelle », GNU/Linux Magazine n°168, février 2014 : https://connect.ed-diamond.com/GNU-Linux-Magazine/GLMF-168/GitLab-Gerez-vos-projets-open-source-a-grande-echelle
[4] Site du projet Gitolite : https://github.com/sitaramc/gitolite
[5] BLANC C., « Déployer un environnement de déploiement avec Debian-Installer, Ansible et Git », GNU/Linux Magazine n°210,décembre 2017 : https://connect.ed-diamond.com/GNU-Linux-Magazine/GLMF-210/Deployer-un-environnement-de-deploiement-avec-debian-installer-Ansible-et-Git
[6] Référence complète du fichier de configuration Gitolite : http://gitolite.com/gitolite/conf/
[7] Les Deploy Keys avec GitHub : https://developer.github.com/v3/guides/managing-deploy-keys/#deploy-keys