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.”
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.
1 2 3
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
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
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.
1 2 3 4 5 6 7 8 9 10 11 12 13
Note that one needs to use
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.
1 2 3 4 5
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.
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.
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 log --patchwill show you what commits have been pushed.
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.
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 pullin favor of
sudo git fetch; git diff master origin/masterand 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.