#! /usr/bin/env python from subprocess import call import base64 import gnupg # python-gnupg root_user_fs_path = './user_fses/' root_repo_path = './repos/' authorized_keys_base = '/var/authorized_keys/' gpg = gnupg.GPG(gnupghome='/does/not/exist') def recv_keys(gpg_key_path, ssh_key_path): fingerprint = full_fingerprint(gpg_key_path) username = tiny_gpg_fingerprint(fingerprint) alloc_user(username) add_key(username, ssh_key_path) print("gpg key: %s, ssh key: %s" % (gpg_key_path, ssh_key_path)) print("username: %s, gpg fingerprint: %s" % (username, fingerprint)) return True def full_fingerprint(gpg_key_path): keys = gpg.scan_keys(gpg_key_path) if (len(keys) == 0): raise "hell" if (len(keys) != 1): raise "the roof" return keys[0]["fingerprint"] # the s/./=/g thing here is because # = is apparently not valid in a username! # but having the padding around is nice. so keep it. # so turn it into valid characters! def untiny_gpg_fingerprint(tiny): return binascii.unhexlify( base64.b64decode(tiny.replace('.', '='), '-_') ) def tiny_gpg_fingerprint(raw_fingerprint): fingerprint_bytes = raw_fingerprint.decode('hex') return base64.b64encode(fingerprint_bytes, '-_').replace('=', '.') def add_key(username, ssh_key_path): with open(ssh_key_path, 'r') as key_file: ssh_key = key_file.read() with open(authorized_keys_base + username, 'a+') as user_authorized_keys: user_authorized_keys.write(ssh_key) return True def alloc_user(username): if username == '': raise Exception("Username must not be empty and must be alphanumeric (but it's derived from your full gpg key id, so how did you do that?") fs_path = root_user_fs_path + username repo_path = root_repo_path + username # [D]on't give a password (ssh login only) # Don't create a [H]ome directory # Set the login [s]hell to a non-shell to disallow logins call(['adduser', '-D', '-H', '-s', '/bin/false', username]) call(['dd', 'if=/dev/zero', 'of=' + fs_path, 'bs=4096', 'count=32768']) call(['mkfs.ext4', '-q', fs_path]) call(['mkdir', repo_path]) call(['mount', '-o', 'loop,rw', fs_path, repo_path + '/']) call(['touch', repo_path + '/git-daemon-export-ok'])