In this article I’m going to show you, step by step, how to set up an OSX machine to provide secure access to git repositories over the internet via ssh. This was tested on OSX 10.6.

github provides git repository hosting with a lovely interface. If github isn’t feasible, then this article will help you set up something similar, unfortunately without the nice interface.

I assume that you already have git installed. If not, install the latest version from the git website.

We will be walking through the following steps:

  1. Give the server a static IP address on the local network
  2. Set up port forwarding on the router
  3. Getting dynamic DNS
  4. Add a user named "git" to the server
  5. Setting up ssh securely on the client computers
  6. Setting up ssh securely on the server
  7. Making a bare git repository
  8. Using your new git server

Step 1. Give the server a static IP address on the local network

The server needs a static IP address on the local network for the next step (port forwarding) to work. This can be set in the following location:

System Preferences » Network » Advanced » TCP/IP

If the Configure IPv4 option is set to Manual or Using DHCP with manual address, then you already have a static IP address.

If the Configure IPv4 option is set to Using DHCP, then change it to Using DHCP with manual address. Set the IPv4 Address option to an address that won’t conflict with the DHCP addresses from the router. I’m going to use 192.168.1.150 as an example of the server’s static IP address.

Remember the IPv4 Address setting and the Router setting, as these will be needed in the next step.

Step 2. Setting Up Port Forwarding On The Router

With the default settings, your home router most likely acts as a firewall to the internet outside. The router usually ignores inbound connection requests from the internet. To allow people to access the git repo via the internet, we need to add a port forwarding rule.

Open up your web browser and type the IP address of your router into the address bar. Once inside the router configuration page, you’ll have to hunt around for the port fowarding options because each router is different.

ssh usually works on port 22, but for extra security, we’re going to expose a different port to the internet. Think of a port number between 1024 and 65535 that you will remember; I’m going to use port 12345 as an example.

Configure the router so that the external port 12345 is forwarded to the internal port 22 of the servers static IP address (set in Step 1), like so:

Save the settings on the router.

Step 3. Getting dynamic DNS

Unless you’ve paid your ISP for a static IP address, your router’s IP address on the internet will change from time to time. To avoid having to work out the IP address every few days, you can set up dynamic DNS to automatically give the correct IP address.

I’ll leave out the details for brevity, but you can get this service for free at places like DynDNS.com. You basically sign up for a free domain name like example.dyndns.org, then you install a program on the server that constantly updates the domain with the current IP address.

Instead of doing this…

git clone ssh://git@124.186.120.123:12345/myrepo.git

you can do this…

git clone ssh://git@example.dyndns.org:12345/myrepo.git

Step 4. Add a user named "git" to the server

I’m going to add a single user to the server. All clients will connect via this single user when pushing and pulling from the repo. You could set up a user for each client if you wanted, but it’s by no means necessary.

Open up System Preferences » Accounts and add a standard user like so:

Log into the new git user account. Open Terminal.app and type the following:

echo 'export PATH="$PATH:/usr/local/git/bin/"' >> ~/.bashrc

Log out of the git user, and log back into your administrator account.

Step 5. Setting up ssh securely on the client computers

Even with a non-standard ssh port, your computer is open to automated password guessing attacks from the internet. If your user name is something common like "bob", and your password is something easy to guess like "abc123", then you may be in danger.

To harden up the security, we are going to disable authentication with passwords. Instead, authentication will be done with ssh public keys. You can think of ssh public keys as very long, randomly generated passwords that you don’t have to type in.

This step must be completed on every client computer that wishes to connect to the server. Do not do this step on the server.

Open up Terminal.app and type the following:

cd
mkdir -p .ssh
cd .ssh
ssh-keygen -f my_git_key -t rsa -q
ssh-add -K my_git_key
cp my_git_key.pub ~/Desktop

You should see something like this:

When it asks you to type in a passphrase I advise you to do so, even though it’s optional. If someone steals your computer, the passphrase on the key prevents them from connecting to the server. It is just an added layer of security.

You should now have a file called my_git_key.pub on your desktop. This is one half of your key, stored as a text file. Keep this file, because it is needed in the next step.

Feel free to open my_git_key.pub in any text editor to have a look at it. Like I said, it looks like a very long random password.

Step 6. Setting up ssh securely on the server

Now we’re going to set up the server to accept secure connections.

First, open up System Preferences » Sharing and turn on Remote Login (this is ssh). Set Allow access for to Only these users, and add the git user to the list. It should look like this:

Now open Terminal.app and type in sudo open /etc/sshd_config. When it asks you for a password, type in your login password for the account you are curently in. This step requires your account to be an administrator account.

In the newly opened text editor, change the following lines as indicated below.

Find this line Change it to this
#PermitRootLogin yes PermitRootLogin no
#PasswordAuthentication no PasswordAuthentication no
#ChallengeResponseAuthentication yes ChallengeResponseAuthentication no
#UsePAM yes UsePAM no

Save the file and quit it.

Now, log out of your current account and log into the git user that we created in Step 4. Open up Terminal.app again, and type in the following:

cd
mkdir -p .ssh
touch .ssh/authorized_keys
open -a "TextEdit" .ssh/authorized_keys

This should open a file called authorized_keys, and that file should be completely empty at this point.

Now, remember the my_git_key.pub files from Step 5? There should be one of these files on the desktop of every client computer. You will need these files now on the server computer.

Each my_git_key.pub file contains a single line of text. If your editor is set to wrap text then it may look like more than one line, but it isn’t. This is important, because putting a new line anywhere will break the file.

Get each my_git_key.pub file, copy the single line from it, and paste it into the blank text file (authorized_keys) on a new line. Each line in authorized_keys should hold the entire contents of each my_git_key.pub file. If there are four clients, then there should be four lines. You get the idea.

TextEdit.app doesn’t have the option to turn off text wrapping, so I advise you use a better text editor such as TextWrangler, which is free. To open the file in TextWrangler instead of TextEdit, just replace "TextEdit" with "TextWrangler" in the Terminal.app commands above.

Save the authorized_keys file, and restart the server computer.

7. Making a bare git repository

Now that everything should be set up, all that remains is to make the git repository.

You can’t push a branch to a server if that branch is currently checked out on the server. That would cause problems for whoever is working on the server. Seeing as nobody is actually working on the server this isn’t a problem, because nothing needs to be checkout out. This is the definition of a "bare" git repo: a repository that can never have files checked out. Everyone pulls and pushes from the server like normal, but nobody can actually work on the server (unless they make a non-bare clone).

If you are creating a new repository called "my_bare_repo.git", you can make it bare like so:

git init --bare my_bare_repo.git

If you have an existing repo, you can make a bare clone of it like so:

git clone --bare /wherever/the/existing/repo/is.git

8. Using your new git server

Let’s say you have the following setup:

Dynamic domain mygit.dyndns.org
Forwarded port 12345
Bare git repo path /Users/git/my_repo.git

On the client machine, you would clone the repo like so:

git clone ssh://git@mygit.dyndns.org:12345/Users/git/my_repo.git

If you have an existing local repo, you can add a remote named "home" like so:

git remote add home ssh://git@mygit.dyndns.org:12345/Users/git/my_repo.git

This may ask you for a password when it tries to authenticate with your ssh key. This is the password you typed in in Step 5. It may be different for each client computer.

Once you’ve got your cloned repo, you work on it like normal. You pull down updates from the server with git fetch or git pull, and you push branches with git push origin branch_name_here.

  • Maddicken

    How should step 4 “echo ‘export PATH=”$PATH:/usr/local/git/bin/”‘ >> ~/.bashrc”  be changed to work on OSX Lion?

  • http://tomdalling.com/ Tom Dalling

    I haven’t installed Lion yet, but try doing this in the terminal (in addition to step 4):

    sudo echo ‘[ -r "$HOME/.bashrc" ] && source “$HOME/.bashrc”‘ >> /etc/profile

    It looks like .bashrc isn’t run by default anymore, but this should make it run according to: http://natelyman.com/index.php?option=com_content&view=article&id=129:fixing-git-after-installing-os-x-lion-&catid=39:iphone-sdk

  • Matt

    Hi Tom,

    Just wanted to say thanks for this guide, really helped me get my remote git server setup on my home machine.

    Good work.
    Matt.

  • http://ben.kree.gr/ Benjamin Kreeger

    Actually AFAIK, git comes installed on Lion by default, which means you shouldn’t have to change any user’s path to find it. I think it’s at /usr/bin/git now.

  • Nebu

    Is there a possibility to disable the password authentication only for the git user? According to “man ssh_config” this should be possible by editing ~/.ssh/config the same way as you suggested to edit /etc/sshd_config (sans the root-part of course), but I can’t seem to get it running…

  • http://tomdalling.com/ Tom Dalling

    I haven’t done a whole lot of ssh config before, so I’m not sure how to do that I’m afraid.

  • H-Max

    Just wanted to say thanks for this guide, I’ve set up a remote git repository on a old mac mini here at work and it works like a charm. I feel powerful now.

  • http://twitter.com/codeslubber Rob Williams

    This is good, but the problem here is this works only for one user, right? When you try to go beyond that you are going to have to try and use gitosis or gitolite. That is a huge PITA. I was way down the road with gitosis before I ran into some insoluble issues with OS/X (and it’s a dead project making them even more insoluble).

  • http://tomdalling.com/ Tom Dalling

    It is multi-user in the sense that multiple people can use the git repo with their own name and email address. Everyone has access to everything, so if you want advanced permission settings you will need to set it up differently – probably with an ssh account per user.

  • http://twitter.com/brasskazoo Will D

    Great post, but one more point you should add is connecting an existing local repo to a bare remote repository – i.e.:

    git remote add origin ssh://….
    git push origin master

  • Mindwalker

    Assuming one has set this up using the steps described, how would one go about working with a repository on the actual server itself? Is there a way on the server to pull down a copy from that as if using a remote client, such that the master branch would never be checked out on the server?

  • http://tomdalling.com/ Tom Dalling

    Yes, it just has to be in a different directory, and the remote url is different when you clone it. For example, if you wanted a working clone in “/Users/mindwalker/projects/my_repo” you’d do:

    cd /Users/mindwalker/projects
    git clone /Users/git/my_repo.gitOr maybe, if the mindwalker user is set up like the other client machines:git clone ssh://git@localhost/Users/git/my_repo.git

  • Contact

    I found this very helpful, but lost quite a bit of time by overlooking the last two sentences of point 4. I think they could use a bit more emphasis, as it’s not related to adding the user, but rather adding the path to git for remote access.

    Nonetheless a great resource, thanks a million!

    Klaus

  • Crap

    gitolite

  • John

    OS X for a server? Hahahahahahahaha.

    …hahahahahahahahahahahahahaha.