Super Pi Part 5: Git Server

Posted on April 12, 2021

I don't have a great reason for running a Git server on my network, except for maybe the old adage about storing data in three places. I first started using one when Microsoft bought out GitHub and I didn't know about Gitlab. I try not to spread too much hate, but I avoid Microsoft products partly on principle and partly because I use them all the time at work and get tired of them. Configuring my own Git server did give me an opportunity to learn some new Linux skills, though. The first thing I did is install Git:

sudo apt install git

With how active this Pi is going to be, I thought it would be prudent to keep my git files on a separate drive. For now, I just have a flash drive plugged into the Pi. If I end up configuring my Pi to boot from an SSD instead of an SD card, I might change things up, but for now I'll throw the git user's home directory on the flash drive.

Preparing the Drive

Since this drive will essentially be part of the file system, I want to reformat the whole thing with an ext4 file system. Faster writing isn't really a concern with how infrequently I commit and how small my codebases are, but I like the consistency. After plugging in my flash drive, I used fdisk to find out how it was mounted:

sudo fdisk -l

I found my flash drive was mounted on /dev/sdb1, so the next step is to unmount and create the file system, which deletes everything on it:

sudo umount /dev/sdb1
sudo mkfs.ext4 -O ^has_journal /dev/sdb1

I provided the ^has_journal option to omit the journal from the file system. Much like SD cards, flash drives also don't last forever, and I thought leaving off the journal might save a little wear.

The next thing I did was change the label of the drive. My main motivation for this is to make the mounting process easier. A happy side effect, though, is that if for some reason I throw this drive in my drawer of random computer stuff and find it later wondering what's on it, as soon as I plug it into a computer, the toast notification will show the label and hopefully give me a little more context. To change the label on the drive, I'll use e2label, provided by the OS:

sudo e2label /dev/sdb1 Git

Now, when I unplug the drive and plug it back in, I see Git show up in the toast notification informing me that I just plugged something in. The drive preparation is complete, so I pluged the drive into the Pi and used fdisk again to find out where it was mounted. This happened to be /dev/sdb1 again.

Git User Setup

Now I need a user to support our Git server. I don't want to rely on the pi user to manage my repositories, so I created a new one:

sudo adduser git

If you're following along from previous posts, I mentioned in Part 1 that I would have to re-enable password authentication, so I have to go into /etc/ssh/ssh_config and modify the PasswordAuthentication line:

PasswordAuthentication yes

Mounting the Drive

In addition to adding the /home/git directory, the command also adds some configuration and boot files which I see when I execute ls -a. If I mount the drive to /home/git and the operating system can't find these files on boot, I'm going to have a bad time. I mounted /dev/sdb1/ to /mnt and copied these files straight away to avoid the boot headache, using sudo because root happens to own /home/git:

sudo mount /dev/sdb1 /mnt
sudo cp /home/git/* /mnt

Next, I want to create a permanent mount point, so the OS will know to look for the flash drive and mount it to /home/git/. To make that happen, I added one line to /etc/fstab. This is where changing the label on the drive comes in handy:

LABEL=Git /home/git ext4 defaults 0 0

If the label were not set, I would have to use another unique identifier that would tell the operating system how to find the drive, like a PARTUUID. Now for the moment of truth, rebooting the Pi shows no terrible errors and I can add a file to /home/git/, turn off the Pi, take out the flash drive, put it in another PC, inspect the files, and see the file I added after the /etc/fstab change on the Pi. Before I get any farther, though, I'm plugging that drive back into the Pi.

Disable Password Authentication

I'll lock my Pi back down by disabling password login. This becomes a minor pain point whenever I want to pull files onto a new device, but it seldom happens, so I think it's worthwhile to do. First, though, I need to copy the SSH keys I generated for the pi user to the git user from every device that gets Git access:

ssh-copyid git@192.168.0.23

Once that's done, I can again disable password authentication. I'll need sudo access in order to do so, so I connected to the pi user over SSH and set PasswordAuthentication back to no. I then restarted SSH, so my changes would take effect:

sudo systemctl restart ssh

That's all the setup!

Using Git

Git is a complicated tool, but thankfully the documentation is pretty good. For now, I'll create a repository to see if everything works. From the git user on the Pi, I created a directory for all of my repositories called repos:

mkdir ~/repos

I'll change directories and create my new project:

cd repos
git init --bare myrepo.git

This creates a repo named myrepo.git. I'm using --bare here to tell Git that this repository is for storing the code. I won't be working with this repository directly, only pulling and pushing files to it from a linked repository on a different device. My rule is, if I'm using --bare, I'm also adding the .git component. It isn't necessary, but it is standard practice for source repos. Now, from one of the devices that has SSH keys registered with the Pi's git user, I can bring this repository down, but first I need to install git on the client, the same way I installed it on the Pi:

sudo apt install git

Now to bring down the repo:

git clone ssh://git@192.168.0.23/home/git/repos/myrepo.git

When I execute this command, a folder named myrepo is created in whatever directory my terminal happens to be in. Note, that the .git is left off of the folder. That's because this is a working repository as opposed to a storage repository. Any client accessing the Git server is considered a working client and will only have working repositories. Now I know my Git server works, that's one more box checked!

Remembering IP Addresses is Hard

I've been leveraging Pi-Hole's ability to resolve domains to IPs so much, why not do it here as well? It's actually simpler than it has been previously since I'm not proxying anything. If, however, I wanted to proxy all SSH requests to say, a port larger than 1024 to prevent any kind of sudo access (port 2200 maybe), I could set NGINX up as an SSH proxy in addition to an HTTP proxy. My understanding is that this is complicated to set up, and I'm not interested in limiting access this way, mostly because I want to have sudo access over SSH. Since I'm not proxying anything, all I have to do is open up Pi-Hole's admin UI, go to the DNS Records section, and add an entry to resolve my.git to 192.168.0.23. Now that git clone command becomes this:

git clone ssh://git@my.git/home/git/repos/myrepo.git

This has a neat advantage over cloning using the IP address. Under the hood, the clone command creates a remote named origin whose URI is ssh://git@my.git/home/git/repos/myrepo.git. Every repository I create will have its own URI. An added benefit to making this URI a domain instead of an IP address, is that if the IP address of the Pi (or whatever device is hosting all of my Git repositories) changes, I only need to change the IP in Pi-Hole so that the domain my.git points where I want it to. All of my remotes for all of my Git repositories will still function without any changes since Pi-Hole is resolving the domain to the proper IP. If every repository is pointing to an IP, then I have to go into each repository and manually change every IP to the new IP of the Git server.

That about sums up everything I did to get my Git server going and validate it. It didn't take too long, I think the longest part was getting that flash drive ready.