aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTommi Virtanen <tv@eagain.net>2007-06-04 14:22:50 +0300
committerTommi Virtanen <tv@eagain.net>2007-06-04 14:22:50 +0300
commit2723db4cbd408e773ba862199c9ae29058a6da9f (patch)
treee59e07b9948ebf06b95e6b590e64e569f7672aea
parentEveryone always belongs to group "all". (diff)
downloadgitosis-dakkar-2723db4cbd408e773ba862199c9ae29058a6da9f.tar.gz
gitosis-dakkar-2723db4cbd408e773ba862199c9ae29058a6da9f.tar.bz2
gitosis-dakkar-2723db4cbd408e773ba862199c9ae29058a6da9f.zip
Add actual SSH-based git serving component.
-rw-r--r--gitosis/serve.py106
1 files changed, 106 insertions, 0 deletions
diff --git a/gitosis/serve.py b/gitosis/serve.py
new file mode 100644
index 0000000..cc189ad
--- /dev/null
+++ b/gitosis/serve.py
@@ -0,0 +1,106 @@
+#!/usr/bin/python
+
+"""
+Enforce git-shell to only serve repositories in the given
+directory. The client should refer to them without any directory
+prefix. Repository names are forced to match ALLOW.
+"""
+
+import logging; logging.basicConfig(level=logging.DEBUG)
+
+import sys, os, optparse, re
+from ConfigParser import RawConfigParser
+
+from gitosis import access
+
+def die(msg):
+ print >>sys.stderr, '%s: %s' % (sys.argv[0], msg)
+ sys.exit(1)
+
+def getParser():
+ parser = optparse.OptionParser(
+ usage='%prog [--config=FILE] USER',
+ description='Allow restricted git operations under DIR',
+ )
+ parser.set_defaults(
+ config=os.path.expanduser('~/.gitosis.conf'),
+ )
+ parser.add_option('--config',
+ metavar='FILE',
+ help='read config from FILE',
+ )
+ return parser
+
+ALLOW_RE = re.compile("^(?P<command>git-(?:receive|upload)-pack) '(?P<path>[a-zA-Z][a-zA-Z0-9@._-]*(/[a-zA-Z][a-zA-Z0-9@._-]*)*)'$")
+
+COMMANDS_READONLY = [
+ 'git-upload-pack',
+ ]
+
+COMMANDS_WRITE = [
+ 'git-receive-pack',
+ ]
+
+def main():
+ os.umask(0022)
+
+ parser = getParser()
+ (options, args) = parser.parse_args()
+ try:
+ (user,) = args
+ except ValueError:
+ parser.error('Missing argument USER.')
+
+ cmd = os.environ.get('SSH_ORIGINAL_COMMAND', None)
+ if cmd is None:
+ die("Need SSH_ORIGINAL_COMMAND in environment.")
+
+ if '\n' in cmd:
+ die("Command may not contain newlines.")
+
+ match = ALLOW_RE.match(cmd)
+ if match is None:
+ die("Command to run looks dangerous")
+
+ cfg = RawConfigParser()
+ cfg.read(options.config)
+
+ os.chdir(os.path.expanduser('~'))
+
+ command = match.group('command')
+ if (command not in COMMANDS_WRITE
+ and command not in COMMANDS_READONLY):
+ die("Unknown command denied.")
+
+ path = match.group('path')
+
+ # write access is always sufficient
+ newpath = access.haveAccess(
+ config=cfg,
+ user=user,
+ mode='writable',
+ path=path)
+
+ if newpath is None:
+ # didn't have write access
+
+ newpath = access.haveAccess(
+ config=cfg,
+ user=user,
+ mode='readonly',
+ path=path)
+
+ if newpath is None:
+ die("Access denied.")
+
+ if command in COMMANDS_WRITE:
+ # didn't have write access and tried to write
+ die("Write access denied.")
+
+ # put the command back together with the new path
+ newcmd = "%(command)s '%(newpath)s'" % dict(
+ command=command,
+ newpath=newpath,
+ )
+ os.execve('/usr/bin/git-shell', ['git-shell', '-c', newcmd], {})
+ die("Cannot execute git-shell.")