Gitlab + Custom Hooks

With Gitlab (also with Github) it is straight forward to add post-receive web-hooks so actions can be taken after a push event. At the difference of Github, Gitlab is normally self-hosted, which could technically lead to interesting possibilities with custom post-receive (or any other) hooks. Unfortunately it is not possible to add custom-hooks directly from the web interface, it needs to be done under the hood.

Gitlab relies on Gitolite for it’s authorization process, we will make it relies on Gitolite also for git hooks’ management. We will stick to the Gitolite way of decuplating hooks based on the doc, in the section hook chaining

How to make Gitlab custom hooks aware ?

Remember during Gitlab’s installation the following step – copying Gitlab custom post-receive hook to Gitolite hooks directory :

cp ./lib/hooks/post-receive /home/git/.gitolite/hooks/common/post-receive

In order to make Gitlab custom post-receive hook aware, you need to edit the /home/git/.gitolite/hooks/common/post-receive file so it looks like this :

#!/usr/bin/env bash

# This file was placed here by GitLab. It makes sure that your pushed commits
# will be processed properly.

while read oldrev newrev ref
do
  path_to_hook='/home/git/.gitolite/hooks/common/post-receive.secondary.d/'
  pwd=`pwd`
  reponame=`basename "$pwd" | sed s/\.git$//`
  env -i redis-cli rpush "resque:gitlab:queue:post_receive" "{\"class\":\"PostReceive\",\"args\":[\"$reponame\",\"$oldrev\",\"$newrev\",\"$ref\",\"$GL_USER\"]}" > /dev/null 2>&1
  if [ -x "$path_to_hook/$reponame" ];then
    "$path_to_hook/$reponame" "$reponame" "$oldrev" "$newrev" "$ref" "$GL_USER"
  fi
done

How does it work ?

Explanation of the file difference with previous version

  • Line 8 : indicates the directory where the post-receive hooks will be stored
  • Line 12-14 : if a post-receive hook exists for this project execute it

In practice

In practice there will be one post-receive hook per project and the hook should be named after the project. (Do not forget to make it executable)

And that’s about it, from now on every time you will be pushing your project in Gitlab, it will execute the post-receive script located in $path_to_hook and named after the project itself.

Tests

Project name : customhooks
Post-Receive Hook: location is /home/git/.gitolite/hooks/common/post-receive.secondary.d/customhooks

#!/bin/sh

GIT_WORK_TREE=/var/www/blog git checkout -f

Note : the post-receive scripts can be written in any script-able language be it Shell, Ruby, Python, Perl, etc…

After pushing the customhooks project, I will have a copy of my actual project in the directory /var/www/blog . It’s up to you now to have hooks as sophisticated as your needs requires it.

Conclusion

This post shows how to do it specifically for the post-receive hook, but the same logic can be applied to the other available hooks. Remember, Gitolite manages them not Gitlab directly.
Even if genuinely Gitlab does not give you the possibility to add custom hooks, it is an easy feature to add. QED

Advertisements

Gitolite + OpenLDAP

While for small project one can easily manage Gitolite authorizaton permissions manually, this task can get really cumbersome as the project grows and different roles get to have different permissions (ie. devel, qa, etc…)

Companies traditionally rely on a centralized system to handle their users, the groups they belong to and as many information as they actually need (or not), one of them being LDAP. The purpose of this post is to see how to make Gitolite rely on informations stored in an LDAP DIT to grant user to perform specific actions on the git repositories.

Prequisite : In order to follow this post you will need to have a working Gitolite installation (v3.0+) and a reachable LDAP directory.

This is the LDIF file that will be used to handle authentication :

dn: cn=john,ou=group,dc=yanisguenane,dc=fr
cn: john
gidNumber: 20001
objectClass: top
objectClass: posixGroup
memberUid: john

dn: cn=jane,ou=group,dc=yanisguenane,dc=fr
cn: jane
gidNumber: 20002
objectClass: top
objectClass: posixGroup
memberUid: jane

