Setting up my private git server


Worth the pay off trust me!

I recently rented an ubuntu VM with a public IP and finally got the chance, without supervision, to architect what I wanted my little piece of processing power to do

So I wrote down what I needed/wanted

  • A portfolio website - needed
  • A blog website - wanted
  • A sandbox - needed
  • A scalable system - desperately wanted
  • An intermediary testing/delivery pipeline - needed

And all roads led back to Docker, more specifically, docker compose with plans on switching to Kubernetes the moment I had more than one VM

The next step was figuring out what to do. I needed a load balancer, not to deal with load balancing ironically enough, but redirecting traffic from sub domains to their respective containers, I needed a pipeline of sorts where I have full control on what my users end up getting, and lastly I needed a production and development environment with very minimal extra configuration

Now I could, host my code on GitHub, create a CI/CD pipeline with integration tests that publishes the images to a docker hub, use GitHub as the secret manager for production images and set everything up nicely with an organization worth biliions of dollars watching my back

But where’s the fun in that

I built my own private git server

That’s right! I built a private git server with hooks, Docker compose, hopes and dreams, and I’ve loved every moment of it. I will focus on explaining setting up the architecture and in future blogs talk about my individual repos/images - that’s right every repo becomes an image

Repo is slang for repository chat

Setting up your private repo

Firewall

The first thing I did was to install a firewall. I used ufw. It’s called the “Uncomplicated Firewall” and I’ll be honest, it felt that way. It was intuitive to use and there wasn’t any hiccup along the way

Now the last thing you want to do is set up a firewall and then realize you’re blocked from your VM the next time you ssh in, so you probably want this as a script, but here’s a run down of the commands I used

sudo apt update
sudo apt install ufw

Install ufw

sudo ufw allow 22/tcp
sudo ufw allow 443/tcp

Allow SSH and HTTPS through

sudo ufw enable

Enable Firewall

That’s it I think, you could search on their page for more commands and this

sudo ufw status verbose

shows the beauty you just created

Install git

sudo apt install git

It’s kind of expected but you don’t want to know how long it took me to figure this part out

Creating a git user

The next step was creating a git user, he’ll be the one responsible for everything git, including cloning - especially cloning

Here’s are the steps

sudo groupadd git

Create a git group

sudo useradd -m -s /usr/bin/git-shell -g git --disabled-password git

Create a git user, locked to the git shell, and without a password

The git shell’s a sanity check to make sure your git user doesn’t do anything(like running a script) other than git stuffs.

If you’re still paranoid - like me -, you could edit your /etc/ssh/sshd_config to include this block

Match User git
    PasswordAuthentication no
    AuthenticationMethods publickey

This prevents password login from the git user - even if they have a password

Enabling SSH access to git user

This part’s a bit messy, usually you’d sudo -iu git to become the git user, but the git user can only use git commands so that won’t help

Here’s what I did

# As root, cd to git's home directory
cd /home/git

# Then create a .ssh folder
mkdir .ssh

# cd to the .ssh folder
cd .ssh

# Create a file for storing the public keys
touch authorized_keys

# Set folder permissions
sudo chmod 700 /home/git/.ssh

# Set file permissions
sudo chmod 600 /home/git/.ssh/authorized_keys

# Ensure git owns it
sudo chown -R git:git /home/git/.ssh

And that’s it!

Create a public/private ssh key pair

The next step is to create a public/private key on your local laptop for ssh connection

Here’s what I used, and here’s where I got the code

ssh-keygen -t ed25519 -C "your_email@example.com"

The next part’s more aesthetic, but I’ll be assuming you all are fancy individuals and I’m also too lazy to assume otherwise

In your config file located at ~/.ssh - you’ll have to create one if you don’t have it - you’ll want to add this block

Host git-server
        HostName ip.number.goes.here
        User git
        IdentityFile absolute/path/to/private/key/goes/here
        IdentitiesOnly yes

You can change the Host name from git-server to whatever feels right to you, the Hostname is the IP of your git server and the IdentityFile is the absolute path to your newly created private key(the one without .pub)

Once you create the config file, you’ll also want to copy the content of the public part of the pair of keys you just created and move right back into your server - fingers crossed you aren’t locked out

Enabling access to SSH

cd /home/git/.ssh

Boo! Jumpscare haha

The next part is getting back to your git user .ssh directory

restrict content-from-public-key-here

and then updating the authorized_keys file with restrict followed by a single space and the content of the public file you copied earlier

Creating a repo

With all that out of the way, we can start with the cool parts

I’ll be using /opt/repos to store my repos, but honestly I think you should be good to create a folder in /home/git for your repos

Once you decide where to keep your repos, the next part is creating one!

Here’s my current system

mkdir my-project.git

Create the repo directory(my-project)

sudo -u git git init --bare my-project.git

Initialize the folder as a bare git repo

chown -R git:git my-project.git

Ensure the repo is owned by the git user - despite the shell witchery above, yes I’m paranoid

chmod -R 770 my-project.git

Add the right permissions for the git repo (full permissions for only user and group)

And that’s it

To be fair, initially I only created the directory and then ran git init --bare but along the way it morphed into this monstrosity. Feel free to copy if you’d like to automate the process, it’s the same thing with a lot more checks and redundant code

Clone repo

And now for the moment of truth, if everything worked, and I recounted correctly, this single command should get your repo right into your local computer

git clone git@git-server:/opt/repos/my-project.git

git-server was my fancy Host name, you’ll have to replace it with what you called it in the config file earlier if you decided to be even more fancy, and /opt/repos/my-project.git is a dummy stapler for the absolute directory to your previously created git repo

Author’s Notes

  • Bare repositories are built different. It sounds like a joke but I mean it, they look something like this

    root@localhost:~# ls /opt/repos/blog.git/
    branches  config  description  HEAD  hooks  index  info  logs  objects  refs

    You’ll have to clone the repo to get the actual code, currently I’ve only messed with the hooks and branches folders but that’s for another blog

  • I wrote this blog mostly from recollection and going through my chatgpt history, my brain may have completely shut down some hours of debugging to protect me haha, if there’s any error or pain point following the blog, join my little community and I’ll do my best to help you get through it!