Can you specify git-shell in .ssh/authorized_keys to restrict access to only git commands via ssh?

38

26

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.

Matt Connolly

Posted 2011-06-21T03:50:15.310

Reputation: 1 313

Answers

31

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.

Neil Mayhew

Posted 2011-06-21T03:50:15.310

Reputation: 776

What shell do you have the user set to in this configuration? /bin/bash? – M-Pixel – 2016-08-01T08:44:17.500

@Qwertman, it doesn't matter. All the shell has to do is execute ./gitserve. In my system I do have it set to /bin/bash. If you're using /bin/true for security, however, it's not going to work. It must be set to a real shell. – Neil Mayhew – 2016-08-01T22:48:03.223

I'm wondering why it doesn't work with the restrict option. It's shorter and I usually prefer a whitelist to a blacklist but apparently restrict is more restrictive than the options of this answer. Does anyone know more?

– Mouagip – 2017-09-13T17:34:14.497

Bingo! This is works just like what I was hoping to achieve! Thanks. – Matt Connolly – 2011-10-06T21:57:12.493

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. – Neil Mayhew – 2012-04-04T22:32:32.083

1I understand, I merely added it as reference. – Tim – 2012-04-09T11:42:26.293

33

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...

sophana

Posted 2011-06-21T03:50:15.310

Reputation: 431

+1 that's the correct answer, see http://svnweb.freebsd.org/base/head/crypto/openssh/auth-options.c?revision=261320&view=markup#l154

– Tino – 2014-12-09T17:30:48.200

2Personally, I'd give the full path to git-shell, but that might just be paranoia. – Ulrich Schwarz – 2016-10-22T17:06:15.160

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 – 2017-03-01T08:12:23.527

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.

dschwen

Posted 2011-06-21T03:50:15.310

Reputation: 51

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

user1686

Posted 2011-06-21T03:50:15.310

Reputation: 283 655

@Matt: git-shell(1) says the commands are git <cmd>, not git-<cmd>?

– user1686 – 2011-06-22T09:50:45.930

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).
 – Matt Connolly  – 2011-06-23T00:16:36.057

formatting doesn't work in comments :( – Matt Connolly – 2011-06-23T00:16:52.983

1This 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. – Neil Mayhew – 2011-10-06T15:50:55.160

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 :-( – pvandenberk – 2011-10-13T23:45:41.143

Added eval to fix the quote issue. – user1686 – 2013-12-12T23:00:50.867

2

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.

Felix

Posted 2011-06-21T03:50:15.310

Reputation: 466

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! :-)

pvandenberk

Posted 2011-06-21T03:50:15.310

Reputation: 9 171