HOWTO: Set Up A Private Git Server

By Ted Felix Updated July 8, 2024

The git server I installed way back in November 2013 is still going strong. It has since been upgraded to Ubuntu Server 14.04, an Intel NUC with Ubuntu Server 18.04 on a 64GB SSD and now the same Intel NUC with Ubuntu Server 22.04 on a 256GB SSD. It was running fine on a 64GB SSD, so not a lot of space is required for this.

Install Ubuntu

First, I grabbed the ISO that I wanted to use. I went with Ubuntu Server 22.04.x. This will be supported until April 2027.

I used mkusb to create a bootable flash drive from the Ubuntu .iso file. mkusb74 is the version I usually use. It looks like that's pretty hard to find these days. The latest should work fine. Or use dd if you feel adventurous.

The USB install was ok, although it did require a little babysitting as it would ask various questions along the way. It's a text mode interface, and the final install was about 5.7GB (Ubuntu 18.04).

In the past, I've tried LVM (Logical Volume Manager) and was irritated by the fact that the boot directory is on a rather small partition that fills up as new kernels come out. This time I skipped LVM.

The install offered an "Install OpenSSH Server" option. I selected it, and it installed it for me. If you forget, you can always install it later:

$ sudo apt-get install ssh

Configure Networking

This time around I used Ethernet and it was automatically detected and configured.

If you go the wireless route, you may need to add the appropriate lines to /etc/network/interfaces. In the past, my entries have looked something like this:

# The wireless interface auto wlan0 iface wlan0 inet dhcp wpa-driver wext wpa-ssid WirelessRouterName # 2 => SSID is hidden wpa-ap-scan 2 # RSN => WPA2 wpa-proto RSN wpa-pairwise CCMP wpa-group CCMP wpa-key-mgmt WPA-PSK # ASCII version requires quotes. Hex doesn't. wpa-psk "passphrase" # wpa_passphrase can be used to convert an ASCII passphrase to hex.

Put your wireless router SSID (name) where you see "WirelessRouterName" and your WPA2 passphrase where you see "passphrase". This assumes you are using WPA2 and PSK mode. See the man page for wpa_supplicant.conf(5) for the details on each of the above fields.

Next you'll want to configure the firewall. I used ufw, which is probably not a good idea for a server that isn't protected by another hardware firewall. Setting up ufw is very easy:

$ sudo ufw enable $ sudo ufw status

Install Updates

Use apt and install the latest updates.

$ sudo apt update $ sudo apt upgrade

Check Running Servers

Use one of the following (or try all of them) to see what servers are running and listening on ports. Ubuntu tends to have almost nothing running after a clean install. Very nice.

$ sudo lsof -i $ sudo netstat -lptu $ sudo netstat -tulpn

Securing the Secure Shell

Using git through ssh is simple and secure, so I decided to go with that approach. My main concern was securing ssh so that it would be very difficult to hack. These steps are probably not complete, so I recommend that you check other sources to find out how to secure a ssh server.

Make sure the admin account has an ssh key so that we can get back in after we lock things down. If you are working from the console, this might not be a big deal, but it will probably make your life a lot easier.

The /etc/ssh/sshd_config file has all the settings for sshd that you can use to make it more secure. In no particular order...

Restrict the encryption ciphers to the best by adding this line to sshd_config:

Ciphers aes256-ctr

Pick a random port number between 10000 and 65535 (see random.org) and move the server to that port. Add or adjust the following line in sshd_config, replace "port" with the port number you've picked:

Port port

Restrict authentication so that only public/private key authentication is allowed. Add or adjust the following lines in sshd_config:

PasswordAuthentication no ChallengeResponseAuthentication no PermitRootLogin no UsePAM no

Turn off tcp tunneling so your users can't use your server to bounce around the Internet and cause trouble. Add or adjust this line in sshd_config:

AllowTcpForwarding no

Note that this is pretty extreme as it blocks tunneling even to other ports on the server. If you want to selectively allow connections, e.g. to 192.168.1.200:3000 only, do this:

