This post holds my installation notes of a Git server based on Gitolite. The objective is to allow people to collaborate on a project via git i.e. to be able to host Git repositories managed via SSH, yet having a single system account (git in general) and not one per contributor.

Several utilities can be considered at this level:

  • gitosis, the historical one.
  • gitolite, a modern alternative with more fine-grained access control and many (many!) more powerful features.
  • gitlab, a complete web interface offering a similar interface than Github for Git repository management, code reviews, issue tracking, activity feeds and wikis. For gitlab configuration, see gitlab.md

_WARNING_: in the first two cases, once setup, these tools are configured via a git repository (named gitosis-admin or gitolite-admin). To avoid problems if an error is performed on the syntax of the configuration files and is commited that would deny access to the repository, we will always configure the tools to be managed either remotely or locally.

Resources:

Installation

Manual install

Install the required packages

 $> yum install git gitolite3

Create a git user, to ensure shorten URLs upon clone operations (i.e. git clone git@... instead of git clone gitolite3@... ):

 $> adduser --system --shell /bin/sh --comment "git repository hosting with git clone ... URLs shorter" --gid  gitolite3 --home-dir /var/lib/git --create-home git

Create a local SSH keypair for the root user that will be used to pilot the initial configuration of Gitolite:

 $> ssh-keygen -t rsa -b 4096 -o -a 100 -C "Git-Admin"

If you use SELinux, you’ll need to set the appropriate context (see this blog post)

 $> chcon -R unconfined_u:object_r:user_home_t:s0 /var/lib/git/.ssh
 # OR
 $> semanage fcontext -a -t ssh_home_t /var/lib/git/.ssh/
 $> restorecon -v /var/lib/git/.ssh/

Gitolite is now installed, but is not ready for use. The admin SSH key need to be copied to somewhere gitolite can access it, and then run the setup script.

  $> cp /root/.ssh/id_dsa.pub ~git/root@$(hostname -f).pub

Bootstrap Gitolite with the SSH key for the git user as follows:

 $> sudo su - git
 (git)$> gitolite setup -pk root@$(hostname -f).pub
 NOTE: the admin username is 'root@<hostname>'
 Initialized empty Git repository in /var/lib/git/repositories/gitolite-admin.git/
 Initialized empty Git repository in /var/lib/git/repositories/testing.git/
 WARNING: /var/lib/git/.ssh missing; creating a new one
   (this is normal on a brand new install)
 WARNING: /var/lib/git/.ssh/authorized_keys missing; creating a new one
   (this is normal on a brand new install)

Puppet-based install

You can use the echoes/gitolite module to setup a gitolite server, for instance with the following recipe:

1
2
3
4
5
6
7
class { '::gitolite':
    user_name         => 'git',
    home_dir          => '/var/lib/git',
    admin_key_content => $admin_key_content,
    admin_key_source  => $admin_key_source,
    #home_dir         => '/var/lib/git',
  }

Local configuration repository

You can check again that the root user (or whoever has seen his public SSH key configured for the setup of gitolite) will have access to the gitolite-admin repository that hold the gitolite configuration:

1
2
3
4
5
$> ssh git@localhost -p 8022 info
hello admin, this is git@git-admin running gitolite3 3.6.7-3.el7 on git 1.8.3.1

 R W    gitolite-admin
 R W    testing

In case the above command failed with the following error message:

   Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).

The reason might be linked to SELinux and you may see an error in the audit log (/var/log/audit/audit.log) on the server as follows:

    Apr 14 10:03:46 <hostname> setroubleshoot: SELinux is preventing /usr/sbin/sshd from read access on the file authorized_keys. For complete SELinux messages. run sealert -l eacc55af-fdf1-4f19-9428-6f598236a260
    Apr 14 10:03:46 <hostname>  python: SELinux is preventing /usr/sbin/sshd from read access on the file authorized_keys.

See above for the expected fix – typically restorecon -R -v /path/to/.ssh.

And the root user can now clone the gitolite-admin repository (this step is already handled by the Puppet profile ::profile::gitolite):

1
2
3
$> mkdir -p ~/git/localhost
$> git clone ssh://git@localhost:8022/gitolite-admin.git
$> cd gitolite-admin

Gitolite Admin repository layout

The layout of the gitolite-admin repository can be organized as follows:

1
2
3
4
5
6
7
├── conf/
│   └── gitolite.conf    # The main configuration for gitolite
└── keydir/              # the directory holding the SSH keys
    ├── admins/             # the admins keys
    ├── admin.pub
    ├── servers             # the servers keys (i.e. root etc.)
    └── users               # users SSH keys

SSH Key filename conventions

There is a 1:n mapping between users and ssh public-keys under gitolite. A given user may have one or more registered keys, each in a separate key file under gitolite-admin/keydir/. The keyfile name is significant and determines the user’s identity assumed by the system upon demonstration of possession of the matching private-key. The basic pattern for a keyfile name is one of the following:

  • user.pub
  • user@qualifier.pub (where qualifier does not include a dot)
  • user@host.dom.pub (where user@host.dom is an e-mail address with at least one dot in the hostname)

In the first two cases, the associated identity for access control in the conf/gitolite.conf file is user. In the latter case, it is user@host.dom. The first two cases allow a user to register multiple keys (e.g. user.pub and user@laptop.pub) while still affording the same access privileges to both keys. The latter form allows non-local users to be identified in a way that preserves their contact information.

In principle the user name be arbitrary and can start with any alphanumeric character and contain dots, dashes and underscores (e.g. John-Smith).

In practice, the following convention can be used:

  • admin keys are stored under keydir/admins/
  • regular users keys are stored under keydir/users/
    • Each users key(s) are stored in a dedicated directory named firstname.name@domain.com/, where firstname.name@domain.com is a valid email adress for this user
    • thus a key for a user is username@domain.com/username@domain.com.pub

Completing the configuration of gitolite-admin for remote management

as root on git-admin.hpc

1
2
3
4
5
$> cd /root/git/localhost/gitolite-admin
$> mkdir -p keydir/admins/firstname.name@domain.com
$> vim keydir/admins/firstname.name@domain.com/fname.pub     # add public key
$> git add keydir/admins/firstname.name@domain.com/fname.pub
$> git commit -s -m "[gitolite-admin] NEW_KEY: add admin key for Firsname Lastname"

You need also to update the default configuration conf/gitolite.conf to allow this key:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
+#####################
+# Group definitions #
+#####################
+# General format:
+#      @group = username1 username2
+#
+# /!\ WARNING: DO NOT USE any .pub extension in this config file
+#
+
+# Admins
+@ORGadmins = fname
+
+###########################
+# Gitolite administration #
+###########################
+
 repo gitolite-admin
-    RW+     =   admin
+    RW+     =   admin  @ORGadmins

 repo testing
     RW+     =   @all

Now you should be able to clone remotely the repository and feel more comfortable on your local workstation to update the repository.

1
2
3
4
5
6
7
$> ssh git@<hostname> -p 8022 info
hello svarrette, this is git@<hostname>running gitolite3 3.6.7-3.el7 on git 1.8.3.1

 R W	gitolite-admin
 R W	testing

$> git clone ssh://git@<hostname>/gitolite-admin.git

Common [sysadmins] operations

See also:

Add a new user or a new key

Update your local copy of the repository

	$> git up

Follow the above convention to add the public key. Examples, to add a new UNI user:

	$> cd keydir
	$> mkdir -p users/firstname.name@uni.lu
	$> cp /path/to/user_id_dsa.pub users/firstname.name@uni.lu/fname.pub

Add the key to the git repository and commit your changes (with your commit message prefixed with [gitolite-admin] NEW_KEY:)

	$> git add users/firstname.name@uni.lu/fname.pub
	$> git commit -s -m "[gitolite-admin] NEW_KEY: add key for user F. Name"

Edit the file conf/gitolite.conf, go into the section Users group definitions and create a group for this user:

1
2
3
4
5
6
7
8
9
###########################
# Users group definitions #
###########################
# General format:
#	   @firstname.name = ssh_id1  ssh_id2
#
# /!\ WARNING: DO NOT USE any .pub extension in this config file
#
@firstname.name = fname

Commit your changes (as always, prefixed with [gitolite-admin]) and push them

$> git commit -s -m "configuration for user F. Name"
$> git push origin

Creating a new repository

You have to edit the file conf/gitolite.conf and add a new repo entry.

Typical addition of the repository mynewrepo

repo    mynewrepo
desc = "My new repo"
RW+  = admin @ORGadmins

You should have at least in the RW+ field entries for admin AND @ORGadmins. Then feel free to add other groups/users etc.

Adding a description permits to populate the file ~git/repositories/<reponame>/description assuming you enable the cgit extension.

