Левинца Егор
Обсуждение статьи на форуме проекта Pro-LDAP.ru.
В данном разделе мы рассмотрим аутентификацию с помощью ориентированных на LDAP хелперов squid: squid_ldap_auth и digest_ldap_auth, представляющих, соответственно, Basic и Digest-аутентификации HTTP, а также коротко остановимся на других схемах аутентификации squid.
При Basic-аутентификации клиент передаёт серверу предоставленные пользователем имя и пароль в открытом виде, на основании этих сведений сервер пытается установить подлинность пользователя, пытающегося пройти аутентификацию. Очевидный минус такого подхода — передача конфиденциальных сведений (пароля) в открытом виде, при незащищённом соединении он становится лёгкой добычей злоумышленников. К плюсам можно отнести то, что, получив пароль в открытом виде, сервер может организовать какую угодно проверку подлинности, используя любую базу хранения паролей, в которой пароли представлены в каком угодно (вычисляемом) виде.
При Digest-аутентификации предоставленный пользователем пароль на клиенте соединяется с другимим данными, к этому значению применяется односторонняя хэш-функция MD5 и полученное значение вместе с именем пользователя передаётся на сервер, который на основании этих сведений пытается установить подлинность пользователя. Если злоумышленник перехватит захэшированную строку, получить из неё пароль в открытом виде не получится (в этом и смысл односторонней хэш-функции).
Смысл работы хелпера basic_ldap_auth заключается в выполнении операции простого подсоединения (simple bind) к каталогу LDAP. По сути, операция простого подсоединения представляет собой прохождение аутентификации в каталоге от имени той записи каталога, которая описывает пользователя, с предоставлением пароля в открытом виде. Всё предельно просто: если подсоединение к каталогу прошло успешно, значит и аутентификация в squid считается успешно выполненной.
Чтобы выполнить простое подсоединение к каталогу, нужно знать DN записи, от имени которой будет выполняться подсоединение, и пароль. Пароль, как нам известно, предоставляется HTTP-клиентом в открытом виде, так что с этим вопросов нет. А вот DN пользовательской записи должен быть каким-то образом сопоставлен с предоставленным HTTP-клиентом именем пользователя. Хелпер basic_ldap_auth может решать эту задачу двумя разными способами:
Составление DN из значений аргументов комадной строки и переданного имени пользователя. Проще всего показать это на примере:
# basic_ldap_auth -b 'ou=Managers,dc=mycompany,dc=ru' -u 'uid' -d vasily vasilyPassword<Enter> attempting to authenticate user 'uid=vasily,ou=Managers,dc=mycompany,dc=ru' OK anton antonPassword<Enter> attempting to authenticate user 'uid=anton,ou=Managers,dc=mycompany,dc=ru' ERR Success
Примечание: Здесь и далее в примерах жирным шрифтом выделен текст, вводимый пользователем, а в угловых скобках указаны клавиши, на которые необходимо нажать в процессе или после ввода.
Примечание: Указываемый во всех примерах вызова хелперов из командной строки аргумент -d означает, что хелперы запускаются в режиме отладки, когда на стандартный вывод посылаются диагностические и служебные сообщения. При тестировании это очень удобно, но когда вызов хелпера прописывается в squid.conf, указывать его не следует.
Как видите, DN записи пользователя составляется из имени атрибута, переданного в качестве значения аргумента -u, имени пользователя и DN базовой записи, переданного в качестве значения аргумента -b. Затем происходит попытка аутентификации в каталоге с использованием полученного DN и переданного пароля.
Это самый простой способ использования хелпера basic_ldap_auth, при котором происходит всего одна операция с каталогом: простое подсоединение. Тем самым уменьшаются накладные расходы и время отклика хелпера, что немаловажно в нагруженных системах. Конечно же, такая простота возможна только при определённых условиях:
Более комплексный вариант определения DN записи можно сформулировать так: "Поиск в каталоге записей, удовлетворяющих заданным критериям. Затем DN первой из найденных записей используется для простого подсоединения." Таким образом, взаимодействие с каталогом происходит как минимум в два этапа: анонимный поиск записей и попытка выполнить подсоединение. Если же анонимный поиск в каталоге запрещён (как, например, в Microsoft AD), то этапов уже три: подсоединение к каталогу от имени записи с правами поиска (аргументы -D и -w (-W)), поиск записи по указанным критериям и попытка повторного подсоединения от имени найденной записи.
Ключевым моментом для запуска хелпера в этом режиме является наличие аргумента -f, значением которого является поисковый фильтр LDAP. В этом случае значение аргумента -b рассматривается как база поиска, а диапазон поиска, при необходимости, можно задать аргументом -s (по умолчанию — sub).
Итак, какие же реальные преимущества мы можем получить взамен дополнительной нагрузки на каталог LDAP? Попробуем разобрать это на примерах.
Задача: провести аутентификацию любых пользователей, записи которых имеются в каталоге. Решение:
# basic_ldap_auth -b 'dc=mycompany,dc=ru' -f '(uid=%s)' -d vasily vasilyPassword<Enter> user filter '(uid=vasily)', searchbase 'dc=mycompany,dc=ru' attempting to authenticate user 'uid=vasily,ou=Managers,dc=mycompany,dc=ru' OK anton antonPassword<Enter> user filter '(uid=anton)', searchbase 'dc=mycompany,dc=ru' attempting to authenticate user 'uid=anton,ou=People,dc=mycompany,dc=ru' OK petr petrPassword<Enter> user filter '(uid=petr)', searchbase 'dc=mycompany,dc=ru' Ldap search returned nothing ERR Success
На первом этапе осуществляется поиск записей, соответствующих указанным критериям поиска. В данном случае базой поиска (-b) будет базовая запись всего каталога dc=mycompany,dc=ru, диапазон поиска не указан, поэтому берётся значение по умолчанию sub. Таким образом, при поиске будет осуществляться обход всего дерева каталога, в том числе и обоих интересующих нас веток People и Managers. Чтобы отобрать только записи пользователей, мы указываем фильтр -f '(uid=%s)'. Шаблон %s в этой конструкции заменяется на вводимое значение имени пользователя. Как видно в примере, в первом случае в результате был сформирован фильтр (uid=vasily), во втором — (uid=anton), в третьем — (uid=petr), соответственно были найдены записи пользователей из обоих веток uid=vasily,ou=Managers,dc=mycompany,dc=ru и uid=anton,ou=People,dc=mycompany,dc=ru, а в третьем случае поиск не дал результатов, хотя и прошёл успешно, о чём свидетельствует забавный вывод ERR Success.
Если запись была найдена, осуществляется попытка простого подсоединения к каталогу от имени этой записи с предоставленным паролем. Факт удачного подсоединения к каталогу означает, что basic-аутентификация прошла успешно (OK).
Таким образом, поиск в каталоге даёт нам возможность находить записи любых пользователей независимо от того, в какой ветке (подветке) они хранятся.
Часто возникает задача предоставить доступ в Интернет не всем пользователям подряд, а лишь определённому подмножеству. Выделить такое подмножество можно, задав определённое значение какого-либо атрибута, к примеру, ou: Inet Users в записях тех пользователей, которым разрешён доступ в Интернет. В этом случае можно было бы использовать фильтр -f '(&(uid=%s)(ou=Inet Users))'. В нашем тестовом каталоге таких атрибутов нет, но мы можем смоделировать ситуацию, когда используя определённые группы и наложение обратного членства в них (memberof), мы получим операционные (скрытые) атрибуты с указанием на членство в группе. Эти атрибуты мы можем использовать при построении фильтра.
Задача: провести аутентификацию только тех пользователей, которые входят в группу cn=InetUsers,ou=Groups,dc=mycompany,dc=ru, членам которой разрешён доступ в Интернет.
Для начала нам необходимо создать группу, причём такую, чтобы можно было использовать её для наложения memberof (подробнее о группах речь пойдёт далее). Для определения группы будем использовать объектный класс groupOfUniqueNames:
dn: cn=InetUsers,ou=Groups,dc=mycompany,dc=ru objectClass: groupOfUniqueNames cn: InetUsers uniqueMember: uid=vasily,ou=Managers,dc=mycompany,dc=ru uniqueMember: uid=vitaly,ou=People,dc=mycompany,dc=ru
Членами нашей группы (значения атрибута uniqueMember) будут пользователи из разных веток, таким образом мы обозначаем, что расположение записей пользователей в каталоге может быть произвольным. Добавим нашу группу с помощью ldapadd и настроим для нашего DIT наложение memberof на использование объектного класса groupOfUniqueNames и атрибута uniqueMember. Проверим, что получилось:
# ldapsearch -x -LLL -b 'dc=mycompany,dc=ru' 'memberof=cn=InetUsers,ou=Groups,dc=mycompany,dc=ru' memberOf dn: uid=vitaly,ou=People,dc=mycompany,dc=ru memberOf: cn=InetUsers,ou=Groups,dc=mycompany,dc=ru dn: uid=vasily,ou=Managers,dc=mycompany,dc=ru memberOf: cn=InetUsers,ou=Groups,dc=mycompany,dc=ru
Получив такой критерий отбора записей, мы можем построить следующее решение для хэлпера basic_ldap_auth:
# basic_ldap_auth -b 'dc=mycompany,dc=ru' -f '(&(uid=%s)(memberOf=cn=InetUsers,ou=Groups,dc=mycompany,dc=ru))' -d vasily vasilyPassword<Enter> user filter '(&(uid=vasily)(memberOf=cn=InetUsers,ou=Groups,dc=mycompany,dc=ru))', searchbase 'dc=mycompany,dc=ru' attempting to authenticate user 'uid=vasily,ou=Managers,dc=mycompany,dc=ru' OK vitaly vitalyPassword<Enter> user filter '(&(uid=vitaly)(memberOf=cn=InetUsers,ou=Groups,dc=mycompany,dc=ru))', searchbase 'dc=mycompany,dc=ru' attempting to authenticate user 'uid=vitaly,ou=People,dc=mycompany,dc=ru' OK anton antonPassword<Enter> user filter '(&(uid=anton)(memberOf=cn=InetUsers,ou=Groups,dc=mycompany,dc=ru))', searchbase 'dc=mycompany,dc=ru' Ldap search returned nothing ERR Success
В данном случае мы ищем по всему каталогу (база поиска -b 'dc=mycompany,dc=ru', диапазон по умолчанию — sub), вторым критерием в фильтре является соответствие значения атрибута memberOf DN заданной группы. Пользователи vasily и vitaly, являющиеся членами нашей группы, успешно прошли аутентификацию, а пользователь anton — нет. Это чем-то схоже с отбором по группам, но есть существенное отличие: в фильтре можно задавать только конкретные значения групп или других атрибутов (одно или несколько), получается пресловутое "одним — всё, а другим — ничего". Если же нам нужна ситуация, когда "одним — одно, вторым — другое, третьим — третье, а остальным — ничего", то без использования хелпера ext_ldap_group_acl не обойтись.
Бывает так, что значение атрибута, образующего RDN записи пользователя, не соответствует имени учётной записи пользователя. Первое, что приходит в голову, — каталог Microsoft AD, где RDN образовано атрибутом cn (например, cn=Vitaly Fridzon), а в качестве логина чаще всего используется значение атрибута sAMAccountName. Применительно к нашему экспериментальному каталогу можно поставить задачу так: предоставлять доступ в Интернет только руководителям, аутентификацию проходить по фамилии. Решение:
# basic_ldap_auth -b 'ou=Managers,dc=mycompany,dc=ru' -s one -f '(sn=%s)' -d karasev vasilyPassword<Enter> user filter '(sn=karasev)', searchbase 'ou=Managers,dc=mycompany,dc=ru' attempting to authenticate user 'uid=vasily,ou=Managers,dc=mycompany,dc=ru' OK
Ограничив область (-b 'ou=Managers,dc=mycompany,dc=ru') и диапазон (-s one) поиска, мы, во-первых, получили точное соответствие условиям нашей задачи, а во-вторых, снизили нагрузку на сервер. Фильтр -f '(sn=%s)' позволяет нам отобрать записи по значению атрибута sn, не фигурирующего в RDN. В нашем случае в результате преобразования шаблона мы получили фильтр (sn=karasev), по которому была найдена запись uid=vasily,ou=Managers,dc=mycompany,dc=ru и от имени этой записи было выполнено успешное подсоединение к каталогу (OK).
При построении фильтра в хелпере basic_ldap_auth можно использовать шаблон %s несколько раз (до 16-ти, согласно документации). Это может пригодиться, если записи у Вас в каталоге устроены по разному и логин хранится в разных атрибутах. В нашем каталоге это не так, но мы можем поставить иную задачу: аутентифицировать пользователей по их логину, либо по фамилии. Решение:
# basic_ldap_auth -b 'dc=mycompany,dc=ru' -f '(|(uid=%s)(sn=%s))' -d anton antonPassword<Enter> user filter '(|(uid=anton)(sn=anton))', searchbase 'dc=mycompany,dc=ru' attempting to authenticate user 'uid=anton,ou=People,dc=mycompany,dc=ru' OK ponkrashov antonPassword<Enter> user filter '(|(uid=ponkrashov)(sn=ponkrashov))', searchbase 'dc=mycompany,dc=ru' attempting to authenticate user 'uid=anton,ou=People,dc=mycompany,dc=ru' OK
Поиск происходит по всему каталогу (база поиска -b 'dc=mycompany,dc=ru', диапазон по умолчанию — sub). Фильтр -f '(|(uid=%s)(sn=%s))' в первом случае преобразуется в (|(uid=anton)(sn=anton)), во втором — в (|(uid=ponkrashov)(sn=ponkrashov)). В результате обоих поисков найдена одна и та же запись uid=anton,ou=People,dc=mycompany,dc=ru, от имени которой и происходит подсоединение к каталогу.
# basic_ldap_auth -b 'dc=mycompany,dc=ru' -f '(&(uid=%s)(uid=v*))' -d vasily vasilyPassword<Enter> user filter '(&(uid=vasily)(uid=v*))', searchbase 'dc=mycompany,dc=ru' attempting to authenticate user 'uid=vasily,ou=Managers,dc=mycompany,dc=ru' OK vitaly vitalyPassword<Enter> user filter '(&(uid=vitaly)(uid=v*))', searchbase 'dc=mycompany,dc=ru' attempting to authenticate user 'uid=vitaly,ou=People,dc=mycompany,dc=ru' OK anton antonPassword<Enter> user filter '(&(uid=anton)(uid=v*))', searchbase 'dc=mycompany,dc=ru' Ldap search returned nothing ERR Success
Ещё один пример поиска с дополнительным критерием. На этот раз в результат поиска попадают только пользователи, значение атрибута uid которых начинается с буквы v.
Итак, с тем, как работает хелпер, мы более-менее разобрались. Дело за малым — внедрить его как средство аутентификации в работу сервера squid. Для этого требуется:
Нагляднее рассмотреть это на примере. Для начала покажем возможные параметры вызова хелпера и разные варианты определения ACL, затем рассмотрим их подробнее:
### 1. Настройка Basic-аутентификации с помощью хелпера basic_ldap_auth## Вызов хелпераauth_param basic program /path/to/basic_ldap_auth -b "dc=mycompany,dc=ru" -f "(uid=%s)" -H ldap://127.0.0.1:389## Область действия аутентификацииauth_param basic realm Squid Basic Auth## Количество параллельно запущенных процессов хелпераauth_param basic children 5## "Время жизни" пройденной аутентификацииauth_param basic credentialsttl 2 hours## Ожидание символов UTF-8 в имени пользователя или пароле# auth_param basic utf8 on## Учёт регистра символов в имени пользователя# auth_param basic casesensitive on### 2. ACL аутентификации## ACL возвращает TRUE для всех пользователей, прошедших аутентификациюacl acl_all_authed_users proxy_auth REQUIRED## Вариант: ACL возвращает TRUE для конкретных пользователей, прошедших аутентификацию# acl acl_some_authed_users proxy_auth vasily vitaly## Вариант: ACL возвращает TRUE для пользователей, прошедших аутентификацию, имя которых соответствует регулярному выражению# acl acl_regex_authed_users proxy_auth_regex -i ^a.*$### 3. Правила доступа## Разрешён доступ всем, кто прошёл проверкуhttp_access allow acl_all_authed_users## Вариант: Разрешён доступ конкретным пользователям, прошедшим проверку# http_access allow acl_some_authed_users## Вариант: Разрешён доступ пользователям, прошедшим проверку, имя которых соответствует регулярному выражению# http_access allow acl_regex_authed_users## Остальным доступ запрещёнhttp_access deny all
В строках 1-13 определяются настройки Basic-аутентификации. Все директивы настройки Basic-аутентификации начинаются с ключевых слов auth_param basic. Строка 3 интересует нас больше всего, поскольку в ней описывается непосредственно вызов хелпера, на что указывает ключевое слово program. Для этой конфигурации мы используем вызов хелпера с "классическим" вариантом поиска записей в каталоге (смотрите пример 2.1.2 выше), Вы же должны указать параметры, соответствующие Вашей задаче. Аргумент -d мы не указываем, поскольку он нужен для вывода отладочной информации и в рабочей системе будет только мешать. Для примера мы указали аргумент -H, значением которого является URL размещения каталога; если, как в нашем случае, каталог обслуживается на том же сервере и на стандартном порту, данный аргумент можно не указывать. Возможно, Вас заинтересуют и другие аргументы подключения к LDAP-серверу (-P, -O, -Z), их назначение смотрите в man-странице basic_ldap_auth. Ещё раз необходимо обратить внимание на то, что значения аргументов хелпера обрамляются двойными кавычками (с одинарными хелпер просто не будет вызываться).
Все остальные директивы auth_param basic являются опциональными. В строке 5 указывается область действия аутентификации (ключевое слово realm). Для Basic-аутентификации принципиального значения этот параметр не несёт, он выводится как подсказка в диалоге запроса аутентификации. Значение по умолчанию — "Squid proxy-caching web server". В строке 7 указывается, сколько одновременных процессов хелпера будет порождать squid (ключевое слово children). Тут, как говорится, чем больше, тем лучше, главное, чтобы памяти хватало. Значение по умолчанию — 5. В строке 9 указывается "время жизни" результатов аутентификации, то есть как часто повторно вызывается хелпер для проверки аутентификации пользователя с тем же именем. Значения по умолчанию тут нет, многое зависит от того, часто ли у Вас меняются пароли.
Существует ещё несколько директив Basic-аутентификации. В нашем случае они не несут смысловой нагрузки, поэтому мы оставили их закомментированными. В строке 11 указана директива с ключевым словом utf8. Имеет смысл включить её, если в имени пользователя или пароле имеются символы, не входящие в набор iso-latin-1, например, кириллические (в нашем случае это не так). Значение по умолчанию — "off". Наконец, в строке 13 указана директива с ключевым словом casesensitive, то есть чувствительность имени пользователя к регистру символов. В LDAP практически все (за редким исключением) строки к регистру нечувствительны, соответственно, включать этот параметр нет смысла — он не будет работать в любом случае. Значение по умолчанию — "off".
Далее, в строках 15-21, определяются списки контроля доступа (ACL) аутентификации. Как и любые директивы списков контроля доступа, они начинаются с ключевого слова acl, затем следует имя списка и его тип, после чего указываются дополнительные параметры для конкретного типа ACL. Мы рассмотрим 3 варианта ACL аутентификации. Первый из них (строка 17) сработает при успешном прохождении аутентификации любым пользователем. Мы назвали его acl_all_authed_users. Этот вариант используется чаще всего, особенно когда дальнейшее разделение пользователей по группам происходит с помощью каких-либо хелперов внешних ACL.
Другие два варианта (закомментированы в строках 19 и 21) позволяют произвести примитивное разбиение пользователей по группам уже на этапе аутентификации без использования дополнительных ACL. В первом из них (мы назвали его acl_some_authed_users) после ключевого слова proxy_auth, указывающего на тип ACL, явно перечисляются те пользователи, при успешной аутентификации которых этот ACL сработает. Такой вариант разбиения по группам приемлем лишь в системах с небольшим количеством пользователей, которые редко меняются. В последнем из рассматриваемых вариантов (acl_regex_authed_users) после ключевого слова proxy_auth_regex, указывающего на тип ACL, и опции -i приводится регулярное выражение. Этот ACL сработает, если имя пользователя, успешно прошедшего аутентификацию, соответствует указанному в нём регулярному выражению (в нашем случае имя пользователя начинается с буквы a). Как видите, такой подход может быть приемлем только в системах со специфичными условиями. Выбирайте тот вариант, который Вам больше подходит.
Опция -i в последнем ACL приводится для примера и означает, что имя пользователя будет рассматриваться как строка, нечувствительная к регистру символов. Эту опцию можно использовать в любом ACL (не только аутентификации).
Наконец, в строках 23-31 с помощью директив http_access определяются правила доступа на основании ранее определённых ACL. Тут всё просто: срабатывает указанный по имени ACL (основной в строке 25 и два других варианта в закомментированных строках 27 и 29) — срабатывает и правило, и доступ в Интернет разрешён (ключевое слово allow), нет — срабатывает последнее правило в строке 31, запрещающее доступ в Интернет (ключевое слово deny и ACL по умолчанию all). Другими словами, аутентификация пройдена — есть доступ, нет — нет.
Мы весьма бегло прошлись по директивам файла squid.conf. Перед составлением рабочей конфигурации не поленитесь прочитать подробнейшие комментарии файла squid.conf.default (название может отличаться), имеющемся в любом дистрибутиве.
Работа хелпера digest_ldap_auth несколько отличается от работы других хелперов squid тем, что именно он возвращает серверу squid. Это связано со спецификой работы Digest-аутентификации: если после обработки входных параметров хелпер принял отрицательное решение, то он возвращает ERR, а если положительное, то так называемое значение H(A1) (в других источниках — HA1). На основании этого значения и других данных сервер squid вычисляет так называемое значение response, и, если оно совпало с аналогичным значением, вычисленным и переданным клиентом, аутентификация считается успешно пройденной.
Что же представляет из себя значение H(A1)? Это MD5-хэш от строки, собранной из имени пользователя, наименования области действия аутентификации (realm) и пароля пользователя, объединённых через двоеточие, то есть:
H(A1) = MD5(username:realm:password)
Хелпер digest_ldap_auth получает на вход строку, состоящую из взятого в кавычки имени пользователя и взятого в кавычки наименования области действия аутентификации, объединённых через двоеточие, то есть:
"username":"realm"
Итак, два из трёх значений уже есть. Осталось только обратиться к каталогу LDAP, определить запись-источник данных, получить оттуда недостающие данные и выдать соответствующее этому пользователю и области значение H(A1). Хелпер может сделать это двумя разными способами:
В целом же обращение к каталогу и выбор необходимой записи пользователя происходит почти аналогично тому, как это работает в basic_ldap_auth (смотрите выше): параметры определения записи каталога либо составляются из значений аргументов командной строки и переданного имени пользователя, либо требуемая запись определяется на основании выполнения операции поиска LDAP. Поясним все эти варианты на двух примерах.
# digest_ldap_auth -b ou=Managers,dc=mycompany,dc=ru -u uid -A userPassword -d anton:Squid Digest Auth<Enter> ERR "anton":"Squid Digest Auth"<Enter> Connected OK searchbase 'ou=Managers,dc=mycompany,dc=ru' ERR No such user "vasily":"Squid Digest Auth"<Enter> searchbase 'ou=Managers,dc=mycompany,dc=ru' password: vasilyPassword 67c6aa0de7fcfe7b3240dcc16c56aa41
Как и basic_ldap_auth, при вызове с аргументами -b и -u хелпер digest_ldap_auth работает в режиме составления DN записи из значений этих аргументов и переданного имени пользователя. В аргументе -A указывается атрибут записи пользователя, содержащий пароль для Digest-аутентификации. В данном вызове мы не указали аргумент -e, сообщающий хелперу о том, что в атрибуте (заданном в аргументе -A) содержится уже сформированное значение H(A1), поэтому хелпер считает, что в этом атрибуте содержится пароль в открытом виде. Обратите внимание, что для Digest-аутентификации squid у пользователя может быть отдельный пароль, хранящийся в совершенно другом атрибуте, и указывать, конечно же, требуется нужный атрибут. Просто в нашем случае так совпало, что хелпер получает нужное значение из основного парольного атрибута userPassword.
С аргументами разобрались, теперь перейдём к передаваемым входным параметрам. Как мы уже знаем, это строка, состоящая из взятого в кавычки имени пользователя и взятого в кавычки наименования области действия аутентификации, объединённых через двоеточие. Если эта строка сформирована неправильно, хелпер сразу выдаёт ошибку, как в первом случае. Во втором случае строка сформирована правильно, хелпер извлекает из неё имя пользователя и формирует утверждение uid=anton (название атрибута uid передано в аргументе -u). Тут поведение digest_ldap_auth отличается от basic_ldap_auth, ведь для выполнения подсоединения достаточно иметь DN записи, а для извлечения значения из записи требуется получить саму запись. Поэтому полученное утверждение uid=anton используется не как RND, а как фильтр для выполнения поиска с базой, переданной в аргументе -b. Во втором случае поиск с фильтром uid=anton в ветке ou=Managers,dc=mycompany,dc=ru результатов не даёт и возвращается ошибка ERR No such user. В третьем случае поиск с фильтром uid=vasily в той же ветке возвращает соответствующую запись, из неё извлекается значение атрибута userPassword (передан в аргументе -A), формируется значение H(A1) и выдаётся на стандартный выход. Получив это значение, squid сам принимает решение, прошёл пользователь аутентификацию, или нет. Следует отметить, что если при выполнении поиска запись будет найдена, но в ней не окажется нужного атрибута, хелпер также выдаст ошибку ERR No such user.
Для начала нам требуется выполнить ряд предварительных действий по подготовке записей каталога, а именно:
Для нашего примера возьмём пользователя anton, область аутентификации Squid Digest Auth и пароль antonDigestPassword. Сформируем H(A1):
# echo anton:Squid Digest Auth:antonDigestPassword | md5sum | cut -d ' ' -f 1 1f5f8526da25fce2238f08cdf280fae7
Не будем оригинальничать и в качестве атрибута для хранения H(A1) возьмём, как и в официальном руководстве, атрибут l (location). Разумеется, можно взять и любой другой незанятый атрибут объектного класса inetOrgPerson (в нашем случае). Сформируем файл anton_digest.ldif такого содержания:
dn: uid=anton,ou=People,dc=mycompany,dc=ru changetype: modify add: l l: Squid Digest Auth:1f5f8526da25fce2238f08cdf280fae7
Как видно, в запись пользователя anton добавляется выбранный нами атрибут l со значением, состоящим из названия области аутентификации и сформированного H(A1), разделённых двоеточием (разделитель может быть и иным, тогда его нужно явно указывать в аргументе -l хелпера digest_ldap_auth). Название области аутентификации приводится в этом значении на тот случай, если пользователь проходит Digest-аутентификацию в нескольких разных системах с разными паролями.
Применим наши изменения:
# ldapmodify -x -D cn=manager,dc=mycompany,dc=ru -W -f ./anton_digest.ldif Enter LDAP Password: modifying entry "uid=anton,ou=People,dc=mycompany,dc=ru"
Посмотрим, что у нас получилось:
# ldapsearch -x -LLL -b 'dc=mycompany,dc=ru' 'uid=anton' dn: uid=anton,ou=People,dc=mycompany,dc=ru objectClass: inetOrgPerson uid: anton cn: Anton Ponkrashov sn: Ponkrashov userPassword:: YW50b25QYXNzd29yZA== l: Squid Digest Auth:1f5f8526da25fce2238f08cdf280fae7
Осталось протестировать собственно Digest-аутентификацию (напомним, поиск записей пользователей будет вестись по всему каталогу):
# digest_ldap_auth -b dc=mycompany,dc=ru -s sub -F uid=%s -e -A l -d "vasily":"Squid Digest Auth"<Enter> Connected OK user filter 'uid=vasily', searchbase 'dc=mycompany,dc=ru' No attribute value found ERR No such user "anton":"Squid Proxy Auth"<Enter> user filter 'uid=anton', searchbase 'dc=mycompany,dc=ru' password: (null) ERR No such user "anton":"Squid Digest Auth"<Enter> user filter 'uid=anton', searchbase 'dc=mycompany,dc=ru' password: 1f5f8526da25fce2238f08cdf280fae7 1f5f8526da25fce2238f08cdf280fae7
Аргументы -b, -s и -F задают критерии поиска записей в каталоге LDAP: базу поиска, диапазон поиска и фильтр, соответственно. Всё это нам уже знакомо по хелперу basic_ldap_auth, также как и наличие шаблона %s в поисковом фильтре, который заменяется на имя пользователя, получаемое из входной строки. Самые интересные для нас аргументы: -e — говорит о том, что в каталоге в записи пользователя хранится уже сформированное значение H(A1), и -A — указывает атрибут записи, из которого это значение можно извлечь (в данном случае атрибут l).
На вход хелпер принимает уже знакомую нам строку, состоящую из взятого в кавычки имени пользователя и взятого в кавычки наименования области действия аутентификации, объединённых через двоеточие. В первом случае из входной строки сформирован фильтр uid=vasily, в результате поиска с базой dc=mycompany,dc=ru и диапазоном sub была найдена запись uid=vasily,ou=Managers,dc=mycompany,dc=ru, но в ней не оказалось искомого атрибута l, поэтому хелпер вернул ошибку. Во втором случае был указан пользователь anton, в записи которого присутствует искомый атрибут l, но имя области аутентификации указано неверно (не совпадает с именем в атрибуте l). В результате получить значение H(A1) для запрашиваемой области аутентификации не удалось и хелпер вновь вернул ошибку. Наконец, в третьем случае переданные параметры позволили найти запись uid=anton,ou=People,dc=mycompany,dc=ru и извлечь из неё требуемое значение H(A1), которое хелпер вернул на стандартный вывод.
Осталось показать, как внедрить хелпер digest_ldap_aut в качестве средства аутентификации сервера squid. Основные постулаты всё те же, что и для хелпера basic_ldap_auth (да и для любого другого механизма аутентификации squid):
Поскольку всё это уже подробно обсуждалось в примере настройки хелпера basic_ldap_auth, ограничимся лишь коротким примером и необходимыми пояснениями к нему:
### 1. Настройка Digest-аутентификации с помощью хелпера digest_ldap_auth## Вызов хелпераauth_param digest program /path/to/digest_ldap_auth -b "dc=mycompany,dc=ru" \-F "(&(objectClass=inetOrgPerson)(uid=%s)(l=Squid Digest Auth:*))" \-e -A l -H ldap://127.0.0.1:389 -P## Область действия аутентификацииauth_param digest realm Squid Digest Auth## Количество параллельно запущенных процессов хелпера# auth_param digest children 5## Ожидание символов UTF-8 в имени пользователя или пароле# auth_param digest utf8 on## Заплатка для некоторых браузеров, неверно обрабатывающих Digest-аутентификацию при POST-запросах# auth_param digest post_workaround on## Настройка параметров метки nonce# auth_param digest nonce_garbage_interval 5 minutes# auth_param digest nonce_max_duration 30 minutes# auth_param digest nonce_max_count 50# auth_param digest nonce_strictness off# auth_param digest check_nonce_count on### 2. ACL аутентификации## ACL возвращает TRUE для всех пользователей, прошедших аутентификациюacl acl_all_authed_users proxy_auth REQUIRED### 3. Правила доступа## Разрешён доступ всем, кто прошёл проверкуhttp_access allow acl_all_authed_users## Остальным доступ запрещёнhttp_access deny all
В строках 1-19 определяются настройки Digest-аутентификации. Все директивы настройки Digest-аутентификации начинаются с ключевых слов auth_param digest. В строках 3-5 описывается вызов хелпера, на что указывает ключевое слово program. В данном случае мы воспользовались вариантом вызова хелпера из примера 2.2.2, несколько расширив фильтр поискового запроса LDAP для более точного отбора интересующих нас записей каталога. Также мы указали аргумент -H чтобы показать, с какого именного LDAP-сервера мы хотим получать данные, опустили аргумент -d (в рабочей системе вывод отладочной информации излишен) и добавили аргумент -P (установка постоянного соединения с LDAP-сервером, чтобы не тратить время на повторные подключения).
В строке 7 указывается область действия аутентификации (ключевое слово realm). В отличие от Basic-аутентификации, данный параметр для Digest-аутентификации крайне важен, поскольку на его основе строятся различные значения, которыми обмениваются клиент и сервер в процессе аутентификации, в том числе значение H(A1). Здесь обязательно следует указывать именно то имя области аутентификации, которое мы использовали при формировании значения H(A1) для записей пользователей в LDAP-каталоге.
Все остальные директивы auth_param digest являются опциональными. Часть из них мы уже обсуждали при настройке Basic-аутентификации, описание других можно посмотреть в комментариях файла squid.conf.default. Информацию по отметке nonce можно найти в RFC 7616.
Списки контроля доступа (строки 21-23) и настройка разграничения доступа на их основе (строки 25-29) подробно обсуждались в примере по Basic-аутентификации. При успешном прохождении аутентификации пользователь получает доступ, в противном случае — нет.
Кроме двух рассмотренных схем аутентификации, squid поддерживает ещё схемы NTLM и Negotiate. Обе они были разработаны для интеграции squid в концепцию Microsoft SSO (Single Sign On, технология единого входа). Поскольку прямого отношения к LDAP они не имеют, мы рассмотрим их очень кратко, чтобы в дальнейших примерах иметь некоторое представление об их работе.
NTLM-аутентификация для проверки подлинности пользователя использует обмен пакетами NTLMSSP, характерными для доменов Windows NT (samba3). Официальное HOWTO по настройке NTLM-аутентификации. Для нас важно, что после прохождения NTLM-аутентификации в переменную squid %LOGIN помещается конструкция ИМЯ_ДОМЕНА_WINNT+разделитель+имя_пользователя, например, MYCOMPANY\alex.
Negotiate-аутентификация для проверки подлинности пользователя использует протокол Kerberos. Такая аутентификация характерна для доменов Microsoft Active Directory (samba4). Официальное HOWTO по настройке Negotiate-аутентификации. Для нас важно, что после прохождения Negotiate-аутентификации в переменную squid %LOGIN помещается принципал Kerberos в виде имя_пользователя@REAML, например, anton@mycompany.ru.
В squid.conf можно настроить работу сразу нескольких типов аутентификации, последовательно задав директивы сначала для первого типа, затем для второго и т.д. В этом случае будет предпринята попытка поочерёдно выполнить аутентификацию каждого типа в порядке их указания, пока какая-либо из попыток не увенчается успехом, или все завершатся неудачей. Пример настройки нескольких типов аутентификации можно посмотреть в файле squid.conf.default.
Последнее изменение страницы — 13 октября 2016 года.
Обсуждение статьи на форуме проекта Pro-LDAP.ru.