среда, июля 22, 2009

Allowing ssh access to Darcs repositories

I've been using Darcs since early 2006 and I do not see any reason to switch to any other (D)VCS like git.

Today at work we decided to put our new project into Darcs and I needed to setup access for others to my repository. The readonly access is not a problem, but allowing others to push into my repositories was a bit trickier. There is a protocol wrapper called "darcs-server", but I don't like that solution. I wanted to configure Darcs to do get/pull/push with minimal additional components.

The traditional way to do Push is via SSH. You can create a separate account for Darcs VCS or just share your own. Then, you set up access via public/private keys and here we go: you can do all operations. The only thing is that everybody can do anything with your account, which is bad. You need to restrict users to running just a small set of commands sufficient for normal Darcs operation. With Darcs 2 it turns out that you only need to allow running "darcs" with various arguments (as Darcs 2 uses special tunelling to do all work while Darcs 1 used to open tons of connections). After several experiments, here is what you need:


#!/bin/sh

first_word() { echo $1; }

line="$SSH_ORIGINAL_COMMAND"

command=$(first_word $line)
if [ "$command" = "darcs" ]; then
sh -c "$line"
else
echo "$command is not allowed"
fi


Then, you add "command='/home/username/bin/darcs-wrapper' " at the beginning of your collegues' public key lines in ~/.ssh/authorized_keys and boom - they are restricted to running only darcs. You can also add no-port-forwarding, no-X11-forwarding, no-agent-forwarding, no-pty to tighten security. And you can set environment variable DARCS_LOGNAME to name of particular user to get proper names in darcs changes logs: add "environment='DARCS_LOGNAME=username'" to your authorized_keys file.

Hope that helps.

UPDATE: after thinking a little, I found a case when this can be workaround to execute any command, e.g. "darcs help; rm -rf *". The problem is with "sh -c". Originally, it was introduced in place of call to "exec" to workaround problem with extra quotation marks that are not removed when parsing line and which cause Darcs to handle arguments improperly. It turns out, that "exec" is really needed there and unquoting should be done manually. Here is the corrected version:


#!/bin/sh

first_word() { echo $1; }
unquoted_words() {
for word in $@; do
echo $(echo $word | sed -E "s/^'(.*)'$/\1/; s/^\"(.*)\"$/\1/")
done
}

line="$SSH_ORIGINAL_COMMAND"

command=$(first_word $line)
if [ "$command" = "darcs" ]; then
exec $(unquoted_words $line)
else
echo "$command is not allowed"
fi


UPDATE2: that "sed -E" thing seem to not work on Linux. Linux Sed's option for full regular expressions is called "-r". Probably need to try rewriting regexps to normal Sed's functionality. Later.

UPDATE3: very handy thing in debugging Darcs' SSH is using "--debug --verbose" options to "darcs". Then, you can see what commands are sent to server and how response is interpreted. Then, you can issue those commands manually and see if server's wrapper script outputs errors.