It would be great to support git-annex in ikiwiki-hosting. One of the things that would need to change would be the iki-git-shell command, which currently doesn't allow git-annex-shell to run, which breaks git-annex. I tried setting the annex-shell variable on the client side to git annex-shell to try to workaround the issue, but it still fails with:

fatal: unrecognized command 'git-annex-shell '/home/w-...''
...

The client then sets the annex-ignore variable and ignores that remote. And of course we'd also need ikiwiki itself to support git-annex but that's another story - at least allowing uploads would be a nice start.

The workaround, of course, is to set git-annex-shell as the command in the authorized_keys file instead of iki-git-shell... but that doesn't quite work because the remote URLs change completely.

So one solution is to exploit the git-shell-commands extension of git-shell, which allows running arbitrary commands from git-shell. Since git-annex starts with git, it will clear the iki-git-shell checks - but it fails in git-shell because it's not a recognized git command. To fix this, we need to add the following script in $HOME/git-shell-commands/git-annex-shell:

#!/usr/bin/python

import os
import re
import shlex
import sys

try:
    # original command is like:
    # "git-annex-shell 'configlist' '/'"
    # argv is expected to be:
    # ['git-shell-commands/git-annex-shell', '/home/.../source.git/']
    # the last argument is the sanitized repo path from iki-git-shell
    # so parse the original command string into an array safely
    orig_argv = shlex.split(os.environ['SSH_ORIGINAL_COMMAND'])
    # construct the command with the original command and the safe repo path
    cmd = ['git-annex-shell', '-c', orig_argv[1], sys.argv[1]]
    # if we have more arguments, shove them in (skipping unsafe path)
    cmd += orig_argv[3:]
    # restrict git-shell to known commands
    os.environ['GIT_ANNEX_SHELL_LIMITED'] = 'true'
    # restrict git-annex to the sanitized repository
    os.environ['GIT_ANNEX_SHELL_DIRECTORY'] = sys.argv[1]
    # keep all commands as readonly, disabled
    #os.environ['GIT_ANNEX_SHELL_READONLY'] = 'true'
    # split into that new command
    os.execlp('git-annex-shell', *cmd)
except Exception as e:
    print "git-annex-shell wrapper improperly called: %s" % e

This will not work out of the box with iki-git-shell because the C code garbles the SSH_ORIG_COMMAND with strtok. The following patch is also necessary to carry the command down below:

diff --git a/iki-git-shell.c b/iki-git-shell.c
index 70250ad..37639a2 100644
--- a/iki-git-shell.c
+++ b/iki-git-shell.c
@@ -74,6 +74,10 @@ int main (int argc, char **argv) {
                fprintf(stderr, "error: SSH_ORIGINAL_COMMAND not set\n");
                exit(1);
        }
+       if (asprintf(&command, command) == -1) {
+               perror("asprintf");
+               exit(1);
+       }

        s=strtok(command, " -"); /* handle "git-" and "git " */
        if (! s) {

That way the tokenizer operates on a copy of the string instead of garbling the environment directly.

Now, I know that:

  1. this is clunky
  2. it's python (heretic! burn! burn!)
  3. there's more comments than code (but I took a while to figure that stuff out, so better to share)
  4. it doesn't solve git-annex support in ikiwiki (ie. the objects won't get pushed to the source repo)
  5. it's 1h30AM on a friday

But:

  1. it's a start!
  2. it works (ie. it will drop keys, share configs, etc)
  3. it should be fairly secure

Your feedback would be greatly appreciated here. At the very least I think the patch on iki-git-shell can't hurt, unless hiding the original git command was deliberate.

Thanks! -- anarcat