shell

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 4dc335922e04ba720e6afd3a6cd98799edaccc2d
parent 8d60db10c6c9897ed71cfe0c7cc0e5542309e4a0
Author: Lapinot <peio.borthelle@ens-lyon.fr>
Date:   Tue, 16 Nov 2021 22:36:20 +0100

le rewrite de peio

Diffstat:
MREADME.md | 4++--
Apolicy.py | 16++++++++++++++++
Mshell.py | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
3 files changed, 80 insertions(+), 25 deletions(-)

diff --git a/README.md b/README.md @@ -6,8 +6,8 @@ Put `shell.py` in `/var/lib/git/` which should be the home of system user `git`. Make sure it is executable, and setup `.ssh/authorized_keys` as such: ``` -command="~/shell.py lucas",no-port-forwarding,no-X11-forwarding,no-agent-forwarding SSH_KEY -command="~/shell.py peio",no-port-forwarding,no-X11-forwarding,no-agent-forwarding SSH_KEY +restrict,command="~/shell.py lucas" SSH_KEY +restrict,command="~/shell.py peio" SSH_KEY ``` The first argument should be the username associated with the given SSH key. diff --git a/policy.py b/policy.py @@ -0,0 +1,16 @@ +import os +import re + +READ = 0b001 +WRITE = 0b010 +CREATE = 0b100 + +BASEDIR = os.path.expanduser('~/git-repos') + +re_private = re.compile(r'u/(?P<u>[^/]+)/(?P<r>.+)') + +def get_perm(user, repo): + m = re_private.match(repo) + if m and m.group('u') == user: + return READ | WRITE | CREATE + return 0 diff --git a/shell.py b/shell.py @@ -3,33 +3,72 @@ import sys import os import subprocess +import shlex + +import policy + +def die(msg, status=1): + sys.stderr.write(msg) + sys.stderr.write('\n') + sys.stderr.flush() + sys.stdout.flush() + sys.exit(status) + +def do_git_receive_pack(user, cmd, args): + if len(args) < 1: + die('error: git-receive-pack must be given an argument') + repo = args[-1] + path = os.path.join(policy.BASEDIR, repo) + perm = policy.get_perm(user, repo) + + if not policy.WRITE & perm: + die('error: sorry %s, you are not allowed to push to %s' % (user, repo)) + + if not os.path.isdir(path): + if not policy.CREATE & perm: + die('error: sorry %s, you are not allowed to create new repo %s' % (user, repo)) + subprocess.run(['git', 'init', '--bare', path], stdout=sys.stderr.buffer) + sys.stderr.write('info: created new repo %s' % repo) + + os.execvp('git-receive-pack', ['git-receive-pack', *args[:-1], path]) + +def do_git_upload_pack(user, cmd, args): + if len(args) < 1: + die('error: git-upload-pack must be given an argument') + repo = args[-1] + path = os.path.join(policy.BASEDIR, repo) + perm = policy.get_perm(user, repo) + + if not policy.READ & perm: + die('error: sorry %s, you are not allowed to pull from %s' % (user, repo)) + + if not os.path.isdir(path): + if not policy.CREATE & perm: + die('error: sorry %s, you are not allowed to create new repo %s' % (user, repo)) + subprocess.run(['git', 'init', '--bare', path], stdout=sys.stderr.buffer) + sys.stderr.write('info: created new repo %s' % repo) + + os.execvp('git-upload-pack', ['git-upload-pack', *args[:-1], path]) + +def do_default(user, cmd, args): + die('Hello there, %s!\nThis the SBIRE custom shell for git.' % user, status=0) + +COMMANDS = { + 'git-receive-pack': do_git_receive_pack, + 'git-upload-pack': do_git_upload_pack, +} def main(): + if len(sys.argv) != 2: + die('usage: shell.py USER') user = sys.argv[1] - cmd, *args = os.getenv("SSH_ORIGINAL_COMMAND").split(" ") - - if cmd == "git-receive-pack": - repo = args[0][1:-1] - path = os.path.join(os.getcwd(), repo) - # TODO: check authorization for pushing to repo - - # if repo does not exist, create it - # TODO: this fails when creating the repo - # on 2nd push everything is fine - if not os.path.isdir(path): - subprocess.run(["git", "init", "--bare", repo]) - sys.stderr.write("Created repository %s" % repo) - - subprocess.run(["git-receive-pack", args[0][1:-1]]) - if cmd == "git-upload-pack": - # TODO: check whether repo exists - # TODO: check authorization for reading from repo - subprocess.run(["git-upload-pack", args[0][1:-1]]) - - elif cmd == "help": - print(("Hello there, {}!\n" - "This is our custom shell for git.").format(user)) + ssh_orig = os.getenv('SSH_ORIGINAL_COMMAND') + if not ssh_orig: + die('error: SSH_ORIGINAL_COMMAND is empty') + cmd, *args = shlex.split(ssh_orig) + + COMMANDS.get(cmd, do_default)(user, cmd, args) if __name__ == '__main__': main()