AllowTcpForwarding yes PermitOpen 192.168.1.200:3000

Finally, once you've made all your changes, ask sshd to reload the config file (this sends it a good ol' SIGHUP):

$ sudo systemctl reload ssh

For stronger security, two-factor authentication (2FA) is all the rage these days. Google Authenticator is easy to install and use. Click here for the steps.

Open a hole in the server firewall

So that ssh traffic can get in through the random port we picked above, we'll need to poke a hole through the server's firewall. With ufw (replace "port" with the random port number we selected for sshd):

$ sudo ufw allow port $ sudo ufw status

Now we can test ssh from the local network. From another computer on the network, try logging into the server with ssh. (Replace "port" with the random port number we selected for sshd.):

$ ssh -p port user@host

Some additional ssh security tips to try:

Install and Configure Git

Install git.

$ sudo apt install git

For 22.04, this was already installed.

Set up a "git" group. Members of this group will be able to push to the repo. Everyone else will be able to fetch only.

$ sudo groupadd git

Then add your user to that group so that you can test. Change "ted" to your userid on the server.

$ sudo gpasswd -a ted git

Now we need to create the /srv/git directory and set it up properly. (Be sure to replace "ted" with your server userid. "root" is probably ok too.)

$ sudo mkdir -p /srv/git $ cd /srv $ sudo chown ted:git git $ chmod 775 git $ chmod g+s git

Tell git that we want to do sharing with the git group. This should preserve the "git" group on all files along with the setgid bit on all directories. We'll double-check later just in case.

$ sudo git config --global core.sharedRepository group

Now you can clone your first repo into the /srv/git directory. One way is to clone from a repo in another directory. In this example, we assume the repo is in a directory called "source-repo-dir". Replace "projectname" with an appropriate name for the git project.

$ git clone --bare source-repo-dir /srv/git/projectname.git

Setting core.sharedRepository to "group" should have ensured that the group and setgid bits were handled properly when we did the clone. Check to make sure this is the case. Go through the new repo and make sure all files and directories belong to the "git" group and that the setgid bit is on for all directories. If this isn't the case, fix up the permissions with chown and chmod:

$ cd /srv/git $ chown -R ted:git projectname.git $ chmod -R 775 projectname.git $ find projectname.git -type d -exec chmod g+s '{}' \;

Now we can test git from another machine on the network. Clone the repo and see if it works. Replace "user" with your server userid, "host" with the IP or hostname of the server, and "port" with the random sshd port we selected. Also replace "projectname" with the name of the project.

git clone ssh://user@host:port/srv/git/projectname.git projectname

Internet Firewall

Set up a hole in your Internet firewall so that people outside of the local network can get to your git server. If you have a dynamic IP, set up Dynamic DNS so that others can find you. With my Asus router, this was built-in and very easy to set up. Next, poke a hole through your firewall to allow data for the sshd port we selected above to get to the server. E.g. if the port we picked was 2222, and the server was at 192.168.5.15, set up port forwarding on the firewall to send connections for port 2222 to 192.168.5.15:2222.

Once this is set up, try testing from the outside. Or test from the inside with the dynamic DNS name. This should be almost as good.

Add Users

The following steps assume you are logged in as root. Though it is dangerous, it makes it a lot easier to do this. Especially the "cat" step which otherwise requires "sh -c". To login as root:

$ sudo bash

To add a user to the server:

# adduser --disabled-password --shell /usr/bin/git-shell username

Optionally, add them to the "git" group if you want them to be able to push:

# gpasswd -a username git

For the git-shell to work, you must create a git-shell-commands directory. Not sure why, but if you don't, you'll get errors that it is missing.

# cd /home/username # mkdir git-shell-commands

Ask them to generate a public key for you with ssh-keygen and email it to you. Append their public key to their authorized_keys file.

# mkdir .ssh # chmod 700 .ssh # cat id_rsa.pub >>.ssh/authorized_keys # chmod 600 .ssh/authorized_keys

