Git is one of the most popular version control systems currently available, and several thousands of projects, new and old, have started using it in the past few years. You might have also used it, and shortly after, realized that Git does not do much for Access Control by itself. You need an access control system that is simple and quick to install and get running, yet flexible and powerful enough for your future needs.
This chapter will describe what Gitolite is, and why you might need it. It shows a few examples of the basic features, and also shows you how you can try out Gitolite safely. It assumes that you have some basic knowledge of Git, and have used Git both locally and with a remote repository.
Git server administrators face a bit of a challenge. The high uptake rate of Git means that there are thousands of developers who are not really familiar with Git, and who therefore may be running Git commands that cause irreversible or highly disruptive changes to the Git repository. Furthermore, Git itself does not help much with this; whatever access controls it has, apply to the entire repository, and cannot be made more fine-grained.
For instance, the master branch in most projects represents the most stable code. Yet, a junior developer can easily run a command such as
git push origin +master, (which pushes the developer's local branch onto the server) and thus overwrite weeks or months of hardwork by the rest of the team. People with deeper Git expertise can probably recover the lost commits, but it would certainly take time and effort.
Worse, Git's command syntax sometimes makes it worse. For example, the command to delete the master branch is only slightly different from a normal push, that is,
git push origin :master (notice the extra colon?).
The most common need, therefore, is to prevent these kinds of accidents: overwriting (or rewinding) one or more commits, and deleting a branch or a tag.
Git itself does provide some measure of protection. You can set the config items
true. Unfortunately, this is a bit of a blunt instrument—now no one can delete or rewind any branch!
In larger setups with several repositories and several developers, you may also be concerned about allowing everyone to read all repositories. Or it may be that some roles (such as a test engineer) should not need to write to the repository; read-only access is sufficient. Up to a certain point, this problem can be solved with Unix permissions and user/group permissions applied judiciously. Perhaps even POSIX ACLs can be used if you're comfortable with that sort of thing.
Each Git user needs a corresponding Unix user ID on the server.
Managing access rights can only be done by using the
Checking the current set of permissions is not straightforward. You will need to run multiple commands and correlate their output manually.
Auditing changes in permissions over time is impossible because no history is kept by the system.
These disadvantages require a lot of effort to manage even a few repositories and users, and even a modestly sized setup would quickly become unmanageable.
We will see how simple Access Control can be with Gitolite. First, here's an example where the junior developers (let's call them Alice and Bob here) should be prevented from rewinding or deleting any branches, while the senior developers (Carol and David) are allowed to do so:
We will see this in more detail in later chapters, but Gitolite uses a plain text file to specify the configuration, and these access rules are placed in that file.
repo foo RW = alice bob RW+ = carol david
You probably guessed that the
RW stands for read and write. The
+ in the second rule stands for force, just as it does in the
push command, and allows you to rewind or delete a branch.
Now, suppose we want the junior developers to have some specific set of branches that they should be allowed to rewind or delete, a sort of "sandbox", if you will. The following command will help you to implement that:
RW+ sandbox/ = alice bob
Alice and Bob can now push, rewind, or delete any branches whose names start with
Access Control at the repository level is even easier, and you may even have guessed what that looks like:
repo foo RW+ = alice R = bob repo bar RW+ = bob R = alice repo baz RW+ = carol R = alice bob
As you can see, you have three users with different access permissions for each of the three repositories. Doing this using the file systems' permissions mechanisms or POSIX ACLs would be doable, but quite cumbersome to set up and to audit/review.
The access control examples show the most commonly used feature of Gitolite, the repository and branch level access control, but of course Gitolite has many more features. In this section, we will briefly look at a few of them while noting that there are many more waiting in the wings for you to find as you read this book.
Gitolite allows you to create groups of users or repositories for convenience. Think back to Alice and Bob, our junior developers. Let's say you had several rules that Alice and Bob needed to be mentioned in. Clearly, this is too cumbersome; every time a new developer joined the team, you'd have to change all the rules to add him or her.
@junior-devs = alice bob
Later, it lets you do this by using the following command:
repo foo RW = @junior-devs RW+ = carol david RW+ sandbox/ = @junior-devs
This allows you to add the junior developer in just one place at the top of the configuration file instead of potentially several places all over. More importantly, from the administrator's point of view, it serves as excellent documentation for the rules themselves; isn't it easier to reason about the rules when a descriptive group name is used rather than actual usernames?
Gitolite allows the administrator to give each developer a unique set of branches, called personal branches, that only he or she can create, push, or delete. This is a very convenient way to allow quick backups of work-in-progress branches, or share code for preliminary review.
We saw how the sandbox area was defined:
RW+ sandbox/ = alice bob
However, this does nothing to prevent one junior developer from accidentally wiping out another's branches. For example, Alice could delete a branch called
sandbox/bob/work that Bob may have pushed. You can use the special word
USER as a directory name to solve this problem:
RW+ sandbox/USER/ = alice bob
This works as if you had specified each user individually, like this:
RW+ sandbox/alice/ = alice RW+ sandbox/bob/ = bob
Now, the set of branches that Alice is allowed to push is limited to those starting with
sandbox/alice/, and she can no longer push or delete a branch called, say,
With Gitolite, the administrator can choose to let the user create their own repositories, in addition to the ones that the administrator themselves creates. For this example, ignore the syntax, which will be explained in a much later chapter, but just focus on the functionality now:
repo dev/CREATOR/[a-z].* C = @staff RW+ = CREATOR
This allows members of the
@staff group to create repositories whose names match the pattern supplied, which just means
dev/<username>/<anything starting with a lowercase alphabetic character>. For example, a user called
alice will be able to create repositories such as
Conceptually, Gitolite is a very simple program. To see how it controls access to a Git repository, let us first look at how control flows from the client to the server in a normal git operation (say
git fetch) when using plain
When the user executes a git clone, fetch, or push, the Git client invokes
ssh, passing it a command (either
git-receive-pack, depending on whether the user is reading or writing). The local ssh client passes this to the server, and assuming authentication succeeds, that command gets executed on the server.
First, notice that nothing changes on the Git client side in any way; the changes are only on the server side. In fact, unless an access violation happens and an error message needs to be sent to the user, the user may not even know that Gitolite is installed!
Second, notice the red link from Gitolite's shell program to the
git-upload-pack program. This call does not happen if Gitolite determines that the user does not have the appropriate access to the repo concerned. This access check happens for both read (that is,
git fetch and
git clone commands) and write (
git push) operations; although for writes, there are more checks that happen later.
It's very easy to try out Gitolite in a safe environment without affecting anything else in the system in any permanent manner. Gitolite has a fairly complete set of test scripts, and the officially supported method of trying out Gitolite simply uses a couple of these test scripts to automatically install and set up Gitolite.
At the end of this process, you will have a version of Gitolite all set up and ready to use. You will also have an "admin" user, and six "normal" users, using which you can try out most of the features of Gitolite (with the exception of some advanced features such as mirroring).
A Unix (Linux, BSD, HP-UX, AIX, Solaris, and so on) server
Git version 1.7.1 or later installed on the server
Perl 5.8.8 or later version installed on the server
An OpenSSH-compatible SSH daemon installed and running on the server
Root access to the server in order to create a new throw away user to do the testing in
At the time of writing this book, Git 1.7.1 is over three years old, and Perl 5.8.8 is quite a bit older than that, so almost any recent Linux or BSD system should work fine.
Log in as root (using whatever commands your OS/distro requires to do that) and create a new throw away user. You can call this anything you want, but we will use the name
gitolite-testhere. Please do not use an existing user for this!
Log in as the
Get the Gitolite sources from the official repository,
You can get this from any other clone of the gitolite sources if your server cannot directly access the internet. Just substitute the URL you have in the preceding
Switch to the directory that was just cloned using the following command:
Install and set up Gitolite in test mode using the following command:
env GITOLITE_TEST=y prove t/ssh*
Go back to the HOME directory:
This will churn through two of the test scripts. You will see a warning about an
authorized_keys file being created, which you can ignore, and finally a message saying
All tests successful, with some statistics on the test run.
At the end of that process, you should have the following: one "admin" user (called
admin) and six normal users (named
u6). These users are simulated using an
ssh feature. If you are familiar with
ssh, you can look in
~/.ssh/config to see how this is done.
Clone the special
$ git clone admin:gitolite-admin Cloning into 'gitolite-admin'... remote: Counting objects: 8, done. remote: Compressing objects: 100% (4/4), done. remote: Total 8 (delta 1), reused 0 (delta 0) Receiving objects: 100% (8/8), done. Resolving deltas: 100% (1/1), done.
Examine the contents of the clone:
$ cd gitolite-admin/ $ ls -a . .. conf .git $ ls -a conf . .. gitolite.conf
conf/gitolite.conf file and add the following lines, which tell Gitolite to create a new repository called
bar and allow users
u2 all rights to it:
repo bar RW+ = u1 u2
Save the file, then add the change (
git add) and commit the file (
$ git add conf/gitolite.conf $ git commit -m 'added repo bar' [master 1111cee] added repo bar 1 file changed, 3 insertions(+) $ git push Counting objects: 7, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (4/4), 338 bytes | 0 bytes/s, done. Total 4 (delta 1), reused 0 (delta 0) remote: Initialized empty Git repository in /home/gitolite-test/repositories/bar.git/ To admin:gitolite-admin f226f28..1111cee master -> master
As you can see, we've just created a new repository called
bar. If you look at the output carefully, you might notice, among the familiar output of a
git push command, a line saying that an empty Git repository was initialized on the server. This is useful because you don't have to log on to the server to create the repository—Gitolite takes care of it.
Let's look at some access rights. Running ssh against the server and supplying
info as the command will show you what repositories you have access to:
$ ssh admin info hello admin, this is gitolite-test@server running gitolite3 v220.127.116.11-6-g5bdc750 on git 18.104.22.168 R W gitolite-admin R W testing $ ssh u1 info hello u1, this is gitolite-test@server running gitolite3 v22.214.171.124-6-g5bdc750 on git 126.96.36.199 R W bar R W foo R W testing $ ssh u3 info hello u3, this is gitolite-test@server running gitolite3 v188.8.131.52-6-g5bdc750 on git 184.108.40.206 R W foo R W testing
A note on command and URL syntax
Remember that we are running the Gitolite server, as well as simulating the seven different users, on the same Unix user (which is
gitolite-test). As a result, you are able to use commands such as
git clone admin:gitolite-admin and
ssh u1 info. In a real setup, you will represent yourself, and the server will be elsewhere. The commands will be of the form
git clone gitolite-test@server:gitolite-admin and
ssh gitolite-test@server info.
In this chapter, we learned why Gitolite is useful, saw an example of access control rules, and got a glimpse of some of its features. We also learned the basic ideas behind Gitolite, and created a test instance of Gitolite in order to try it out safely.
In the next chapter, we will install Gitolite properly and learn the basics of administering Gitolite.