Левинца Егор
Обсуждение статьи на форуме проекта 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.