Plan Zero

Hello, there!

Flattr this

Hosting an admin-friendly git server with git-shell

Published

I use Git for managing all of my code and documentation. To give me the flexibility I need, I've always hosted my own Git server using Gitosis, which was less than fun to work with and has now been deprecated within the Git community. I'm setting up a new server, so I've been looking for other solutions. The most commonly recommended replacement is Gitolite, which looks like a great solution. If you don't need fine-grained access control, however, there are even easier solutions.

Scott Chacon's excellent Pro Git book mentions several ways to set up a git server using plain Git. One of the more flexible options is to set up an SSH user for Git and add the SSH public key of each user to the Git user's authorized_keys file. Anyone who's public key is in the list can access the hosted Git repositories. It's remarkably easy to set up a Git server in this manner.

As described by the book (see below for my version), setting up a Git server is as simple as:

# sudo adduser git
# su - git
$ mkdir .ssh
$ cat yourkey.pub >> .ssh/authorized_keys

Next, add a bare repository for each project:

$ mkdir coolproject.git
$ cd coolproject.git
$ git --bare init

That's the server set up. Now authorised users can commit projects in the same way as with any other Git server:

$ git init
$ git add .
$ git commit -m 'Initial commit'
$ git remote add origin git@gitserver:coolproject.git
$ git push origin master

Checking out as simple as:

$ git clone git@gitserver:coolproject.git

That really is all there is to it! Git has been built to work seamlessly with the SSH protocol, so once you've set up the SSH side of things, Git takes care of the rest.

Improving things

There are a few things we can do to make the server a bit more friendly and a lot more secure. Git comes with a shell, cunningly named git-shell, which can be used to restrict the user we created to purely git-related activities, rather than the default full command shell. This is generally an excellent idea.

Once the Git user's shell is set to git-shell, anybody attempting to login as the git user will get the following message:

fatal: Interactive git shell is not enabled.
hint: ~/git-shell-commands should exist and have read and execute access.

Notice that last part? There's not much documentation on git-shell-commands, but it turns out that if you create a directory with this name in the Git user's home directory, any executable files you place inside the directory will become accessible through an interactive shell when you su up or SSH into the git user. If you create a script named help, it will get executed when the shell starts. Kind of cool, and really handy for making your Git server more friendly :-)

The tools

Putting it all together, you can now create a user as follows, then use the tools below to create projects and add keys:

# useradd --create-home --skel /dev/null --home-dir /srv/data/git --shell /usr/bin/git-shell git
# chmod 750 /srv/data/git

The first command creates a user named git with a home directory in /srv/data/git (change this as you need), with no skeleton files (.bashrc, .bash_logout, etc), and assigns the user the all-important /usr/bin/git-shell shell. No password was specified, so the user cannot log in by the usual means (we'll use su as root and authorized_keys to allow access). The second command is optional and just sets permissions on git's home directory so that nobody on the server can access the repositories directly.

The Git documentation comes with a couple of handy sample tools for use with the Git shell, list to list available repositories, and help to display a list of available commands. On Ubuntu, these can be found in /usr/share/doc/git/contrib/git-shell-commands, on other distros a quick search should find them if the documentation is installed. The listings below are for are a couple of additional tools I wrote, the first to create a new project repository, the second to add new SSH keys.

Create a git-shell-commands directory in your git user's home directory, then copy in the sample tools (or download them at the end of the article). Next add the two tools listed below. Once the tools are in place, make them executable and your new Git server will be ready to use!

~/git-shell-commands/create

The create script allows you to create a new repository. If you leave off the customary .git extension it'll add it for you (otherwise list won't find the project). If the project already exists, you'll see a warning from mkdir and initialisation won't take place.

#!/bin/sh

# If the user is not root
if [ "$USERNAME" != "root" ]
then

	# Dislpay a notice and stop
	echo "Sorry, only root can use this command."
	exit 1

fi

