2024-05-14 10:04:35 +00:00
|
|
|
#!/usr/bin/python3
|
|
|
|
|
|
|
|
import collections
|
|
|
|
import os
|
|
|
|
import re
|
|
|
|
|
|
|
|
import ldap3
|
|
|
|
|
2024-07-04 13:31:25 +00:00
|
|
|
{% set cluster_password = lookup('passwordstore', "cluster/"+cluster.name, returnall=true) | from_yaml %}
|
2024-05-14 10:04:35 +00:00
|
|
|
realm = '{{ hostvars[inventory_hostname]["sync-ldap"] }}'
|
|
|
|
ldap_host = '{{ domain }}'
|
2024-07-04 13:31:25 +00:00
|
|
|
ldap_user = '{{ cluster_password.ldap_user }}'
|
|
|
|
ldap_pass = '{{ cluster_password.ldap_pass }}'
|
2024-05-14 10:04:35 +00:00
|
|
|
ldap_base = '{{ domain | split(".") | map("regex_replace", "^", "dc=") | join(",") }}'
|
|
|
|
|
|
|
|
# build LDAP query for users
|
|
|
|
filters = [
|
|
|
|
'(objectClass=user)', # only users
|
|
|
|
'(objectCategory=person)', # that are people
|
|
|
|
'(schacHomeOrganization=*)', # presumably
|
|
|
|
'(!(userAccountControl:1.2.840.113556.1.4.803:=2))' # with enabled accounts
|
|
|
|
]
|
|
|
|
|
|
|
|
# run query
|
|
|
|
server = ldap3.Server(ldap_host, use_ssl=True)
|
|
|
|
ldap = ldap3.Connection(server, ldap_user, ldap_pass, auto_bind=True)
|
|
|
|
ldap.search(ldap_base,
|
|
|
|
f'(&{"".join(filters)})', # conjuction (&(…)(…)(…)) of queries
|
|
|
|
attributes=['userPrincipalName', 'givenName', 'sn', 'mail', 'memberOf'])
|
|
|
|
|
|
|
|
# build user and group dicts
|
|
|
|
all_users = {}
|
|
|
|
all_groups = collections.defaultdict(set)
|
|
|
|
for e in ldap.entries:
|
|
|
|
user = f'{e.userPrincipalName.value}@{realm}'
|
|
|
|
all_users[user] = { k: e[k].value for k in e.entry_attributes }
|
|
|
|
for group in e.memberOf:
|
|
|
|
if m := re.match(r'^CN=([^,]*)', group.replace('\\,', '-')):
|
|
|
|
group = re.sub(r'[^A-Za-z0-9_.-]', '-', m[1])
|
|
|
|
all_groups[group].add(user)
|
|
|
|
|
|
|
|
with open('/etc/pve/user.cfg.new', 'w') as f:
|
|
|
|
# user:{username}@{realm}:1:0:{name}:{surname}:{mail}:AD sync::
|
|
|
|
for user, info in sorted(all_users.items()):
|
|
|
|
print(f'user:{user}:1:0:{info["givenName"]}:{info["sn"]}:{info["mail"]}:AD sync::', file=f)
|
|
|
|
|
|
|
|
# group:{name}:{users}:AD sync:
|
|
|
|
print(f'group:ALL:{",".join(sorted(all_users))}:AD sync:', file=f)
|
|
|
|
for group, users in all_groups.items():
|
|
|
|
print(f'group:{group}:{",".join(sorted(users))}:AD sync:', file=f)
|
|
|
|
|
|
|
|
# keep everything not added by us
|
|
|
|
for line in open('/etc/pve/user.cfg'):
|
|
|
|
if not re.match('^(user|group):.*:AD sync:', line):
|
|
|
|
print(line, end='', file=f)
|
|
|
|
|
|
|
|
os.rename('/etc/pve/user.cfg.new', '/etc/pve/user.cfg')
|