Then normalize the owner/group:

# chown -R username:username .ssh

If your repo will be owned by one user and accessed by other users, you will need to add a .gitconfig for each user to tell git that it is safe to work with specific repo directories that are owned by other users:

[safe] directory = /srv/git/projectname.git

Without this you will see "detected dubious ownership" errors.

At this point, you should be done. Be sure to exit bash as staying logged in as root is considered dangerous.

# exit $

Now they should be able to clone from their machine:

git clone ssh://username@host:port/srv/git/projectname.git projectname

To troubleshoot, the user can use ssh to connect and they should get a "git>" prompt if all is configured properly. "quit" will exit.

ssh -p port username@host

If you are using Google Authenticator for two-factor authentication, you'll need to allow users to run google-authenticator. Click here for the steps.

Updates

See Ubuntu LTS Server Guide: Automatic Updates.

Install unattended-upgrades:

sudo apt install unattended-upgrades

Turns out this is already installed on Ubuntu 22.04 server.

The default configuration stays on top of security updates only. You can change that in /etc/apt/apt.conf.d/50unattended-upgrades. You can also configure autoremove and reboots when required in that file.

Periodic Maintenance

Git repos occasionally need garbage collection. You can set up a weekly script to do this. If you don't, I suspect that git itself will periodically garbage collect your repos anyway, so no big deal. Here's my git garbage collection script, git-gc. You'll need to tweak "projectname.git" for your needs. Plus, you might need to gc multiple repos.

#!/bin/bash # Git garbage collection script. # Dump this in /etc/cron.weekly and call it git-gc. # Do git garbage collection cd /srv/git/projectname.git git gc --aggressive

Monitoring the Server

A server that is exposed to the public Internet needs to be monitored to make sure it continues to be secure. There are many ways to check this. This is definitely not an exhaustive checklist. In fact, it's a pretty lousy one based on a few things I've researched. I highly recommend looking elsewhere for advice on this.

The /var/log/auth.log file shows attempted ssh logins. You can monitor this to detect attempted break-ins.

denyhosts is a program that will watch for excessive login failures and ban the host that is causing them. This should block attackers before they get in by brute-force.

Use lsof -i to make sure no unexpected servers are running.

"logcheck" is a program that might be useful, but I've not looked into it.

An Intrustion Detection System like tripwire is also a good idea. It will detect modifications to the system that might be an indication of a break-in.

Building Git

If you want to keep up with the latest releases of Git, but still run the LTS version of Ubuntu, you'll need to download the Git source and build it.

First, you'll need to install some software to do the build:

$ sudo apt-get build-dep git $ sudo apt-get install autoconf

You can find the Git source on github. The tags on the Git project have .tar.gz files with the source:

You can use wget to download the file you want. Or you can use the text browser "w3m" to find and download the latest version:

$ w3m github.com/git/git/tags

Move the cursor on top of a link and hit enter to download. Press q to exit out of w3m.

Expand the code archive with tar:

$ tar -xf git-1.8.2.3.tar.gz

Within the git source directory, you will find an INSTALL file with instructions on how to build and install. For 1.8.2.3 the "configure" set of steps seemed to work best:

$ make configure $ ./configure --prefix=/usr $ make all doc $ sudo make install install-doc install-html

And you've got the latest Git.

UPS

I've been trying to get NUT to monitor my UPS and shutdown the server when the UPS runs out of power. Click here for details.

References

Ubuntu Server Guide 12.04

How to Deploy a Server - From Linux Journal. Walks through the various options for automating server setup: by hand, images, post-install script, central CM (e.g. Puppet and Chef). Might be worth looking into so that I can migrate the server easily.

The Git Book

Git Conflict Resolution - My article walking through one of the trickier aspects of git.

License

Copyright (C) 2012-2023, Ted Felix

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. See http://www.gnu.org/licenses/fdl.html for the full text of this license.

<- Back to my software page.