Связка LightSquid + Active Directory

    Использую на прокси-сервере squid анализатор логов LightSquid, и вот, однажды захотелось чтобы статистика была в виде компьютер – реальное имя, а так как править конфигурационный файл для 100+ юзеров показалось делом рутинным, ну и при изменении имени или добавлении новых пользователей лазить опять в конфиг не хотелось, решил брать автоматом имена из Active Directory. За деталями прошу под кат.

    У меня используется Active Directory, но с минимальными изменениями скрипт должен работать и с обычной LDAP.
    Описывать установку LightSquid не буду, она довольно проста, гугл вам в помощь. Для преобразования IP в hostname будем использовать DNS, описывается ето в конфигурационном файле lightsquid.cfg строчкой:

    $ip2name=«dns»;

    Парсер перечитывает файлы realname.cfg и group.cfg и берет оттуда реальные имена юзеров, привязаных к хостам. Формат файлов такой:

    realname.cfg:
    «hostname» «real name»

    group.cfg:
    «hostname» «group number» «group name»

    Что нам надо?


    Надо взять с АД хосты, имена юзеров привязаных к хостам, группы в каких содержатся юзеры; потом пишем всё ето в конфигурационные файлы в соответственном формате.

    Вот собственно скрипт, который ето делает:

    #!/usr/local/bin/perl
    #
    # ldap2lightsquid (c) Roman Melko <romanmelko@gmail.com>
    # Description:  Synchronize users and computers of LightSquid with LDAP server
    # Requirements: Should run periodically
    # Version:      2012030601
    # License:      BSD
    #
    
    use strict;
    use Net::LDAP;
    
    my $domain = "example.ua"; # Domain is supposed to have 2 levels
    my @parts = split(/\./,$domain);
    my $domain0 = $parts[1];
    my $domain1 = $parts[0];
    my $user = "<username>"; # LDAP user
    my $password = "<password>"; # LDAP password
    my $cfgpath = "/usr/local/etc/lightsquid/"; # depends on OS
    my $realname = "$cfgpath/realname.cfg";
    my $group = "$cfgpath/group.cfg";
    # departments OU
    my @units = (
            "MGT",
            "OPR",
            "PRO",
            "Sales
    ");
    # computers OU
    my @pcunits = (
            "Developer servers",
            "Servers",
            "Workstation OPR",
            "Workstations PRO",
            "Workstations Sales",
            "Workstations Telemarketing"
    );
    my @dep = ("no in group");
    my $ldap = Net::LDAP->new("$domain") or die "$0";
    $ldap->bind("CN=$user,DC=$domain1,DC=$domain0", password=>$password);
    my $base_path = "OU=<some path>,OU=<some path>,DC=$domain1,DC=$domain0"; # base LDAP path, change to yours
    my $num = @units;
    my $pcnum = @pcunits;
    my $attrs = "sn, givenname, department, samaccountname";
    my $filter = "(objectcategory=CN=Person,CN=Schema,CN=Configuration,DC=$domain1,DC=$domain0)";
    my $pcattrs = "cn, managedBy";
    my $pcfilter = "(objectcategory=CN=Computer,CN=Schema,CN=Configuration,DC=$domain1,DC=$domain0)";
    my $count;
    my $results;
    my %department_id = ();
    my %department_name = ();
    
    sub get_host_info {
            for (my $i=0; $i<$count; $i++) {
                    my $entry = $results->entry($i);
                    my $hostname = join(".",lc($entry->get_value('cn')),$domain);
                    my @tmp_array = split(/,/,$entry->get_value('managedBy'));
                    @tmp_array = split(/=/,$tmp_array[0]);
                    my $fullname = $tmp_array[1];
                    if(!$fullname) {
                            next;
                    }
                    print(REALNAME "$hostname\t$fullname\n");
                    print(GROUP "$hostname\t$department_id{$fullname}\t$department_name{$fullname}\n");
            }
    }
    
    sub get_user_info {
            for (my $i=0; $i<$count; $i++) {
                    my $entry = $results->entry($i);
                    my $depnum = @dep;
                    my $depid = $depnum;
                    $depid++;
                    foreach $depnum (0 .. @dep) {
                            if ($entry->get_value('department') eq $dep[$depnum]) {
                                    $depid = $depnum;
                            }
                    }
                    if ($depid > $depnum) {
                            $dep[$depid] = $entry->get_value('department');
                    }
                    if (length $depid < 2) {
                            $depid = "0".$depid;
                    }
                    my $name = $entry->get_value('givenname');
                    my $surname = $entry->get_value('sn');
                    $name =~ s/^\s+//;
                    $name =~ s/\s+$//;
                    $surname =~ s/^\s+//;
                    $surname =~ s/\s+$//;
                    my $fullname = join(" ",$name,$surname);
                    $department_id{$fullname} = $depid;
                    $department_name{$fullname} = $entry->get_value('department');
            }
    }
    
    open (REALNAME, ">", $realname) or die $!;
    open (GROUP, ">", $group) or die $!;
    
    # Getting real names and departments
    
    foreach $num (0 .. @units) {
            my $base = 'OU='.$units[$num].','.$base_path;
            $results = $ldap->search(base=>$base,filter=>$filter,attrs=>$attrs);
            $count = $results->count;
            if ($count > 0) {
                    get_user_info();
            }
    }
    
    # Getting pc names and owners, writing results to conf files
    
    foreach $pcnum (0 .. @pcunits) {
            my $base = 'OU='.$pcunits[$pcnum].',OU=Resources,'.$base_path;
            $results = $ldap->search(base=>$base,filter=>$pcfilter,attrs=>$pcattrs);
            $count = $results->count;
            if ($count > 0) {
                    get_host_info();
            }
    }
    
    # Closing connection to LDAP and files
    
    $ldap->unbind;
    close (REALNAME);
    close (GROUP);
    
    exit 0
    


    Важные моменты:
    — в каждом хосте в АД должно быть заполнено поле managedby, так привязывается хост к юзеру;
    — в каждого юзера в АД должно быть заполнено поле department, так привязывается юзер к группе (у меня пока что так, до реальных групп руки не дошли, так как у меня один юзер может принадлежать к многим группам с похожыми именами);
    — units и pcunits вы должны заполнить сами своими данными;
    — скрипт должен выполняться периодически, например в cron;
    — юзер, под каким осуществляется доступ скрипта в AD, должен быть в корне AD, по другому меня не пускало;
    — для того чтобы заставить работать скрипт с стандартной базой LDAP курите маны, там есть различия с АД.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 14

      0
      Я так и не понял, у вас какая авторизация на squid?
        0
        На squid нет авторизации вообще
        0
        Как понимаю скрипт надо пускать в кроне, перед парсером…

        но в общем есть и другой путь, более нативный так сказать
        брать имена юзеров из AD прямо в момент появления этого юзера…
          0
          Упс, поправил, хотя это указано вначале скрипта: "# Requirements: Should run periodically". Под нативным методом вы имеете ввиду батник в профайле юзера в АД?
            0
            делаем свой ip2name, который при необходимости
            лезет в AD и берет от туда нужные данные, (при необходимости кешируем)
            и пишет их к «себе в массив»

            в Lightsquid.cfg есть две фукции
            CreateRealnameFile
            CreateGroupFile

            переопределяем их, чтоб «свои массивы» писали в файл

              0
              всеравно ж ето будет вызываться парсером, а он по крону
                0
                зато скрипт будет только 1
                эт раз
                бедт дергаться инфо не для ВСЕХ людей а только для нужных
                соотв и лишнего в файлы не пишем :)

                хотя эт просто коменты, идея и была — чтоб каждый мог свое прикрутить, как ему удобно ;)
              0
              пускать по логике надо перед каждым пуском lightparser или как минимум раз в день
              т.к. .realname уникален для каждого дня
              и если юзер перехал на другой комп, то желательно обновить данные про это :)
                0
                да, у меня оно пускается чуть ли не каждые 10 мин, так как в сети есть DHCP и ресурсов для етого много ненадо
            0
            Просмотрел статью и не понял — предполагается, что один юзер=один комп? А что делать при нестандартных конфигурациях с разделяемыми учётками или мобильными пользователями?
              0
              При правильной конфигурации в АД (поля managedBy в хостах) — один юзер=много компов, тоесть в компе нужно прописывать его владельца. А для не-доменных компов просто не будет отображаться имя. Можно добавить блок в скрипте который будет оставлять статически прописанные записи в конф файлах в таком случае
                0
                хоть 150 человек за компом, просто правильный ip2name сделать

                Можно вытягивать данные о залогиненом сейчас юзере и брать как имя пользователя его
                ip2name=smb кажись

                был вариант для системы биллинга, там ip2name лазил в базу чтоб узнать кто в указанное время использовал это ip…

                собственно идея ip2name как раз в том и состоит чтоб по записи лога однозначно определить юзера по которому вести учет
                  0
                  Немного не пойму, — ip2name юзается во врема парсинга, и хоть 10 юзеров использовало определенный комп, а в отчете будет видно только последнего
                    0
                    это зависит исключительно от ip2name ;)

                    если ip2name вернет одного юзера — будет один
                    если 10 разных — будет 10

                    сбственно задача этой функции и есть по куче параметров определить учетное имя пользователя

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое