Homemade git remote

So it all began with a wish to set up a git remote on a dev server that should receive pushes and run a deploy script triggered by a git hook. Of course - said to myself - I don’t need any of those fancy tools available, that’s just a couple of lines in shell, and we’re good to go. The truth is, it really is. What actually happened is, once again, it took a long time to find a shorter way.

Anyway, just to clarify the end goal, the repository will be owned by the git user, so the hooks that trigger the deploy script will be ran by the same git user as well, but the project needs to be owned and ran by a user other than git.

So, for the sake of clarity, let’s imagine that my dev server (the one where I want the git remote to be located) is on the IP address: 111.22.33.44 , and that I have a user account there named thedev which has sudo privileges.

So the first thing we do is to connect to the server with ssh:

ssh thedev@111.22.33.44

The project will have to be deployed to the following location, so make some place for it:

mkdir -p /home/thedev/dev/myproject

Assuming it’s a never before used server, we want to create a git user and group, under which account we will actually run our business. To add the git user and group and set it’s password:

sudo groupadd git
sudo useradd -G git git
sudo passwd git

Next, create the git home folder and make the git user the owner of it:

sudo mkdir -p /home/git
sudo chown git:git /home/git

Change the login shell of the git user to bash, just to make life easier:

sudo chsh -s /bin/bash git

Create the folder where the actual bare repository will be, and make the git user/group the owner of it:

sudo mkdir -p /opt/git
sudo chown git:git /opt/git

Also, if git is not already installed on the server:

sudo apt-get install git

exit

Now that we completed all this, we returned to our local machine, and now we’re going to connect through ssh using the newly created git user and create the bare git repository:

ssh git@111.22.33.44

cd /opt/git
mkdir myreponame.git
cd myreponame.git
git init --bare --shared

So the repository is created, we just have to add the public ssh keys of those users, who will be allowed to push to this remote. For start, you can add your own only, and later the others. You can find yours in the ~/.ssh/ folder (on your local machine), it will be a file with .pub extension. There may be multiple keys there of course, so choose the one you wish to use. If there are no keys in that folder, be bold and create one (when asked for a filename, it really is optional, but please choose a passphrase):

cd ~/.ssh/
ssh-keygen -t rsa -C "thedev@mycompany.com"
ssh-add id_rsa

Assuming you didn’t choose a custom filename, id_rsa will be used as it’s the default one. To get the public key:

cat id_rsa.pub

Copy the whole output, full lines (don’t skip the “ssh-rsa” part), and now that you obtained the public key, get back to the terminal window with the active ssh connection to the remote server and:

cd /home/git
mkdir .ssh
cd .ssh
nano authorized_keys

Paste the public ssh key(s) of the user(s) allowed to push here, one per line, and save the file. That’s it, we’re almost done, we just have to set up the deployment hook:

cd /opt/git/myreponame.git/hooks

There are many sample hook scripts in this folder already, they all end with the .sample extension. To actually use one of these hooks, the .sample extension needs to be removed, and executable permissions must be added to the script, but I just went with creating a new file:

nano post-receive

This snippet will do the whole “deployment”, just enter the contents and save the file (but make sure to correct the paths and owner parameters):

#!/bin/sh
GIT_WORK_TREE=/home/thedev/dev/myproject git checkout -f
sudo -u root chown -R thedev:git /home/thedev/dev/myproject

There is only one problem with this, we’re trying to give ownership to another user in this script, which requires root privileges. To allow this, leave the system, and ssh back again:

exit
ssh thedev@111.22.33.44

sudo visudo

Giving permissions away could be quite dangerous, that is why we limit the command to work only for these exact paramters, but still, I’d like some insight from someone else if this is exploitable in some way. Regardless, for now just add this line to the file:

git ALL=(root) NOPASSWD: /bin/chown -R thedev\:git\ /home/thedev/dev/myproject

This will allow the git user to execute the above chown operation without password prompt.

exit

Once again, we returned to our local machine. Go to your local git repository that needs to be pushed to the newly created remote:

cd my/repo/location/that/needs/to/be/pushed

You can choose any name for the remote, devserver was my choice here:

git remote add devserver git@111.22.33.44:/opt/git/myreponame.git
git push devserver master
# or any other branch you wish

If you poke into your remote server after the push, you’ll see how the hook executed the deploy script, and the pushed project can be found on the path you specified in the script. Extend it however you like, just watch out for permission issues, it’s quite easy to run into them.

Oh yes, I can feel the waves of astonishment how blown away you are right now.

Discussion

comments powered by Disqus