42

I'd like to be able to use a ssh key for authentication, but still restrict the commands that can be executed over the ssh tunnel.

With Subversion, I've achieved this by using a .ssh/authorized_keys file like:

command="/usr/local/bin/svnserve -t --tunnel-user matt -r /path/to/repository",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty ssh-rsa AAAAB3NzaC1yc2EAAAABIetc...

I've tried this with "/usr/bin/git-shell" in the command, but I just get the funky old fatal: What do you think I am? A shell? error message.

6 Answers 6

36

The following works for me.

In ~/.ssh/authorized_keys:

command="./gitserve",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty ssh-dss AAAAB…

In the ~/gitserve script:

#!/bin/sh
exec git-shell -c "$SSH_ORIGINAL_COMMAND"

Note that if you put gitserve somewhere other than the home directory, you will have to adjust the command="./gitserve" parameter in authorized_keys.

7
  • Bingo! This is works just like what I was hoping to achieve! Thanks. Oct 6, 2011 at 21:57
  • In this related post on SO stackoverflow.com/questions/5871652/… they point to another solution here: joey.kitenet.net/blog/entry/locking_down_ssh_authorized_keys
    – Tim
    Mar 30, 2012 at 14:16
  • 1
    @Tim This is essentially the same solution, but squeezes the content of my ~/gitserve script into authorized keys by using perl. Personally, I prefer keeping it in a separate script. Apr 4, 2012 at 22:32
  • 1
    I understand, I merely added it as reference.
    – Tim
    Apr 9, 2012 at 11:42
  • What shell do you have the user set to in this configuration? /bin/bash?
    – M-Pixel
    Aug 1, 2016 at 8:44
43

I could successfully use git-shell directly in the authorizedKeys file without using an additionnal script.

The key is to add \" around the env variable.

Tested in rhel6 openssh-server-5.3p1-70.el6.x86_64:

no-port-forwarding,no-agent-forwarding,command="git-shell -c \"$SSH_ORIGINAL_COMMAND\"" ssh-dss AAAA...
3
  • +1 that's the correct answer, see svnweb.freebsd.org/base/head/crypto/openssh/…
    – Tino
    Dec 9, 2014 at 17:30
  • 2
    Personally, I'd give the full path to git-shell, but that might just be paranoia. Oct 22, 2016 at 17:06
  • Has anyone found a way of restricting the directory to which it has access? I found that i can cd into a directory before executing git-shell, but git-shell allows ".." and absolute paths.
    – yokto
    Mar 1, 2017 at 8:12
5

Grawity's solution can be easily modified to work by replacing the line

        exec $SSH_ORIGINAL_COMMAND

with the line

        git-shell -c "$SSH_ORIGINAL_COMMAND"

The quotes take care of the issues reported above, and replacing exec with git-shell seems a bit more secure.

4

git-shell is designed to be used as a login shell, so that it would receive -c "originalcommand" as arguments. This doesn't happen with "forced commands" in OpenSSH; instead, the forced command is passed to the configured shell.

What you can do is write a script that checks $SSH_ORIGINAL_COMMAND and executes it. Example in bash:

#!/bin/bash

SSH_ORIGINAL_COMMAND=${SSH_ORIGINAL_COMMAND/#git /git-}

case $SSH_ORIGINAL_COMMAND in
    "git-receive-pack"*|"git-upload-pack"*|"git-upload-archive"*)
        eval exec $SSH_ORIGINAL_COMMAND
        ;;
    *)
        echo "Go away." >&2
        exit 1
        ;;
esac
6
  • @Matt: git-shell(1) says the commands are git <cmd>, not git-<cmd>? Jun 22, 2011 at 9:50
  • Hmm... I made that change according to what I saw when I ran it. With both a bash script and a ruby script, I saw "git-receive-pack" as the command. Quote from my man git-shell (git 1.7.3.4): Currently, only four commands are permitted to be called, git-receive-pack git-upload-pack and git-upload-archive with a single required argument, or cvs server (to invoke git-cvsserver). Jun 23, 2011 at 0:16
  • formatting doesn't work in comments :( Jun 23, 2011 at 0:16
  • 1
    This doesn't work for me, because my git client (1.7.5.4) sends the repo name in single quotes, presumably because it's expecting to have the entire command line interpreted by a shell. The exec $SSH_ORIGINAL_COMMAND then passes the single quotes on to git-receive-pack etc. which therefore doesn't find the repository. Oct 6, 2011 at 15:50
  • Thanks for the solution, grawity, but it doesn't work for me, for the same reason that was reported by Neil Mayhew... my 1.7.x version of git also sends the repo name in single quotes, ultimately causing the $SSH_ORIGINAL_COMMAND to be invalid, and causing git-shell to bum out :-( Oct 13, 2011 at 23:45
3

For completeness, and since the original question didn't specify that the same account had to be useable for non-git things, the obvious answer is to use git-shell as it was designed to be used: set it as the login shell (i.e. with usermod, in /etc/passwd) for that ssh user.

If you do have a single user account that you want to use two ways:

  • when connecting with key authentication, use only for git
  • when connecting with password authentication, or using a different private key, provide a full shell

...then the other answers in this thread apply. But if you can afford to allocate a separate user to git, then setting its shell to git-shell is the simplest way to restrict it, and is slightly more secure as it requires no additional shell scripts.

1

I couldn't get grawity's solution to work for the same reason that was reported by Neil Mayhew (ie. the single quotes sent by the git client causing an invalid $SSH_ORIGINAL_COMMAND -- I'm using git v1.7.x)

However, the following solution implemented by @moocode just works:

https://moocode.com/posts/6-code-your-own-multi-user-private-git-server-in-5-minutes

Ruby FTW! :-)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .