Работа с данными Active Directory c помощью сценариев: Часть 2. Совместное использование Bourne Shell и утилиты ldapsearch

Источник: ibm
Рашид Ачилов

Введение

В данной статье мы попробуем решить конкретную задачу: с помощью программы Sarg сформировать так называемый usertab-файл, в котором определяется соответствие учетных записей пользователей, использованных в процессе аутентификации в squid, и их настоящих имен. В результате должен быть получен отчет примерно такого вида:

ivanov-iv		Иванов Иван
petrov-pp		Петров Петр
root			Администратор

Для этого потребуется выполнить действия, практически аналогичные действиям, выполнявшимся в предыдущей статье вручную:

  1. подключение к LDAP с соответствующим фильтром и списком полей;
  2. получение и вывод данных;
  3. преобразование полученной информации и сохранение её в виде файла.

Подготовка к работе

Нас интересуют только поля displayName и sAMAccountName. Сценарий будет называться ldapshtest. Так как это демонстрационный сценарий, а не полная версия (которая должна содержать больше элементов и исполняться в соответствующем окружении), то некоторые фрагменты будут опущены и рассматриваться будут только те фрагменты, которые непосредственно отвечают за выполнение интересующих действий. Данный сценарий будет использовать конфигурационный файл типового формата, показанный в листинге 1, в котором собраны настройки подключения к LDAP.

Листинг 1. Конфигурационный файл сценария ldapshtest
ldap_server=192.168.50.1
ldap_basedn="dc=shelton,dc=int"
ldap_binddn=ldapread@SHELTON.INT
ldap_password="cXdlcnR5YXNkZgo 1"
ldap_common_filter="(&(sAMAccountName=*)(sAMAccountType=805306368)(telephoneNumber=*))"
etcdir=/usr/local/etc
sargdir=sarg2
sargfile=sargusers

В листинге 6 все должно быть понятно кроме пароля. Он записан в таком странном формате по нескольким причинам. Конечно, не следует хранить пароли в открытом виде. Но в то же время трудно найти обратимую функцию, чтобы надежно зашифровать пароль и потом также надежно его расшифровать. Поэтому был выбран компромисс - простейшее преобразование формата, а не шифр. Точнее говоря, это преобразование Base64, у которого отброшены заполнители и вместо них проставлено их количество. Этот пароль можно привести к исходному состоянию с помощью несложной процедуры, приведенной в листинге 2.

Листинг 2. Процедура обратного преобразования пароля
# обратное преобразование строки пароля
# вход:		$1 enconved (строка)    	- пароль из файла конфигурации
# выход:		_passwd			- пароль в текстовом виде
demux_passwd()
{
  local _ideconv _iadd _ifill
  _ifill="="
  _ideconv=$1
  if [ ! $2 -eq 0 ]; then
   _iadd=$2
   while [ $_iadd -gt 0 ]; do 
     _ideconv=$_ideconv$_ifill	
     _iadd=$(($_iadd-1))
    done
  fi
  _passwd=`echo $_ideconv / $util_mmencode -u`
}

Особых пояснений этот код тоже не требует: если второй параметр не нуль, то циклом в конец строки дописывается столько символов заполнителей, сколько было передано во втором параметре, а затем строка декодируется. Внешняя переменная $util_mmencode содержит путь к программе mmencode.

Алгоритм работы создаваемого сценария состоит из нескольких этапов. После предварительной части (загрузка конфигурационного файла, разбор командной строки, поиск необходимых программ) сценарий "расшифровывает" пароль и создает временный каталог для работы. Потом выполняется подключение к LDAP, откуда загружаются учетные записи и имена пользователей, которые сохраняются во временных файлах. Часть кода опущена, ссылку на полную версию можно найти в разделе "Материалы для скачивания". В листинге 3 приведен только код, представляющий интерес.

Листинг 3. Основная процедура сценария
safe_logger "LDAPQuery ver. $revisionNumber started"
# обратное преобразование пароля
demux_passwd $ldap_password
_ldap_pwd=$_passwd
# создать временный каталог, если он еще не создан и перейти в него
make_tempdir
# выполнить поиск учетных записей пользователей в LDAP и сохранить результат
ldap_data_query sAMAccountName $tempdir/users.ldif
# преобразовать данные в читаемый вид
ldap_data_deconvert $tempdir/users.ldif $tempdir/users.list
# выполнить поиск имен пользователей в LDAP и сохранить результат
ldap_data_query displayName $tempdir/names.ldif
# преобразовать данные в читаемый вид
ldap_data_deconvert $tempdir/names.ldif $tempdir/names.list
# сравнить списки и добавить в файл sargusers отсутствующие учетные записи
ldapusers=`cat $tempdir/users.list`
sargusers=`cat $etcdir/$sargdir/$sarglist / awk '{print $1}'`
IFS=$newline
_added=0
# внешний цикл по списку пользователей из LDAP
for _oneldapuser in $ldapusers
 do
   _togo=0	
   # внутренний цикл по списку пользователей Sarg
   for _onesarguser in $sargusers
    do
      # если пользователь найден, то прервать внутренний и внешний циклы
      # и перейти к следующей записи внешнего цикла
      if [ $_oneldapuser = $_onesargluser ]; then
        continue 2
      fi
    done
   # если мы находимся здесь - пользователь LDAP не найден в списке пользователей Sarg.
   # следует записать в список пользователей Sarg 
   # строку с учетной записью из users.list и именем пользователя из names.list
   _oneusername=`cat $tempdir/names.list / tail -n +$_togo / head -n 1`
   echo -e "$_oneldapuser \t$_oneusername\n" >> $etcdir/$sargdir/$sarglist
   _togo=$(($_togo+1))
   _added=$(($_added+1))
 done

