+ setup_group_maps(ldap_conn)
+
+ for host in host_attrs:
+ if not "hostname" in host[1]:
+ continue
+ generate_host(host, global_dir, accounts, host_attrs, ssh_userkeys)
+
+def generate_host(host, global_dir, all_accounts, all_hosts, ssh_userkeys):
+ current_host = host[1]['hostname'][0]
+ OutDir = global_dir + current_host + '/'
+ if not os.path.isdir(OutDir):
+ os.mkdir(OutDir)
+
+ # Get the group list and convert any named groups to numerics
+ GroupList = {}
+ for groupname in AllowedGroupsPreload.strip().split(" "):
+ GroupList[groupname] = True
+ if 'allowedGroups' in host[1]:
+ for groupname in host[1]['allowedGroups']:
+ GroupList[groupname] = True
+ for groupname in GroupList.keys():
+ if groupname in GroupIDMap:
+ GroupList[str(GroupIDMap[groupname])] = True
+
+ ExtraList = {}
+ if 'exportOptions' in host[1]:
+ for extra in host[1]['exportOptions']:
+ ExtraList[extra.upper()] = True
+
+ if GroupList != {}:
+ accounts = filter(lambda x: IsInGroup(x, GroupList, current_host), all_accounts)
+
+ DoLink(global_dir, OutDir, "debianhosts")
+ DoLink(global_dir, OutDir, "ssh_known_hosts")
+ DoLink(global_dir, OutDir, "disabled-accounts")
+
+ sys.stdout.flush()
+ if 'NOPASSWD' in ExtraList:
+ userlist = GenPasswd(accounts, OutDir + "passwd", HomePrefix, "*")
+ else:
+ userlist = GenPasswd(accounts, OutDir + "passwd", HomePrefix, "x")
+ sys.stdout.flush()
+ grouprevmap = GenGroup(accounts, OutDir + "group", current_host)
+ GenShadowSudo(accounts, OutDir + "sudo-passwd", ('UNTRUSTED' in ExtraList) or ('NOPASSWD' in ExtraList), current_host)
+
+ # Now we know who we're allowing on the machine, export
+ # the relevant ssh keys
+ GenSSHtarballs(global_dir, userlist, ssh_userkeys, grouprevmap, os.path.join(OutDir, 'ssh-keys.tar.gz'), current_host)
+
+ if not 'NOPASSWD' in ExtraList:
+ GenShadow(accounts, OutDir + "shadow")
+
+ # Link in global things
+ if not 'NOMARKERS' in ExtraList:
+ DoLink(global_dir, OutDir, "markers")
+ DoLink(global_dir, OutDir, "mail-forward.cdb")
+ DoLink(global_dir, OutDir, "mail-forward.db")
+ DoLink(global_dir, OutDir, "mail-contentinspectionaction.cdb")
+ DoLink(global_dir, OutDir, "mail-contentinspectionaction.db")
+ DoLink(global_dir, OutDir, "mail-disable")
+ DoLink(global_dir, OutDir, "mail-greylist")
+ DoLink(global_dir, OutDir, "mail-callout")
+ DoLink(global_dir, OutDir, "mail-rbl")
+ DoLink(global_dir, OutDir, "mail-rhsbl")
+ DoLink(global_dir, OutDir, "mail-whitelist")
+ DoLink(global_dir, OutDir, "all-accounts.json")
+ GenCDB(accounts, OutDir + "user-forward.cdb", 'emailForward')
+ GenDBM(accounts, OutDir + "user-forward.db", 'emailForward')
+ GenCDB(accounts, OutDir + "batv-tokens.cdb", 'bATVToken')
+ GenDBM(accounts, OutDir + "batv-tokens.db", 'bATVToken')
+ GenCDB(accounts, OutDir + "default-mail-options.cdb", 'mailDefaultOptions')
+ GenDBM(accounts, OutDir + "default-mail-options.db", 'mailDefaultOptions')
+
+ # Compatibility.
+ DoLink(global_dir, OutDir, "forward-alias")
+
+ if 'DNS' in ExtraList:
+ DoLink(global_dir, OutDir, "dns-zone")
+ DoLink(global_dir, OutDir, "dns-sshfp")
+
+ if 'AUTHKEYS' in ExtraList:
+ DoLink(global_dir, OutDir, "authorized_keys")
+
+ if 'BSMTP' in ExtraList:
+ GenBSMTP(accounts, OutDir + "bsmtp", HomePrefix)
+
+ if 'PRIVATE' in ExtraList:
+ DoLink(global_dir, OutDir, "debian-private")
+
+ if 'GITOLITE' in ExtraList:
+ GenSSHGitolite(all_accounts, all_hosts, OutDir + "ssh-gitolite", current_host=current_host)
+ if 'exportOptions' in host[1]:
+ for entry in host[1]['exportOptions']:
+ v = entry.split('=',1)
+ if v[0] != 'GITOLITE' or len(v) != 2: continue
+ options = v[1].split(',')
+ group = options.pop(0);
+ gitolite_accounts = filter(lambda x: IsInGroup(x, [group], current_host), all_accounts)
+ if not 'nohosts' in options:
+ gitolite_hosts = filter(lambda x: GitoliteExportHosts.match(x[1]["hostname"][0]), all_hosts)
+ else:
+ gitolite_hosts = []
+ command = None
+ for opt in options:
+ if opt.startswith('sshcmd='):
+ command = opt.split('=',1)[1]
+ GenSSHGitolite(gitolite_accounts, gitolite_hosts, OutDir + "ssh-gitolite-%s"%(group,), sshcommand=command, current_host=current_host)
+
+ if 'WEB-PASSWORDS' in ExtraList:
+ DoLink(global_dir, OutDir, "web-passwords")
+
+ if 'RTC-PASSWORDS' in ExtraList:
+ DoLink(global_dir, OutDir, "rtc-passwords")
+
+ if 'TOTP' in ExtraList:
+ DoLink(global_dir, OutDir, "users.oath")
+
+ if 'KEYRING' in ExtraList:
+ for k in Keyrings:
+ bn = os.path.basename(k)
+ if os.path.isdir(k):
+ src = os.path.join(global_dir, bn)
+ replaceTree(src, OutDir)
+ else:
+ DoLink(global_dir, OutDir, bn)
+ else:
+ for k in Keyrings:
+ try:
+ bn = os.path.basename(k)
+ target = os.path.join(OutDir, bn)
+ if os.path.isdir(target):
+ safe_rmtree(dst)
+ else:
+ posix.remove(target)
+ except:
+ pass
+ DoLink(global_dir, OutDir, "last_update.trace")
+
+
+def getLastLDAPChangeTime(l):
+ mods = l.search_s('cn=log',
+ ldap.SCOPE_ONELEVEL,
+ '(&(&(!(reqMod=activity-from*))(!(reqMod=activity-pgp*)))(|(reqType=add)(reqType=delete)(reqType=modify)(reqType=modrdn)))',
+ ['reqEnd'])
+
+ last = 0
+
+ # Sort the list by reqEnd
+ sorted_mods = sorted(mods, key=lambda mod: mod[1]['reqEnd'][0].split('.')[0])
+ # Take the last element in the array
+ last = sorted_mods[-1][1]['reqEnd'][0].split('.')[0]
+
+ return last
+
+def getLastKeyringChangeTime():
+ krmod = 0
+ for k in Keyrings:
+ mt = os.path.getmtime(k)
+ if mt > krmod:
+ krmod = mt
+
+ return int(krmod)
+
+def getLastBuildTime(gdir):
+ cache_last_ldap_mod = 0
+ cache_last_unix_mod = 0
+ cache_last_run = 0
+
+ try:
+ fd = open(os.path.join(gdir, "last_update.trace"), "r")
+ cache_last_mod=fd.read().split()
+ try:
+ cache_last_ldap_mod = cache_last_mod[0]
+ cache_last_unix_mod = int(cache_last_mod[1])
+ cache_last_run = int(cache_last_mod[2])
+ except IndexError, ValueError:
+ pass
+ fd.close()
+ except IOError, e:
+ if e.errno == errno.ENOENT:
+ pass
+ else:
+ raise e
+
+ return (cache_last_ldap_mod, cache_last_unix_mod, cache_last_run)
+
+def mq_notify(options, message):
+ options.section = 'dsa-udgenerate'
+ options.config = '/etc/dsa/pubsub.conf'
+
+ config = Config(options)
+ conf = {
+ 'rabbit_userid': config.username,
+ 'rabbit_password': config.password,
+ 'rabbit_virtual_host': config.vhost,
+ 'rabbit_hosts': ['pubsub02.debian.org', 'pubsub01.debian.org'],
+ 'use_ssl': False
+ }
+
+ msg = {
+ 'message': message,
+ 'timestamp': int(time.time())
+ }
+ conn = None
+ try:
+ conn = Connection(conf=conf)
+ conn.topic_send(config.topic,
+ json.dumps(msg),
+ exchange_name=config.exchange,
+ timeout=5)
+ finally:
+ if conn:
+ conn.close()
+
+def ud_generate():
+ parser = optparse.OptionParser()
+ parser.add_option("-g", "--generatedir", dest="generatedir", metavar="DIR",
+ help="Output directory.")
+ parser.add_option("-f", "--force", dest="force", action="store_true",
+ help="Force generation, even if no update to LDAP has happened.")
+
+ (options, args) = parser.parse_args()
+ if len(args) > 0:
+ parser.print_help()
+ sys.exit(1)
+
+ if options.generatedir is not None:
+ generate_dir = os.environ['UD_GENERATEDIR']
+ elif 'UD_GENERATEDIR' in os.environ:
+ generate_dir = os.environ['UD_GENERATEDIR']
+ else:
+ generate_dir = GenerateDir
+
+
+ lockf = os.path.join(generate_dir, 'ud-generate.lock')
+ lock = get_lock( lockf )
+ if lock is None:
+ sys.stderr.write("Could not acquire lock %s.\n"%(lockf))
+ sys.exit(1)
+
+ l = make_ldap_conn()
+
+ time_started = int(time.time())
+ ldap_last_mod = getLastLDAPChangeTime(l)
+ unix_last_mod = getLastKeyringChangeTime()
+ cache_last_ldap_mod, cache_last_unix_mod, last_run = getLastBuildTime(generate_dir)
+
+ need_update = (ldap_last_mod > cache_last_ldap_mod) or (unix_last_mod > cache_last_unix_mod) or (time_started - last_run > MAX_UD_AGE)
+
+ fd = open(os.path.join(generate_dir, "last_update.trace"), "w")
+ if need_update or options.force:
+ msg = 'Update forced' if options.force else 'Update needed'
+ generate_all(generate_dir, l)
+ if use_mq:
+ mq_notify(options, msg)
+ last_run = int(time.time())
+ fd.write("%s\n%s\n%s\n" % (ldap_last_mod, unix_last_mod, last_run))
+ fd.close()
+ sys.exit(0)
+
+
+if __name__ == "__main__":
+ if 'UD_PROFILE' in os.environ:
+ import cProfile
+ import pstats
+ cProfile.run('ud_generate()', "udg_prof")
+ p = pstats.Stats('udg_prof')
+ ##p.sort_stats('time').print_stats()
+ p.sort_stats('cumulative').print_stats()
+ else:
+ ud_generate()
+
+# vim:set et:
+# vim:set ts=3:
+# vim:set shiftwidth=3: