Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add a git command wrapper script, adapted from the one running on git…

….postgresql.org.

A much simplified version that just ensures that correct git commands are attempted
and that makes sure the commands are logged.
  • Loading branch information...
commit f609dcf69231537c968157edfb41589599368811 1 parent 81e3c09
@mhagander authored
Showing with 141 additions and 0 deletions.
  1. +28 −0 README.rst
  2. +113 −0 gitwrap.py
View
28 README.rst
@@ -105,3 +105,31 @@ committerlist
config file. This ensures that committers don't accidentally use a
badly configured client. All the commiters should be listed in the
[committers] section, in the format User Name=email.
+
+
+git command wrapper script
+==========================
+This script wraps the command run through ssh to make sure that it can
+only be approved git commands, and to make sure the commands are logged
+with who does what.
+
+The script is adapted from the one running on git.postgresql.org, but
+significantly simplified.
+
+Installation & configuration
+----------------------------
+Put the script ``gitwrap.py`` "somewhere". In the same directory, create
+a file called ``gitwrap.ini`` with contents like this: ::
+
+ [paths]
+ logfile=/some/where/gitwrap.log
+ gitrepo=/some/where/repository.git
+
+Make sure the git user has permissions on these directories.
+
+When this is done, put something like this in ``~/.ssh/authorized_keys``
+for the git user: ::
+
+ command="/home/git/gitwrap/gitwrap.py 'Some User'",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa ABCDABCD<sshkeyhere>
+
+One row for each committer.
View
113 gitwrap.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+
+#
+# Simple wrapper around the git commands to ensure that we only speak
+# git. Should be referenced in the authorized_keys file.
+#
+# Copyright (C) 2010 PostgreSQL Global Development Group
+# Author: Magnus Hagander <magnus@hagander.net>
+#
+# Released under the PostgreSQL license
+#
+# Looks for a config file in the same directory as gitwrap.py called
+# gitwrap.ini, containing:
+#
+# [paths]
+# logfile=/some/where/gitwrap.log
+# gitrepo=/some/where/repository.git
+#
+
+import sys
+import os
+import os.path
+import ConfigParser
+
+ALLOWED_COMMANDS = ('git-upload-pack', 'git-receive-pack')
+
+class Logger(object):
+ def __init__(self, cfg):
+ self.user = "Unknown"
+ self.logfile = cfg.get('paths','logfile')
+
+ def log(self, message):
+ f = open(self.logfile,"a")
+ f.write("(%s): %s" % (self.user, message))
+ f.write("\n")
+ f.close()
+
+ def setuser(self, user):
+ if user:
+ self.user = user
+
+class InternalException(Exception):
+ pass
+
+class PgGit(object):
+ user = None
+ command = None
+ path = None
+
+ def __init__(self, cfg):
+ self.cfg = cfg
+ self.logger = Logger(cfg)
+ self.path = "%s" % cfg.get('paths', 'gitrepo')
+
+ def parse_commandline(self):
+ if len(sys.argv) != 2:
+ raise InternalException("Can only be run with one commandline argument!")
+ self.user = sys.argv[1]
+ self.logger.setuser(self.user)
+
+ def parse_command(self):
+ env = os.environ.get('SSH_ORIGINAL_COMMAND', None)
+ if not env:
+ raise InternalException("No SSH_ORIGINAL_COMMAND present!")
+
+ # env contains "git-<command> <argument>" or "git <command> <argument>"
+ command, args = env.split(None, 1)
+ if command == "git":
+ subcommand, args = args.split(None,1)
+ command = "git-%s" % subcommand
+ if not command in ALLOWED_COMMANDS:
+ raise InternalException("Command '%s' not allowed" % command)
+
+ self.command = command
+ if not args.startswith("'/"):
+ raise InternalException("Expected git path to start with slash!")
+
+ if not self.path.endswith(args[2:].rstrip("'")):
+ raise InternalException("Incorrect repository path")
+
+ def run_command(self):
+ self.logger.log("Running \"git shell %s %s\"" % (self.command, "'%s'" % self.path))
+ os.execvp('git', ['git', 'shell', '-c', "%s %s" % (self.command, "'%s'" % self.path) ])
+
+ def run(self):
+ try:
+ self.parse_commandline()
+ self.parse_command()
+ self.run_command()
+ except InternalException, e:
+ try:
+ self.logger.log(e)
+ except Exception, e:
+ pass
+ sys.stderr.write("%s\n" % e)
+ sys.exit(1)
+ except Exception, e:
+ try:
+ self.logger.log(e)
+ except Exception, e:
+ # If we failed to log, try once more with a new logger, otherwise,
+ # just accept that we failed.
+ try:
+ Logger().log(e)
+ except:
+ pass
+ sys.stderr.write("An unhandled exception occurred on the server\n")
+ sys.exit(1)
+
+if __name__ == "__main__":
+ c = ConfigParser.ConfigParser()
+ c.read("%s/gitwrap.ini" % os.path.abspath(sys.path[0]))
+ PgGit(c).run()
Please sign in to comment.
Something went wrong with that request. Please try again.