Add actual SSH-based git serving component.
This commit is contained in:
parent
65dc130228
commit
2723db4cbd
1 changed files with 106 additions and 0 deletions
106
gitosis/serve.py
Normal file
106
gitosis/serve.py
Normal file
|
@ -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.")
|
Loading…
Reference in a new issue