Выгрузка почтовых адресов из Active Directory с помощью Python

Мы рассмотрим простой скрипт для получение всех возможных почтовых адресов по LDAP из Active Directory. Скрипт можно взять за основу и использовать для других реализаций глобального каталога.
Готовим систему
На компьютере, где будет запускаться скрипт
Сервер Active Directory
Процесс написания скрипта
Подключение по ldap
Получение пользователей AD
Фильтр по почтовому атрибуту
Выгрузка списка адресов электронной почты
Пример готового скрипта
Подготовка системы
Рассмотрим процесс подготовки компьютера, на котором будет запускаться скрипт и сервера Active Directory.
Компьютер
В данном примере скрипт будет запускаться на компьютере с Linux.
1. Для начала установим сам Python и дополнительные компоненты:
а) На системах RPM (Red Hat, CentOS, Fedora):
yum install python3 python3-devel openldap-devel
б) На системах DEB (Debian, Ubuntu):
apt-get install python3 python3-pip libsasl2-dev python-dev libldap2-dev libssl-dev
2. Также установим модуль ldap для python:
pip3 install python-ldap
3. Создадим каталог, в котором будет находиться скрипт:
mkdir /scripts
Создадим сам скрипт:
vi /scripts/get_email_from_ad.py
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
* в данном примере мы создали скрипт get_email_from_ad.py с двумя строчками — шебангом и определением кодировки.
4. Даем разрешение скрипту на запуск:
chmod +x /scripts/get_email_from_ad.py
Active Directory
Для подключения к глобальному каталогу по LDAP нам нужна учетная запись для прохождения авторизации на сервере. Нужна учетная запись с минимальными правами.
Лучше всего для задач скрипта создать отдельного пользователя. В данной инструкции для связывания с Active Directory будет использоваться учетная запись export_emails. Создать ее можно с помощью инструмента Пользователи и компьютеры Active Directory.
Написание скрипта
Чтобы было проще разобраться, начнем разработку по шагам.
1. Подключение по LDAP
Для начала просто подключимся к глобальному каталогу. В наш скрипт добавим:
...
import ldap
try:
lconn = ldap.initialize('ldap://dmosk.local:389')
lconn.protocol_version = ldap.VERSION3
lconn.set_option(ldap.OPT_REFERRALS, 0)
lconn.simple_bind_s('export_emails@dmosk.local', 'export_emails_12345!')
except ldap.SERVER_DOWN:
print("Error connection to AD")
* в данном примере мы подключаемся к серверу dmosk.local (домен должен разрешаться в любой из серверов глобального каталога) по порту 389. Для прохождения аутентификации мы используем логин export_emails и пароль export_emails_12345! (учетная запись, которая заранее нами была создана в Active Directory). Также мы указываем, что используется 3 версия протокола ldap и не должны использоваться рефералы.
Пробуем запустить скрипт:
/scripts/get_email_from_ad.py
Он должен вернуть пустую строку. Если мы получим ошибку, то необходимо проверить связь с сервером и корректность введелнных данных (логина и пароля).
2. Получение списка пользователей
Научимся извлекать пользователей по ldap. Добавим в наш скрипт:
...
base = "DC=dmosk,DC=local"
scope = ldap.SCOPE_SUBTREE
filter = "(&(objectcategory=person))"
attrs = ['displayname','title']
result_set = []
ldap_result_id = lconn.search_ext(base, scope, filter, attrs)
try:
while 1:
result_type, result_data = lconn.result(ldap_result_id, 0)
if (result_data == []):
break
else:
if result_type == ldap.RES_SEARCH_ENTRY:
result_set.append(result_data)
except ldap.SIZELIMIT_EXCEEDED:
print()
print(result_set)
* где:
- base — начальный контейнер, с которого начинаем поиск объектов.
- scope — область поиска. В нашем примере используется SCOPE_SUBTREE, что означает искать также в дочерних объектах.
- filter — задаем фильтр. В текущем примере ищем объекты person (пользователи).
- attrs — атрибуты объектов, которые нас интересуют.
- result_set — массив данных с полученным результатом.
- ldap_result_id — идентификатор поиска объектов в каталоге.
Запускаем наш скрипт:
/scripts/get_email_from_ad.py
Мы должны получить массив данных всех найденных пользователей и их атрибутов.
3. Получение почтовых атрибутов
На этом этапе мы отсеем все объекты, у которых нет адреса электронной почты. В итоге, наш массив данных будет содержать пользователей и атрибуты с адресами электронной почты.
Откроем наш скрипт и заменим строки:
...
filter = "(&(objectcategory=person))"
attrs = ['displayname','title']
...
... на:
filter = "(&(mail=*))"
attrs = ['mail','proxyAddresses']
* в данном примере мы выгрузим все объекты, у которых есть адрес почты (не пустой атрибут mail). Нас будут интересовать 2 атрибута — собственно, mail и proxyAddresses, в котором могут быть перечислены все возможные адреса объекта.
* данный фильтр позволит получить список не только пользователей, но и групп. Возможно, вам это не нужно. Тогда можно изменить наш фильтр, например на "(&(objectcategory=person)(mail=*))" — тогда мы получим только пользователей, у которых есть электронные адреса.
Запускаем наш скрипт:
/scripts/get_email_from_ad.py
Мы должны получить список объектов с адресами электронной почты.
4. Получение списка адресов электронной почты
На конец, вытащим почтовые адреса и составим общий список.
В нашем скрипте удаляем строку:
print(result_set)
Добавляем строки:
all_emails = []
for user in result_set:
proxyAddresses = user[0][1].get('proxyAddresses')
mail = user[0][1].get('mail')
if (proxyAddresses):
for email_b in proxyAddresses:
email = email_b.decode("utf-8")
all_emails.append(email.split(':')[1])
else:
all_emails.append(mail[0].decode("utf-8"))
unique_all_emails = list(set(all_emails))
print(*unique_all_emails, sep = '\n')
* и так, данными действиями мы перебираем полученный массив пользователей и извлекаем все email-адреса. Если у пользователя не пустой атрибут proxyAddresses, значит берем адреса из него, если у пользователя есть только mail, то берем его. В итоге, все данные помещаем в массив all_emails, который в конце преобразовываем для получения уникальных значений (в каталоге могут хранится одинаковые email для разных объектов).
Запускаем наш скрипт:
/scripts/get_email_from_ad.py
Мы должны получить список уникальных email-адресов для всех объектов, которые были найдены в глобальном каталоге.
Пример готового скрипта
Написанный нами скрипт можно представить так:
- #!/usr/bin/env python3
- # -*- encoding: utf-8 -*-
- # Импортируем python-модули
- import ldap
- # Задаем необходимые переменные
- domain = 'dmosk.local'
- base = 'DC=dmosk,DC=local'
- bind_dn = f'export_emails@{domain}'
- bind_dn_password = 'export_emails_12345!'
- scope = ldap.SCOPE_SUBTREE
- filter = "(&(mail=*))"
- attrs = ['mail','proxyAddresses']
- result_set = []
- all_emails = []
- # Подключаемся к глобальному каталогу по LDAP
- try:
- lconn = ldap.initialize(f'ldap://{domain}:389')
- lconn.protocol_version = ldap.VERSION3
- lconn.set_option(ldap.OPT_REFERRALS, 0)
- lconn.simple_bind_s(bind_dn, bind_dn_password)
- except ldap.SERVER_DOWN:
- print("Error connection to AD")
- # Получаем результаты поиска объектов в AD
- ldap_result_id = lconn.search_ext(base, scope, filter, attrs)
- Все результаты поиска объектов заносим в переменную result_set
- try:
- while 1:
- result_type, result_data = lconn.result(ldap_result_id, 0)
- if (result_data == []):
- break
- else:
- if result_type == ldap.RES_SEARCH_ENTRY:
- result_set.append(result_data)
- except ldap.SIZELIMIT_EXCEEDED:
- print()
- # Получаем список email-адресов и заносим его в переменную all_emails
- for user in result_set:
- proxyAddresses = user[0][1].get('proxyAddresses')
- mail = user[0][1].get('mail')
- if (proxyAddresses):
- for email_b in proxyAddresses:
- email = email_b.decode("utf-8")
- all_emails.append(email.split(':')[1])
- else:
- all_emails.append(mail[0].decode("utf-8"))
- # Получаем уникальные значения электронных адресов и заносим их в переменную unique_all_emails
- unique_all_emails = list(set(all_emails))
- # Выводим результат на экран
- print(*unique_all_emails, sep = '\n')