Код до начала сравнения списков интуитивно понятен: идут вызовы процедур для получения данных из LDAP и их последующего преобразования. Далее выполняются два вложенных цикла для сравнения двух списков. Первый список - это список полученных из LDAP учетных записей, второй - список учетных записей пользователей, уже присутствующих в файле. Он получается с помощью простейшей команды на awk. Суть его в том, что оба списка считываются в переменные, затем берется один элемент "внешнего" списка и последовательно сравнивается со всеми элементами "внутреннего" списка. Когда находится совпадение, выполняются необходимые действия, потом внешний цикл (в Bourne Shell можно указать оператору break или continue число уровней вложенности, на которые он одновременно воздействует) переходит к следующему элементу.

Во внешнем цикле, идущем по списку учетных записей из LDAP обнуляется счетчик, отмечающий позицию учетной записи в списке. Внутренний цикл идет по списку пользователей, уже присутствующих в файле. Если учетная запись из LDAP уже присутствует в файле, то незачем искать её дальше и можно переходить к следующей. Если она отсутствует, то берется имя пользователя из списка имен (отступив с помощью tail на значение счетчика позиции и взяв оттуда одну строку), после чего формируется строка вывода и дописывается в список пользователей Sarg.

Основной интерес представляют процедуры ldap_data_query() и ldap_data_deconvert(). Первая процедура всего лишь вызывает утилиту ldapsearch, передавая ей все необходимые параметры из конфигурационного файла и список переданных параметров. Единственное, что выполняется перед вызовом ldapsearch - это замена запятых в списке запрашиваемых полей на пробелы. Код процедуры ldap_data_query() приведен в листинге 4.

Листинг 4. Процедура ldap_data_query()
# запросить некоторые данные у LDAP сервера
# вход:	$1 fieldname (строка) 	- имя атрибута(ов), запрашиваемых в AD
#		$2 outfile (строка) - имя файла для сохранения полученных данных
# выход:	none
ldap_data_query()
{
  local _ldapfield _outfile
  _ldapfield=`echo $1 / sed -e "s:,: :g"`
  _outfile=$2
  # выполнить запрос к LDAP и сохранить результат
  $util_ldapsearch -T $tempdir -D $ldap_binddn -w $_ldap_pwd -LLL                    -h $ldap_server -b $ldap_basedn -P 3                    -a always $ldap_common_filter $_ldapfield > $_outfile   # проверить код возврата
  status=$?
  if [ $status -ne 0 ]; then
    safe_logger "Unable to complete LDAP request to get $_ldapfield from AD"
    exit
  fi
}

В листинге 5 приведены вспомогательные процедуры unspace() и make_tempdir().

Листинг 5. Процедуры unspace() и make_tempdir()
# удалить пробелы и строки комментариев из файла
# вход:	$1 source (строка)		- имя исходного файла
#		$2 destination (строка) 	- имя файла результата
# выход:	none
_unspace()
{
  cat $1 / grep -v ^# / sed -e "s: ::g" > $2
}
# создать временный каталог, если он еще не был создан и перейти в него
# вход:		none
# выход:		tempdir (строка) 	- имя временного каталога
make_tempdir()
{
  if [ ${#tempdir} -eq 0 ]; then
    tempdir=`mktemp -d /tmp/adphones.XXXXXX`
  fi
  cd $tempdir
}

Процедура unspace() удаляет из файла, заданного первым параметром, все пробелы и все строки, начинающиеся с символа "#". В итоге на выходе будут только строки вывода ldapsearch. Процедура make_tempdir() создает временный каталог с уникальным именем, если такового еще не было создано. Сделано это для удобства использования данного сценария из других сценариев, чтобы избежать гонок (race condition).

Заключение

В данной статье был рассмотрен основной алгоритм выполнения запросов к LDAP через сценарии оболочки с помощью утилиты ldapsearch. В следующей статье мы рассмотрим дополнительные аспекты обращения к LDAP из сценариев Bourne Shell.

Загрузка

Имя Размер Метод загрузки
ldapshtst.tar.bz2 5KB HTTP 

Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=29552