Setting up a git server on macOS 10.12

07 Feb 2017

I’m working on learning the Go language. In order to do so, I’m going through the exercises in The Go Programming Language. But before I got too far, it seemed worthwhile to set up a git repo to hold the code. The git docs provide useful info for making this go on linux, but I wanted to make it work on my mac. And after a bit of work, I came up with the following process for setting up a git server:

Set up a git user

We’re going to want a git user that doesn’t have login permissions, so that there’s a service account to own the files in the repo. There’s no adduser command, and on the mac these days, everything is done via opendirectory. On macOS 10.12, the following sequence of commands will create a git user:

sudo dscl localhost create /Local/Default/Users/git    # username is git
sudo dscl localhost create /Local/Default/Users/git RealName "git repo management"
sudo dscl localhost create /Local/Default/Users/git Password '*'    # should not be able to login via password
sudo dscl localhost create /Local/Default/Users/git UniqueID 250    # Pick something unique
sudo dscl localhost create /Local/Default/Users/git PrimaryGroupID 20    # Staff group
sudo dscl localhost create /Local/Default/Users/git UserShell /usr/bin/git-shell    # No shell access allowed!
sudo dscl localhost create /Local/Default/Users/git NFSHomeDirectory /usr/local/gitrepo

Set up git-shell as a valid shell

Now there’s a git user, but it needs a bit more work before we’re ready to roll. sudo emacs -nw /etc/shells Add /usr/bin/git-shell to the list of valid shells so that ssh access will work.

Allow the git user to use ssh

The macOS Sharing setting in Preferences can be used to lock down ssh (or “Remote Login”, as it’s called on macOS). The new git user will need to be allowed to use ssh. This turns out to be easy:

sudo dseditgroup -o edit -t user -a git com.apple.access_ssh

and done!

Create the git user’s home directory

I decided to put the git user’s directory in /usr/local/gitrepo. We’ll need to make that directory, and then add a .ssh directory in there to hold the authorized_keys. Here’s how:

sudo mkdir /usr/local/gitrepo
sudo mkdir /usr/local/gitrepo/.ssh
sudo chmod 700 .ssh
sudo touch .ssh/authorized_keys && chmod 600 .ssh/authorized_keys
sudo chown -R git /usr/local/gitrepo

Make an bare repo for your project

In this case, I want to set up an empty repo for testing. I’m going to make a bare repo with the name empty:

sudo --user git bash # get a shell as the git user
cd /usr/local/gitrepo
mkdir empty.git
cd empty.git
git init -bare

Set up ssh pubkeys for your users

We’ve blocked the git user from having password-driven authentication, so we now need to add our SSH keypair pubkeys to the git user’s account. You’ll find your pubkey in the file ~/.ssh/id_rsa.pub. If you don’t have one of those, make a keypair by running: ssh-keygen -t rsa -b 4096 -C "me@example.com" (of course, replace me@example.com with your own email address).

Now you’ll need to add the contents of the ~/.ssh/id_rsa.pub to the git account. This is easy to do, but a little tricky because the git user can’t read your pubkey, and you can’t write it to the authorized_keys file as anyone other than the git user (or root). This is where the /tmp directory comes into play!

cp ~/.ssh/id_rsa.pub /tmp/user.pubkey
sudo --user git bash
cd /usr/local/gitrepo/.ssh
cat /tmp/user.pubkey >> authorized_keys

Test it out!

At this point you should be about ready to test. Try to ssh to your server, from the user account where the pubkey came from:

ssh git@localhost

You should see something like fatal: Interactive git shell is not enabled.

Now you can try to clone the repo, which is the true test.

cd ~/Projects
git clone git@localhost:/usr/local/gitrepo/empty.git

If all is well, you should now have a new folder named empty, and a warning that says warning: You appear to have cloned an empty repository. Which is true!

Now you can make a new repo for your project, clone it, and collaborate.