CodeGnome Consulting, LTD

Programming - DevOps - Project Management - Information Security

Remote Forwarding With SSH and Git-Daemon

| Comments

Local Git Repositories for Remote Machines

Have you ever needed to track changes to files on a remote host, but haven’t wanted to set up a dedicated Git repository on an external host? I certainly have.

I considered a variety of solutions, and settled on using remote SSH forwarding to a temporary instance of git-daemon running on my local machine. It’s fast, simple, and no less secure than storing your repository on the remote host in the first place.

Why “Remote Forwarding?”

SSH forwarding can be a little arcane. The short answer is that you differentiate between local and remote forwarding based on where the tunnel entrance is, rather than its endpoint.

In our case, Git will connect to port 9418 on the remote host, and tunnel back to the user’s local machine. That means the tunnel entrance (or listener) for forwarding is “remote.”

Initial Steps

First, you will need to create a base path for Git repositories on your local machine. Use anything you want, but ~/Documents/git is a sensible placeholder value.

Next, you need to create a bare repository on your local machine. For example, if you want to store /etc/apache2 from your remote host, you could use this example.

Create a Bare Repository
1
2
3
mkdir -p ~/Documents/git/apache2.git
cd ~/Documents/git/apache2.git
git init --bare

Create a Reverse-Forwarding Function

This is where the real magic happens. The idea here is that you create a function that will launch the Git protocol server on your local machine, fire up an SSH connection, and then tear everything down afterwards.

Create Function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
git-reverse-forward () {
    (
        BASEPATH=~/Documents/git
        git daemon \
            --base-path="$BASEPATH" \
            --enable=receive-pack \
            --enable=upload-archive \
            --export-all \
            "$BASEPATH" &
    )

    ssh -R 9418:localhost:9418 \
        -o ControlPersist=no   \
        -o ControlPath=none    \
        www.example.com

    pkill -u "$LOGNAME" -f '^git[- ]daemon'
}

You can paste this function into your current shell, or add it to ~/.bashrc so that you’ll have it every time you launch a new, interactive Bash shell. I recommend the latter.

WARNING: If you don’t enclose these steps in a script or function, Bash may incorrectly treat the git-daemon tear-down as an argument to the SSH client, and attempt to run the command on the remote machine. No, I don’t know why it does that; I just know that pasting the code directly into a terminal may not deliver the correct results.

Initialize the Remote Repository

If you haven’t already done so, go ahead call source ~/.bashrc to ensure that your handy-dandy new function is available. Try type -t git-reverse-forward if you want to make sure the function is available in your current shell.

Next, we’ll initialize a Git repository in /etc/apache2 on the remote host.

Initialize the Remote Repository
1
2
3
4
5
6
7
8
9
10
11
12
13
# Launch git-daemon and SSH.
git-reverse-forward

# Initialize your remote repository for first use.
cd /etc/apache2
sudo git init
sudo git remote add origin git://localhost/apache2.git
sudo git add .
sudo git commit -m 'Initial commit.'

# Push your changes from the remote host to your local machine.
sudo git push --set-upstream origin master
exit

Note that one needs to use sudo and sudoedit for most operations to avoid permission issues, unless you’re logging in as root. Oh, and if it actually needs to be said, don’t log in as root!

Use the Function, Luke!

Now that you’ve got everything hooked up and running properly, you should be able to make updates and push commits with minimal fuss. A typical setup/modify/teardown cycle will look similar to the following.

Example Function Invocation
1
2
3
4
5
git-reverse-forward
cd /etc/apache2
sudoedit apache2.conf
sudo git commit -av
sudo git push

Security Implications

Astute readers will have noted some security implications to this reverse-tunnel approach. Let’s address them head-on.

The receive-pack feature is needed to allow Git to push from the remote host to the local machine. However, it performs no authentication. In theory, an attacker could push changes into your bare repository whenever the reverse tunnel is open.

With that in mind, it’s worth noting some mitigating factors.

  1. The tunnel is only open for brief periods of time, and always initiated from the local machine. This limits the window of opportunity for mischief.

  2. Push operations to the bare repository represent minimal risk. While Mallory could push changes while the tunnel is open, this won’t change the actual /etc/apache2 directory on your web server; it will just push commits into the bare repository where git whatchanged or git log --patch will show you what commits have been pushed.

  3. Tainted commits can be reverted or filtered from the repository once identified. Of course, if you have a hostile user or process on your remote host, you have significant problems beyond push permissions into a bare repository.

  4. Changes to the working directory on the remote host can only be made by users with write permissions. In our example, only root can modify the files in /etc/apache2. This means the risk presented by a tainted origin is low so as long as we avoid sudo git pull in favor of sudo git fetch; git diff master origin/master and inspect the changes before updating the working directory with sudo git merge origin/master.

While I certainly recommend caution with any reverse tunnel, I think the overall risk of treating the reverse tunnel as a potentially-tainted file sink is not much different than pushing/pulling to a GitHub or Gitolite repository. While it’s true that authentication is stronger with other solutions, the risk of merging changes directly into your working tree (especially without inspecting them carefully) remains exactly the same.

All security controls represent a trade-off. As long as you update your working trees cautiously and are not operating in a hostile computing environment, then the increased convenience of this approach may be an acceptable security trade-off for you.

Comments