Git --local-branching-on-the-cheap

4.7 Git on the Server - Gitosis

Gitosis

Keeping all users’ public keys in the authorized_keys file for access works well only for a while. When you have hundreds of users, it’s much more of a pain to manage that process. You have to shell onto the server each time, and there is no access control — everyone in the file has read and write access to every project.

At this point, you may want to turn to a widely used software project called Gitosis. Gitosis is basically a set of scripts that help you manage the authorized_keys file as well as implement some simple access controls. The really interesting part is that the UI for this tool for adding people and determining access isn’t a web interface but a special Git repository. You set up the information in that project; and when you push it, Gitosis reconfigures the server based on that, which is cool.

Installing Gitosis isn’t the simplest task ever, but it’s not too difficult. It’s easiest to use a Linux server for it — these examples use a stock Ubuntu 8.10 server.

Gitosis requires some Python tools, so first you have to install the Python setuptools package, which Ubuntu provides as python-setuptools:

$ apt-get install python-setuptools

Next, you clone and install Gitosis from the project’s main site:

$ git clone git://eagain.net/gitosis.git
$ cd gitosis
$ sudo python setup.py install

That installs a couple of executables that Gitosis will use. Next, Gitosis wants to put its repositories under /home/git, which is fine. But you have already set up your repositories in /opt/git, so instead of reconfiguring everything, you create a symlink:

$ ln -s /opt/git /home/git/repositories

Gitosis is going to manage your keys for you, so you need to remove the current file, re-add the keys later, and let Gitosis control the authorized_keys file automatically. For now, move the authorized_keys file out of the way:

$ mv /home/git/.ssh/authorized_keys /home/git/.ssh/ak.bak

Next you need to turn your shell back on for the 'git' user, if you changed it to the git-shell command. People still won’t be able to log in, but Gitosis will control that for you. So, let’s change this line in your /etc/passwd file

git:x:1000:1000::/home/git:/usr/bin/git-shell

back to this:

git:x:1000:1000::/home/git:/bin/sh

Now it’s time to initialize Gitosis. You do this by running the gitosis-init command with your personal public key. If your public key isn’t on the server, you’ll have to copy it there:

$ sudo -H -u git gitosis-init < /tmp/id_dsa.pub
Initialized empty Git repository in /opt/git/gitosis-admin.git/
Reinitialized existing Git repository in /opt/git/gitosis-admin.git/

This lets the user with that key modify the main Git repository that controls the Gitosis setup. Next, you have to manually set the execute bit on the post-update script for your new control repository.

$ sudo chmod 755 /opt/git/gitosis-admin.git/hooks/post-update

You’re ready to roll. If you’re set up correctly, you can try to SSH into your server as the user for which you added the public key to initialize Gitosis. You should see something like this:

$ ssh git@gitserver
PTY allocation request failed on channel 0
fatal: unrecognized command 'gitosis-serve schacon@quaternion'
  Connection to gitserver closed.

That means Gitosis recognized you but shut you out because you’re not trying to do any Git commands. So, let’s do an actual Git command — you’ll clone the Gitosis control repository:

# on your local computer
$ git clone git@gitserver:gitosis-admin.git

Now you have a directory named gitosis-admin, which has two major parts:

$ cd gitosis-admin
$ find .
./gitosis.conf
./keydir
./keydir/scott.pub

The gitosis.conf file is the control file you use to specify users, repositories, and permissions. The keydir directory is where you store the public keys of all the users who have any sort of access to your repositories — one file per user. The name of the file in keydir (in the previous example, scott.pub) will be different for you — Gitosis takes that name from the description at the end of the public key that was imported with the gitosis-init script.

If you look at the gitosis.conf file, it should only specify information about the gitosis-admin project that you just cloned:

$ cat gitosis.conf 
[gitosis]

[group gitosis-admin]
writable = gitosis-admin
members = scott

It shows you that the 'scott' user — the user with whose public key you initialized Gitosis — is the only one who has access to the gitosis-admin project.

Now, let’s add a new project for you. You’ll add a new section called mobile where you’ll list the developers on your mobile team and projects that those developers need access to. Because 'scott' is the only user in the system right now, you’ll add him as the only member, and you’ll create a new project called iphone_project to start on:

[group mobile]
writable = iphone_project
members = scott

Whenever you make changes to the gitosis-admin project, you have to commit the changes and push them back up to the server in order for them to take effect:

$ git commit -am 'add iphone_project and mobile group'
[master]: created 8962da8: "changed name"
 1 files changed, 4 insertions(+), 0 deletions(-)
$ git push
Counting objects: 5, done.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 272 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
To git@gitserver:/opt/git/gitosis-admin.git
   fb27aec..8962da8  master -> master

You can make your first push to the new iphone_project project by adding your server as a remote to your local version of the project and pushing. You no longer have to manually create a bare repository for new projects on the server — Gitosis creates them automatically when it sees the first push:

$ git remote add origin git@gitserver:iphone_project.git
$ git push origin master
Initialized empty Git repository in /opt/git/iphone_project.git/
Counting objects: 3, done.
Writing objects: 100% (3/3), 230 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@gitserver:iphone_project.git
 * [new branch]      master -> master

Notice that you don’t need to specify the path (in fact, doing so won’t work), just a colon and then the name of the project — Gitosis finds it for you.

You want to work on this project with your friends, so you’ll have to re-add their public keys. But instead of appending them manually to the ~/.ssh/authorized_keys file on your server, you’ll add them, one key per file, into the keydir directory. How you name the keys determines how you refer to the users in the gitosis.conf file. Let’s re-add the public keys for John, Josie, and Jessica:

$ cp /tmp/id_rsa.john.pub keydir/john.pub
$ cp /tmp/id_rsa.josie.pub keydir/josie.pub
$ cp /tmp/id_rsa.jessica.pub keydir/jessica.pub

Now you can add them all to your 'mobile' team so they have read and write access to iphone_project:

[group mobile]
writable = iphone_project
members = scott john josie jessica

After you commit and push that change, all four users will be able to read from and write to that project.

Gitosis has simple access controls as well. If you want John to have only read access to this project, you can do this instead:

[group mobile]
writable = iphone_project
members = scott josie jessica

[group mobile_ro]
readonly = iphone_project
members = john

Now John can clone the project and get updates, but Gitosis won’t allow him to push back up to the project. You can create as many of these groups as you want, each containing different users and projects. You can also specify another group as one of the members (using @ as prefix), to inherit all of its members automatically:

[group mobile_committers]
members = scott josie jessica

[group mobile]
writable  = iphone_project
members   = @mobile_committers

[group mobile_2]
writable  = another_iphone_project
members   = @mobile_committers john

If you have any issues, it may be useful to add loglevel=DEBUG under the [gitosis] section. If you’ve lost push access by pushing a messed-up configuration, you can manually fix the file on the server under /home/git/.gitosis.conf — the file from which Gitosis reads its info. A push to the project takes the gitosis.conf file you just pushed up and sticks it there. If you edit that file manually, it remains like that until the next successful push to the gitosis-admin project.