#!/usr/bin/python2 import sys import os import random import shutil import tempfile import argparse import textwrap try: import ldap except ImportError, e: print "Please install python-ldap before running this program" sys.exit(1) basedn="<%= dc_suffix %>" peopledn="ou=people,%s" % basedn <%- ldap_servers.map! { |l| "'ldaps://#{l}'" } -%> uris=[<%= ldap_servers.join(", ") %>] random.shuffle(uris) uri = " ".join(uris) timeout=5 binddn="cn=<%= fqdn %>,ou=Hosts,%s" % basedn ldap_secret_file="<%= ldap_pwfile %>" nslcd_conf_file="<%= nslcd_conf_file %>" # filter out disabled accounts also # too bad uidNumber doesn't support >= filters filter="(&(objectClass=inetOrgPerson)(objectClass=ldapPublicKey)(objectClass=posixAccount)(sshPublicKey=*))" keypathprefix='/home' parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description=textwrap.dedent('''\ Will fetch all enabled user accounts under %s with ssh keys in them and write each one to %s//.ssh/authorized_keys It will return failure when no keys are updated and success when one or more keys have changed. This script is intended to be run from cron as root; ''' % (peopledn, keypathprefix))) parser.add_argument('-n', '--dry-run', action='store_true') parser.add_argument('-v', '--verbose', action='store_true') args = parser.parse_args() def get_bindpw(): try: return get_nslcd_bindpw(nslcd_conf_file) except: pass try: return get_ldap_secret(ldap_secret_file) except: pass print "Error while reading password file, aborting" sys.exit(1) def get_nslcd_bindpw(pwfile): try: f = open(pwfile, 'r') except IOError, e: print "Error while reading nslcd file " + pwfile print e raise pwfield = "bindpw" for line in f: ls = line.strip().split() if len(ls) == 2 and ls[0] == pwfield: f.close() return ls[1] f.close() print "No " + pwfield + " field found in nslcd file " + pwfile raise Exception() def get_ldap_secret(pwfile): try: f = open(pwfile, 'r') except IOError, e: print "Error while reading password file " + pwfile print e raise pw = f.readline().strip() f.close() return pw def write_keys(keys, user, uid, gid): keyfile = "%s/%s/.ssh/authorized_keys" % (keypathprefix,user) fromldap = '' for key in keys: fromldap += key.strip() + "\n" fromfile = '' try: f = open(keyfile, 'r') fromfile = f.read() f.close() except: pass if fromldap == fromfile: return False if args.dry_run: print "Would write %s" % keyfile return True if args.verbose: print "Writing %s" % keyfile if not os.path.isdir("%s/%s" % (keypathprefix,user)): shutil.copytree('/etc/skel', "%s/%s" % (keypathprefix,user)) os.chown("%s/%s" % (keypathprefix,user), uid, gid) for root, dirs, files in os.walk("%s/%s" % (keypathprefix,user)): for d in dirs: os.chown(os.path.join(root, d), uid, gid) for f in files: os.chown(os.path.join(root, f), uid, gid) try: os.makedirs("%s/%s/.ssh" % (keypathprefix,user), 0700) except: pass os.chmod("%s/%s/.ssh" % (keypathprefix,user), 0700) os.chown("%s/%s/.ssh" % (keypathprefix,user), uid, gid) (fd, tmpname) = tempfile.mkstemp('', 'ldap-sshkey2file-') os.write(fd, fromldap); os.close(fd) os.chmod(tmpname, 0600) os.chown(tmpname, uid, gid) shutil.move(tmpname, keyfile) # Hmm, apparently shutil.move does not preserve user/group so lets reapply # them. I still like doing it before as this should be more "automic" # if it actually worked, so it's "good practice", even if shutil.move sucks os.chown(keyfile, uid, gid) os.chmod(keyfile, 0600) return True bindpw = get_bindpw() changed = False try: ld = ldap.initialize(uri) ld.set_option(ldap.OPT_NETWORK_TIMEOUT, timeout) if uri.startswith("ldap:/"): ld.start_tls_s() ld.bind_s(binddn, bindpw) res = ld.search_s(peopledn, ldap.SCOPE_ONELEVEL, filter, ['uid','sshPublicKey','uidNumber','gidNumber']) try: os.makedirs(keypathprefix, 0701) except: pass if args.verbose: print "Found users: " + ", ".join(sorted(map(lambda x: x[1]['uid'][0], res))) for result in res: dn, entry = result # skip possible system users if 'uidNumber' not in entry or int(entry['uidNumber'][0]) < 500: continue if write_keys(entry['sshPublicKey'], entry['uid'][0], int(entry['uidNumber'][0]), int(entry['gidNumber'][0])): changed = True ld.unbind_s() except Exception, e: print "Error" raise if changed: if args.verbose: print "SSH keys changed" sys.exit(0) if args.verbose: print "No changes in SSH keys" sys.exit(1) # vim:ts=4:sw=4:et:ai:si