1
2
3
4
5
6
7
8
9
10
11
--- /var/lib/git/.gitolite.rc.old 2018-04-19 11:34:44.973492253 +0200
+++ /var/lib/git/.gitolite.rc 2018-04-19 11:35:09.884569024 +0200
@@ -156,7 +156,7 @@
             # 'upstream',

             # updates 'description' file instead of 'gitweb.description' config item
-            # 'cgit',
+            'cgit',

             # allow repo-specific hooks to be added
             # 'repo-specific-hooks',
  • Once you finished the configuration, commit your changes with your commit message prefixed with [gitolite-admin] NEW_REPO:)

      $> git commit -s -m "[gitolite-admin] NEW_REPO: add repo 'mynewrepo'
    
  • push your changes onto git-admin:

      $> git push origin
    
  • Now you can clone locally the newly created repository

      $> git clone ssh://git@<hostname>/reponame.git
      [...] bla bla work commit
      $> git push -u origin master
    

Using the mailing-list hook

See blog post

You first need to setup the hook script post-receive-email on the server as ~git/.gitolite/hooks/common/post-receive, just copy the one located in /usr/share/doc/git-1.8.3.1/contrib/hooks/post-receive-email

1
2
3
4
5
# On <hostname>
$> cp /usr/share/doc/git-1.8.3.1/contrib/hooks/post-receive-email ~git/.gitolite/hooks/common/post-receive
$> chown git: ~git/.gitolite/hooks/common/post-receive
# Propagate the hook within all repositories in ~git/repositories/*.git/hooks
$> sudo -u git -- gitolite setup --hooks-only

Now you should setup ~git/.gitolite.rc to change GIT_CONFIG_KEYS as ‘.*’. The cgit part is made to populate automatically the description file of each repository:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
--- /var/lib/git/.gitolite.rc.old 2018-04-19 11:34:44.973492253 +0200
+++ /var/lib/git/.gitolite.rc 2018-04-19 11:35:09.884569024 +0200
@@ -24,7 +24,7 @@
     UMASK                           =>  0077,

     # look for "git-config" in the documentation
-    GIT_CONFIG_KEYS                 =>  '',
+    GIT_CONFIG_KEYS                 =>  '.*',

     # comment out if you don't need all the extra detail in the logfile
     LOG_EXTRA                       =>  1,
@@ -156,7 +156,7 @@
             # 'upstream',

             # updates 'description' file instead of 'gitweb.description' config item
-            # 'cgit',
+            'cgit',

             # allow repo-specific hooks to be added
             # 'repo-specific-hooks',

Not doing this will lead to the error upon pushing

1
2
remote: FATAL: git config 'hooks.mailinglist' not allowed
remote: check GIT_CONFIG_KEYS in the rc file

Now you can setup the repositories for which you want to send email notifications. Symply add the entries:

 config hooks.mailinglist = "youmailinglist"
 config hooks.emailprefix = "[GIT] reponame: "

to the appropriate repo.

You can also set this option for all repositories:

1
2
3
repo @all
    config hooks.mailinglist = "firstname.name1@domain.org, firstname.name2@domain.org"
    config hooks.emailprefix = "[%GL_REPO] "

… Or define a group of repositories for which you want to see this activated.

Note that you probably want in this case to setup the desc = "<description>" attribute such that, assuming the cgit entension is enabled, you don’t receive mails with subject like that:

<emailprefix>: UNNAMED PROJECT branch master updated. acb42a341617af8e293bc058c70cebcbc46f419f

It is also important to version the .gitolite.rc file under the gitolite-admin repository – normally it is not the case (see reference documentation), yet for some reason the file is reset to default (loosing the above mentioned modifications) if you don’t do it.

As indicated in the reference doc), just add it to your gitolite-admin repo, push the change, then replace ~/.gitolite.rc with a symlink to ~/.gitolite/.gitolite.rc as git.

1
2
3
$> cd ~git
$> rm .gitolite.rc      # You can always recover the default one with 'gitolite print-default-rc'
$> sudo -u git ln -s .gitolite/.gitolite.rc .

List available repositories

From your local machine, simply run:

1
2
3
4
5
$> ssh git@<hostname> -p 8022 info
hello svarrette, this is git@git-admin running gitolite3 3.6.7-3.el7 on git 1.8.3.1

 R W	gitolite-admin
 R W	testing