# If no project name is given
if [ $# -eq 0 ]
then

	# Display usage and stop
	echo "Usage: create <project.git>"
	exit 1

fi

# Set the project name, adding .git if necessary
project=$(echo "$*" | sed 's/\.git$\|$/.git/i')

# Create and initialise the project
mkdir "$project" && \
cd "$project" && \
git --bare init

~/git-shell-commands/addkey

The addkey script allows you to add new SSH public keys to the authorised key list. To ensure the integrity of the authorized_keys file, the script makes sure you've entered a valid key (which must be entered all on one line). For security, the script also disables some SSH options for the key when it adds it. See the sshd man page for details of each.

#!/bin/sh

# If the user is not root
if [ "$USERNAME" != "root" ]
then

	# Dislpay a notice and stop
	echo "Sorry, only root can use this command."
	exit 1

fi

# Read in the SSH key
echo "Input the key to be added:"
read key

# Place the key in a temporary file (it's hard to get ssh-keygen
# to read from stdin; <<< works for bash, but is non-posix)
keyfile=$(tempfile) &&\
echo "$key" > $keyfile

# Generate a fingerprint
fingerprint=$(ssh-keygen -lf $keyfile)

# Check for errors
if [ $(echo "$fingerprint" | egrep -c '(R|D)SA') -eq 0 ]
then

	# Display the fingerprint error and clean up
	echo "Error: $fingerprint"
	rm $keyfile
	exit 1

fi

# Add the key to the authorised keys file and clean up
mkdir -p .ssh &&\
echo -n "no-agent-forwarding,no-port-forwarding,no-X11-forwarding " >> .ssh/authorized_keys &&\
cat $keyfile >> .ssh/authorized_keys
rm $keyfile

# Display the fingerprint for reference
echo "Success! Added a key with the following fingerprint:"
echo $fingerprint

These two scripts prevent non-root users from using them, but the restriction can of course be relaxed if your users are trusted. I believe checking the shell's USERNAME environment variable is a reasonably secure way to restrict access, because SSH prevents environment variables being passed to the shell, and the user cannot alter the variable's value once logged in (git-shell won't let them!)

With the tools in place, you'll be able to add keys and create repositories with a few keystrokes when you su into the git user from the root account. Here's an example session using the interactive Git shell and the tools provided above:

$ sudo su git
[sudo] password for user: 
Run 'help' for help, or 'exit' to leave.  Available commands:
addkey
create
list
git> addkey
Input the key to be added:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCwo5wOJQB3gUV4NzZN1q3S9siD8HY9ufj/8+nMj7x5
ggpwbujG9gpFbfi/L6p8OJGi09akGxV2jMXxS4BdinFwvLfcCNIkI6Gq4ME0QRjKuYau/ITR5/MNQTh6
TRTCwFWmUOy3Hd1dsn+bqadbd4gAABW6cA54Ggbknv0JAQsoDW+qdaZDoSJIxXhT6aE9cEJ2PyUl6tul
9RRiQqDZjYRcNJydJ5mYBAksXP7uEVsDM+cO9ZOqqsYQvXN0nzfUZjUNfSxV+JmGBBJzJdXCu+pMb0O8
yTzL8ycSSCkaLHBQ35Qw16fBqrWMH6Oj5wqXl0HtykQCnU14juwWOHfdCPnTsPWBWgJDN9FgiuYwFxIM
fuz4tRq2ygcHB+Yt5smiG6eLEE8pCUtTv8EUmVI4LA7fguiA63CYL6aGeK8e08UeaGx0uaxum92D73AB
qlFvn0+Wi4efZV1qvdvFcoMtgThKDTaDepYSb/d020BH1tstj7CJcK05nsBoqwOp4GGhGJtkWbN3Dn69
vRfyIX4w4y2Etw9MoW898rtHFN9eB9FbfWbMDPf8cAayoN40Xd0RocRxH46clV1g2bH0Bn5TGkL6Ltj3
Bvqzu5ZLMbw4s0kgOShK2GCNTBDf86aHG/97CL63FBQu/TcZuXxNmwApL2Wo0dfpCBEpvSu3m7+44y5K
6w== user@host
Success! Added a key with the following fingerprint:
4096 fc:6f:2d:28:d4:13:a1:65:52:a0:33:f6:76:af:84:6a user@host (RSA)
git> create awesomeproject.git
Initialized empty Git repository in /srv/data/git/awesomeproject.git/
git> list
coolproject.git
awesomeproject.git
git> exit
$

Users have also access to the interface and can list available projects:


$ ssh git@gitserver
Run 'help' for help, or 'exit' to leave.  Available commands:
addkey
create
list
git> addkey
Sorry, only root can use this command.
git> create sneakyproject.git
Sorry, only root can use this command.
git> list
coolproject.git
awesomeproject.git
git> exit
Connection to gitserver closed.
$

I hope the information presented here will get you off to a good start with your own Git server, and help you spend less time managing Git and more time coding! All git-shell-commands (including the samples provided in the git documentation) can be downloaded below.

git-shell-commands-0.1.tar.bz2