diff options
Diffstat (limited to 'gitosis/ssh.py')
-rw-r--r-- | gitosis/ssh.py | 66 |
1 files changed, 43 insertions, 23 deletions
diff --git a/gitosis/ssh.py b/gitosis/ssh.py index a315a5c..bfd52fb 100644 --- a/gitosis/ssh.py +++ b/gitosis/ssh.py @@ -1,14 +1,14 @@ +""" +Gitosis code to handle SSH authorized_keys files +""" import os, errno, re import logging +from gitosis import sshkey +# C0103 - 'log' is a special name +# pylint: disable-msg=C0103 log = logging.getLogger('gitosis.ssh') -_ACCEPTABLE_USER_RE = re.compile(r'^[a-zA-Z][a-zA-Z0-9_.-]*(@[a-zA-Z][a-zA-Z0-9.-]*)?$') - -def isSafeUsername(user): - match = _ACCEPTABLE_USER_RE.match(user) - return (match is not None) - def readKeys(keydir): """ Read SSH public keys from ``keydir/*.pub`` @@ -20,30 +20,42 @@ def readKeys(keydir): if ext != '.pub': continue - if not isSafeUsername(basename): + if not sshkey.isSafeUsername(basename): log.warn('Unsafe SSH username in keyfile: %r', filename) continue path = os.path.join(keydir, filename) - f = file(path) - for line in f: + fp = file(path) + for line in fp: line = line.rstrip('\n') - yield (basename, line) - f.close() + if line.startswith('#'): + continue + line = line.strip() + if len(line) > 0: + yield (basename, sshkey.get_ssh_pubkey(line)) + fp.close() COMMENT = '### autogenerated by gitosis, DO NOT EDIT' +SSH_KEY_ACCEPTED_OPTIONS = ['from'] def generateAuthorizedKeys(keys): - TEMPLATE=('command="gitosis-serve %(user)s",no-port-forwarding,' - +'no-X11-forwarding,no-agent-forwarding,no-pty %(key)s') + """ + Genarate the lines for the Gitosis ~/.ssh/authorized_keys. + """ + TEMPLATE = ('%(options)s %(key)s %(comment)s') + OPTIONS = ('command="gitosis-serve %(user)s",no-port-forwarding,' + +'no-X11-forwarding,no-agent-forwarding,no-pty') yield COMMENT for (user, key) in keys: - yield TEMPLATE % dict(user=user, key=key) + options = OPTIONS % dict(user=user, ) + for k in SSH_KEY_ACCEPTED_OPTIONS: + if k in key.options: + options += (',%s="%s"' % (k, key.options[k])) + yield TEMPLATE % dict(user=user, key=key.key, comment=key.comment, options=options) -_COMMAND_RE = re.compile('^command="(/[^ "]+/)?gitosis-serve [^"]+",no-port-forw' - +'arding,no-X11-forwarding,no-agent-forwardi' - +'ng,no-pty .*') +_GITOSIS_CMD_RE = '(/[^ "]+/)?gitosis-serve [^ "]+$' +_COMMAND_RE = re.compile(_GITOSIS_CMD_RE) def filterAuthorizedKeys(fp): """ @@ -56,16 +68,24 @@ def filterAuthorizedKeys(fp): line = line.rstrip('\n') if line == COMMENT: continue - if _COMMAND_RE.match(line): - continue + try: + key = sshkey.get_ssh_pubkey(line) + if 'command' in key.options and \ + _COMMAND_RE.match(key.options['command']): + continue + except sshkey.MalformedSSHKey: + pass yield line def writeAuthorizedKeys(path, keydir): + """ + Update the Gitosis ~/.ssh/authorized_keys for the new Gitosis SSH key data. + """ tmp = '%s.%d.tmp' % (path, os.getpid()) try: in_ = file(path) - except IOError, e: - if e.errno == errno.ENOENT: + except IOError, ex: #pragma: no cover + if ex.errno == errno.ENOENT: in_ = None else: raise @@ -75,11 +95,11 @@ def writeAuthorizedKeys(path, keydir): try: if in_ is not None: for line in filterAuthorizedKeys(in_): - print >>out, line + print >> out, line keygen = readKeys(keydir) for line in generateAuthorizedKeys(keygen): - print >>out, line + print >> out, line os.fsync(out) finally: |