dn: cn=devel,ou=group,dc=yanisguenane,dc=fr
cn: devel
gidNumber: 20003
objectClass: top
objectClass: posixGroup
memberUid: john

dn: uid=jane,ou=people,dc=yanisguenane,dc=fr
uid: jane
uidNumber: 10000
gidNumber: 10000
cn: jane
sn: jane
objectClass: top
objectClass: person
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/bash
homeDirectory: /home/jane

dn: uid=john,ou=people,dc=yanisguenane,dc=fr
uid: john
uidNumber: 10001
gidNumber: 10001
cn: john
sn: john
objectClass: top
objectClass: person
objectClass: posixAccount
objectClass: shadowAccount
loginShell: /bin/bash
homeDirectory: /home/john

Make Gitolite LDAP aware

Thought by default Gitolite is LDAP (and any authentication system) unaware, author left an open door for Gitolite to query a specific authentication system one wants. Be it LDAP or any other queriable system.

They are three rules to make that happen :

  • The query to the authentication system should be done via a script
  • The script should take the username as only parameter
  • The script should return a group space separated list the defined user belongs to

An example of an LDAP script can be find here
Note : It should be edited to meet your LDAP DIT configuration, the link posted matches the LDIF used for this post

In order to make Gitolite LDAP aware one needs to edit the file located at $GITOLITE_HOME/.gitolite.rc. Inside the %RC hash, add the following line :

In v3

GROUPLIST_PGM           =>  '/path/to/ldap-query-groups-script',

In v2

$GL_GET_MEMBERSHIPS_PGM => '/path/to/ldap-query-groups-script',
$GL_BIG_CONFIG => 1,

And … done ! Your Gitolite installation is LDAP aware !

How to use it

  • Add the authorized users to Gitolite keychain
  • As you would do with a regular Gitolite setup, you need to add the user to the Gitolie keychain. The name of the public key file (.pub) should match your LDAP username you want to set up.

    Here, they are two ways to deal with it

    • Full LDAP : get the SSH key from querying your LDAP DIT – if they are stored in here for each user
    • Basic : copy the user public key file via your prefered way

  • Define the repositories and permissions
  • Important : Remember that for a given username, the script will return a list of groups the user belongs to. Hence, your repositories configuration should be group based and not user based. A good practice would be that each user has its individual group, so you can grant access to individual user.

    repo test-ldap-devel
        RW+    =    @devel
    
    repo test-ldap-jane
        Rw+    =    @john @jane
    
  • Finally push the chances
  • Once configured to your needs simply push the changes.

Testing

Session 1 – john

john@workstation-john: ssh-keygen -t rsa -b 1024 -N '' -f ~/.ssh/john
john@workstation-john: scp ~/.ssh/john.pub gitolite@gitolite.myserver.com:~/gitolite-admin/keydir
gitolite@gitolite.myserver.com: git add john.pub && git commit -m "john.pub" && git push origin master
john@workstation-john: git clone gitolite@gitolite.myserver.com:test-ldap-devel.git
Cloning into test-ldap-devel...
warning: You appear to have cloned an empty repository.

Session 2 – jane

jane@workstation-jane: ssh-keygen -t rsa -b 1024 -N '' -f ~/.ssh/jane
jane@workstation-jane: scp ~/.ssh/jane.pub gitolite@gitolite.myserver.com:~/gitolite-admin/keydir
gitolite@gitolite.myserver.com: git add jane.pub && git commit -m "jane.pub" && git push origin master
jane@workstation-jane: git clone gitolite@gitolite.myserver.com:test-ldap-devel.git
Cloning into test-ldap-devel...
FATAL: R any test-ldap-devel jane DENIED by fallthru
(or you mis-spelled the reponame)
fatal: The remote end hung up unexpectedly

jane@workstation-jane: git clone gitolite@gitolite.myserver.com:test-ldap-jane.git
Cloning into test-ldap-jane...
warning: You appear to have cloned an empty repository.

Conclusion

As we can see on Jane’s session, her try to clone test-ldap-devel was denied, but the one to clone test-ldap-jane did work. QED