Аутентификация LDAP в OpenVPN работает, но проверка группы не проходит: куда смотреть
Аутентификация может пройти, а проверка группы отклоняет всех. По очереди просмотрите блок Authorization, атрибут членства и конечную точку каталога.
Учётные данные проходят, туннель начинает подниматься, и затем сеанс завершается ошибкой с сообщением о группе, а не о пароле. Такое сочетание помещает сбой во вторую половину работы плагина LDAP: привязка и проверка пароля прошли, а следующее за ними решение о членстве нет. Обе половины читают из одного и того же блока конфигурации, поэтому исправление в одном месте может оставить другую нетронутой. Начните со строки журнала, где назван шаг, а не с учётной записи, с которой вы случайно тестируете.
Считайте отказ ответом об авторизации
Плагин выполняет привязку службы, ищет учётную запись, проверяет переданный пароль и затем спрашивает, принадлежит ли эта запись разрешённой группе. Это четыре отдельные операции, и только последняя касается групп. Поэтому отказ, приходящий после успешной привязки, ничего не говорит о пароле, даже когда клиент показывает общую ошибку аутентификации. Повышение уровня журнала плагина до изменения конфигурации — это то, что разделяет четыре операции в выводе.
Ищите саму оценку группы. Когда запрос группы вообще не появляется, блок авторизации не достигается, что обычно означает, что директива стоит вне блока, которому она принадлежит, или отсутствует обязательный параметр. Когда запрос появляется и ничего не возвращает, конечная точка или фильтр отвечают на другой вопрос, нежели тот, который вы имели в виду.
Решите, живёт ли членство в фильтре или в блоке группы
Есть две поддерживаемые формы, и их смешивание — это способ, которым работающая конфигурация перестаёт работать. Первая проталкивает членство в поиск пользователя, запрашивая атрибут на объекте пользователя; вторая оставляет поиск пользователя в покое и заставляет плагин после этого искать объект группы. Выберите одну на развёртывание и запишите, какую выбрали.
<Authorization>
BaseDN "OU=People,DC=example,DC=org"
SearchFilter "(&(sAMAccountName=%u)(memberOf=CN=VPN Access,OU=Groups,DC=example,DC=org))"
RequireGroup false
</Authorization>При такой форме фильтр решает всё, поэтому RequireGroup должен быть false, иначе плагин отправится искать объект группы, который фильтр никогда не упоминал. Атрибут в фильтре должен существовать на объекте пользователя и быть заполненным, что не гарантировано, как только группы вложены.
Другая форма оставляет поиск простым и добавляет после него поиск группы. Объект группы называется явно, и плагин сравнивает пользователя с участниками, которых каталог возвращает для него. В фильтре пользователя ничего не меняется, что делает эту форму проще для аудита, когда группой управляет другая команда.
<Authorization>
BaseDN "OU=People,DC=example,DC=org"
SearchFilter "(sAMAccountName=%u)"
RequireGroup true
Group "CN=VPN Access,OU=Groups,DC=example,DC=org"
</Authorization>Слепое пятно общее: простое сравнение членства не разворачивает вложенные группы, поэтому учётная запись, доступ которой приходит через родительскую группу, не проходит, хотя консоль по-прежнему показывает членство. Active Directory предоставляет member на группе, другие схемы используют uniqueMember, а указание на неверный атрибут возвращает пустой результат вместо ошибки.
Выберите конечную точку, которая содержит объект группы
URL определяет, к какому каталогу обращается плагин, а контроллер домена на порту 389 отвечает только за свой собственный контекст имён. Глобальный каталог на порту 3268 отвечает за весь лес, только для чтения и с частичным набором атрибутов. Когда учётные записи находятся в одном домене, а группа VPN в другом, запрос к одному домену может найти пользователя и всё же пропустить членство. Это тот случай, когда перенос URL на порт каталога решает отказ, и это также случай, когда люди обобщают слишком далеко.
Частичный набор атрибутов — это предел: по атрибуту, которого каталог не содержит, фильтровать нельзя, поэтому одно и то же изменение может заставить одно развёртывание работать, а другое возвращать пусто. Подтвердите, что конечная точка действительно возвращает для реального пользователя и группы, прежде чем переписывать блок. Перенаправления и TLS — снова отдельные вопросы, поскольку за перенаправлением нужно проследить, а конечной точке, требующей TLS, нужны соответствующие файлы доверия.
Держите состояние учётной записи и область поиска в их собственных полях
Флаг отключения принадлежит фильтру, а не проверке группы. В Active Directory это второй бит userAccountControl, и без этого условия отключённая учётная запись с действующим паролем проходит аутентификацию, а затем срывается на шаге группы, что выглядит как ошибка авторизации. Область поиска принадлежит базовому DN, потому что начинать от корня домена медленнее и можно задеть объекты за пределами организационного подразделения, которое вы хотели охватить.
Держите и права учётной записи привязки узкими. Широкие права каталога превращают неверно настроенный фильтр в успешный запрос и скрывают ошибку, пока кто-нибудь не проведёт аудит учётной записи. Меняйте членство в самом каталоге, никогда в локальном списке исключений, чтобы отключения учётной записи было достаточно для снятия доступа. Если группой управляет другая команда, договоритесь об атрибуте, который они заполняют, прежде чем писать на него фильтр.
Докажите результат тремя учётными записями
Запустите конфигурацию на трёх идентичностях: разрешённый участник, действующая учётная запись вне группы и учётная запись, которая была отключена. Если отклонены все три, привязка или запрос группы сломаны; если приняты все три, условие группы не применяется вовсе. Смешанный результат — единственный, который доказывает, что конфигурация различает важные для вас случаи.
Прочитайте те же три ответа прямо из каталога, прежде чем винить плагин. Первый запрос показывает атрибут группы, от которого зависит фильтр, а второй — значение управления учётной записью. Подставьте свой базовый DN, учётную запись и конечную точку:
ldapsearch -H ldap://dc01.example.org:3268 -x -D "[email protected]" -W -b "DC=example,DC=org" "(sAMAccountName=alice)" memberOf
ldapsearch -H ldap://dc01.example.org:3268 -x -D "[email protected]" -W -b "DC=example,DC=org" "(sAMAccountName=bob)" userAccountControlПользователь, у которого memberOf возвращается пустым, — это факт каталога, а не ошибка плагина, а учётная запись, которая после отключения всё ещё проходит аутентификацию, означает, что в фильтре отсутствует условие управления учётной записью. Сначала исправьте это доказательство, затем снова запустите тест с тремя учётными записями. Держите строку журнала, где назван сбойный шаг, рядом с изменением конфигурации, чтобы следующий человек мог отделить авторизацию от аутентификации, не повторяя поиск.