Метод ldap auth позволяет выполнять аутентификацию с использованием существующего сервера LDAP и учетных данных - имени пользователя и пароля. Это позволяет интегрировать Stronghold в среды, использующие LDAP. Сопоставление групп и пользователей в LDAP с политиками Stronghold осуществляется с помощью путей users/ и groups/.

Аутентификация

С помощью CLI

$ d8 stronghold login -method=ldap username=mitchellh
Password (will be hidden):
Successfully authenticated! The policies that are associated
with this token are listed below:

admins

С помощью API

$ curl \
    --request POST \
    --data '{"password": "foo"}' \
    http://127.0.0.1:8200/v1/auth/ldap/login/mitchellh

Ответ будет представлен в формате JSON. Например:

{
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": null,
  "auth": {
    "client_token": "c4f280f6-fdb2-18eb-89d3-589e2e834cdb",
    "policies": [
      "admins"
    ],
    "metadata": {
      "username": "mitchellh"
    },
    "lease_duration": 0,
    "renewable": false
  }
}

Конфигурация

Перед тем как пользователи смогут проходить аутентификацию, необходимо предварительно настроить методы аутентификации. Эти шаги обычно выполняются оператором или средством управления конфигурацией.

  1. Включить метод аутентификации LDAP:
d8 stronghold auth enable ldap
  1. Настройте параметры подключения к серверу LDAP, а также информацию о том, как аутентифицировать пользователей, и как запрашивать членство в группах.

Параметры подключения

  • url (строка, обязательно) - Сервер LDAP, к которому необходимо подключиться. Примеры: ldap://ldap.myorg.com, ldaps://ldap.myorg.com:636. Это также может быть список URL через запятую, например, ldap://ldap.myorg.com,ldaps://ldap.myorg.com:636, в этом случае серверы будут использованы в порядке очереди, если в процессе соединения возникнут ошибки.
  • starttls (bool, необязательно) - Если значение параметра true, то после установления незашифрованного соединения выдается команда StartTLS.
  • insecure_tls - (bool, необязательно) - Если true, пропускает проверку SSL-сертификата LDAP-сервера - небезопасно, используйте с осторожностью!
  • certificate - (строка, необязательно) - сертификат CA, который будет использоваться при проверке сертификата LDAP-сервера, должен быть в кодировке x509 PEM.
  • client_tls_cert - (строка, необязательно) - Сертификат клиента для предоставления серверу LDAP, должен быть в кодировке x509 PEM.
  • client_tls_key - (строка, необязательно) - Ключ сертификата клиента для предоставления серверу LDAP, должен быть закодирован в формате x509 PEM.

Параметры поиска

Существуют два разных способа определения объекта пользователя для аутентификации конечного пользователя: Поиск и Основное имя пользователя (User Principal Name, UPN). Если используется Поиск, привязка может быть выполнена либо анонимно, либо с аутентификацией. Метод UPN поддерживается в Active Directory для указания пользователей. Дополнительную информацию о UPN можно найти здесь.

Аутентифицированный поиск

  • binddn (строка, необязательно) - Имя объекта для привязки при выполнении поиска пользователей и групп. Пример: cn=stronghold,ou=Users,dc=example,dc=com.
  • bindpass (строка, необязательно) - Пароль, используемый вместе с binddn при поиске пользователей.
  • userdn (строка, необязательно) - Корневой DN, в котором осуществляется поиск пользователя. Пример: ou=Users,dc=example,dc=com.
  • userattr (строка, необязательно) - Атрибут LDAP-объекта пользователя, который должен совпадать с именем пользователя, переданным при аутентификации. Примеры: sAMAccountName, cn, uid.
  • userfilter (строка, необязательно) - шаблон Go, используемый для построения фильтра поиска пользователей ldap. Шаблон может обращаться к следующим контекстным переменным: [UserAttr, Username]. По умолчанию используется фильтр ({{.UserAttr}}={{.Username}}) или (userPrincipalName={{.Username}}@UPNDomain), если задан параметр upndomain. Фильтр поиска пользователей можно использовать для ограничения количества пользователей, которые могут попытаться войти в систему. Например, чтобы ограничить вход для пользователей, которые не являются подрядчиками (Contractors), можно написать (&(objectClass=user)({{.UserAttr}}={{.Username}})(!(employeeType=Contractor))).

При указании userfilter в фильтре должно присутствовать либо шаблонизированное значение {{.UserAttr}}, либо литеральное значение, соответствующее userattr, чтобы гарантировать, что поиск возвращает уникальный результат, учитывающий userattr для целей сопоставления псевдонимов сущностей, и избежать возможных коллизий при входе в систему.

Анонимный поиск

  • discoverdn (bool, необязательно) - Если true, используется анонимное подключение для определения bind DN пользователя.
  • userdn (строка, необязательно) - Корневой DN, в котором осуществляется поиск пользователя. Пример: ou=Users,dc=example,dc=com.
  • userattr (строка, необязательно) - Атрибут LDAP-объекта пользователя, который должен совпадать с именем пользователя, переданным при аутентификации. Примеры: sAMAccountName, cn, uid.
  • userfilter (строка, необязательно) - шаблон Go, используемый для построения фильтра поиска пользователей ldap. Шаблон может обращаться к следующим контекстным переменным: [UserAttr, Username]. По умолчанию используется фильтр ({{.UserAttr}}={{.Username}}) или (userPrincipalName={{.Username}}@UPNDomain), если задан параметр upndomain. Фильтр поиска пользователей можно использовать для ограничения количества пользователей, которые могут попытаться войти в систему. Например, чтобы ограничить вход для пользователей, которые не являются подрядчиками (Contractors), можно написать (&(objectClass=user)({{.UserAttr}}={{.Username}})(!(employeeType=Contractor))).
  • deny_null_bind (bool, optional) - Опция, предотвращающая обход аутентификации, если указан пустой пароль. По умолчанию установлено значение true.
  • anonymous_group_search (bool, необязательно) - Использовать анонимные подключения при выполнении поиска групп в LDAP. По умолчанию установлено значение false.

При указании userfilter в фильтре должно присутствовать либо шаблонизированное значение {{.UserAttr}}, либо литеральное значение, соответствующее userattr, чтобы гарантировать, что поиск возвращает уникальный результат, учитывающий userattr для целей сопоставления псевдонимов сущностей, и избежать возможных коллизий при входе в систему.

Разрешение алиасов

  • dereference_aliases (строка, необязательно) - Управляет тем, как алиасы разрешаются при выполнении поиска. Возможные значения: never, finding, searching и always. Значение finding разрешает алиасы только во время определения имени. Значение searching разрешает алиасы после определения имени.

Использование UPN (AD)

  • upndomain (строка, необязательно) - userPrincipalDomain, используемый для построения строки UPN (основное имя пользователя) для аутентифицируемого пользователя. UPN будет иметь вид [username]@UPNDomain. Пример: example.com, что приведет к тому, что Stronghold произведет привязку как username@example.com.

Определение членства в группах

После аутентификации пользователя метод аутентификации LDAP должен определить, к каким группам принадлежит пользователь. Конфигурация может варьироваться в зависимости от вашего LDAP-сервера и схемы каталога. Существует два основных подхода к определению членства в группе — первый заключается в поиске аутентифицированного объекта пользователя и следовании за атрибутом к группам, в которых он состоит. Второй подход подразумевает поиск объектов группы, членом которых является аутентифицированный пользователь. Поддерживаются оба метода.

  • groupfilter (строка, необязательно) - Шаблон Go, используемый при построении запроса на членство в группе. Шаблон может использовать следующие переменные контекста: [UserDN, Username]. По умолчанию используется (|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}})), что совместимо с несколькими распространенными схемами каталогов. Для поддержки разрешения вложенных групп в Active Directory вместо этого используйте следующий запрос: (&(objectClass=group)(member:1.2.840.113556.1.4.1941:={{.UserDN}})).
  • groupdn (строка, обязательно) - База поиска LDAP для использования при поиске членства в группах. Это может быть корень, содержащий либо группы, либо пользователей. Пример: ou=Groups,dc=example,dc=com.
  • groupattr (строка, необязательно) - Атрибут LDAP, используемый для объектов, возвращаемых groupfilter, чтобы перечислить членство в группах пользователя. Примеры: для запросов groupfilter, возвращающих объекты group, используйте: cn. Для запросов, возвращающих объекты user, используйте: memberOf. Значение по умолчанию — cn.

Примечание: При использовании Аутентифицированного поиска имя, указанное в binddn, также используется для поиска группы. В противном случае для выполнения поиска группы используется аутентифицированный пользователь (пользователь, выполняюший вход).

Другие параметры

  • username_as_alias (bool, необязательно) - Если установлено в true, метод аутентификации будет использовать имя пользователя, переданное пользователем, в качестве имени алиаса.
  • max_page_size (int, необязательно) - Если установлено значение больше 0, Stronhold будет выполнять постаничный поиск на LDAP-сервере, и запрашивать страницы размером до указанного значения. Этот параметр может понадобиться, если существует ограничение на максимальный размер результата LDAP-сервера. В противном случае постраничный поиск не будет использоваться.

Примеры

Сценацрий 1

  • LDAP-сервер доступен по адресу ldap.example.com, порт 389.
  • Сервер поддерживает команду STARTTLS для инициации шифрования на стандартном порту.
  • Сертификат УЦ хранится в файле с именем ldap_ca_cert.pem.
  • Сервер является Active Directory и поддерживает атрибут userPrincipalName. Пользователи идентифицируются как username@example.com.
  • Группы являются вложенными, мы будем использовать LDAP_MATCHING_RULE_IN_CHAIN для обхода дерева групп.
  • Поиск групп начнется с ou=Groups,dc=example,dc=com. Для всех объектов групп по этому пути атрибут member будет проверяться на соответствие аутентифицированному пользователю.
  • Имена групп определяются на основании их атрибута cn.
$ d8 stronghold write auth/ldap/config \
    url="ldap://ldap.example.com" \
    userdn="ou=Users,dc=example,dc=com" \
    groupdn="ou=Groups,dc=example,dc=com" \
    groupfilter="(&(objectClass=group)(member:1.2.840.113556.1.4.1941:={{.UserDN}}))" \
    groupattr="cn" \
    upndomain="example.com" \
    certificate=@ldap_ca_cert.pem \
    insecure_tls=false \
    starttls=true
...

Сценарий 2

  • LDAP-сервер доступен по адресу ldap.example.com, порт 389.
  • Сервер поддерживает команду STARTTLS для инициации шифрования на стандартном порту.
  • Сертификат УЦ хранится в файле с именем ldap_ca_cert.pem.
  • Сервер не позволяет анонимные подключения для выполнения поиска пользователей.
  • На сервере существует учетная запись для поиска — cn=stronghold,ou=users,dc=example,dc=com с паролем My$ecrt3tP4ss.
  • Объекты пользователей находятся в OU ou=Users,dc=example,dc=com.
  • Имя пользователя, переданное в stronghold при аутентификации, отображается на атрибут sAMAccountName.
  • Членство в группе будет определяться через атрибут memberOf объектов user. Этот поиск начнется в ou=Users,dc=example,dc=com.
$ d8 stronghold write auth/ldap/config \
    url="ldap://ldap.example.com" \
    userattr=sAMAccountName \
    userdn="ou=Users,dc=example,dc=com" \
    groupdn="ou=Users,dc=example,dc=com" \
    groupfilter="(&(objectClass=person)(uid={{.Username}}))" \
    groupattr="memberOf" \
    binddn="cn=stronghold,ou=users,dc=example,dc=com" \
    bindpass='My$ecrt3tP4ss' \
    certificate=@ldap_ca_cert.pem \
    insecure_tls=false \
    starttls=true
...

Сценарий 3

  • LDAP-сервер доступен по адресу ldap.example.com, порт 636 (LDAPS).
  • Сертификат УЦ хранится в файле с именем ldap_ca_cert.pem.
  • Объекты пользователей находятся в OU ou=Users,dc=example,dc=com.
  • Имя пользователя, переданное в Stronghold при аутентификации, соответствует атрибуту uid.
  • User bind DN будет автоматически определен с использованием анонимного подключения.
  • Членство в группе будет определяться через один из атрибутов: memberUid, member или uniqueMember. Этот поиск начнется в ou=Groups,dc=example,dc=com.
  • Имена групп идентифицируются с использованием атрибута cn.
$ d8 stronghold write auth/ldap/config \
    url="ldaps://ldap.example.com" \
    userattr="uid" \
    userdn="ou=Users,dc=example,dc=com" \
    discoverdn=true \
    groupdn="ou=Groups,dc=example,dc=com" \
    certificate=@ldap_ca_cert.pem \
    insecure_tls=false \
    starttls=true
...

Сопоставление групп LDAP и политик

Далее мы хотим создать сопоставление группы LDAP с политикой Stronghold:

d8 stronghold write auth/ldap/groups/scientists policies=foo,bar

Это сопоставляет группу LDAP «scientists» с политиками Stronghold «foo» и «bar». Мы также можем добавить определенных пользователей LDAP в дополнительные (потенциально не-LDAP) группы. Обратите внимание, что политики могут быть указаны и для пользователей LDAP.

d8 stronghold write auth/ldap/groups/engineers policies=foobar
d8 stronghold write auth/ldap/users/tesla groups=engineers policies=zoobar

Это добавляет пользователя LDAP «tesla» в группу «engineers», которая соответствует политике Stronghold «foobar». Сам пользователь «tesla» связан с политикой «zoobar».

Наконец, мы можем проверить это, пройдя аутентификацию:

$ d8 stronghold login -method=ldap username=tesla
Password (will be hidden):
Successfully authenticated! The policies that are associated
with this token are listed below:

default, foobar, zoobar

Примечание о сопоставлении политик

Следует отметить, что сопоставление пользователь -> политика происходит во время создания токена. Изменения в членстве группы на LDAP-сервере ниак не повлияют на токены, которые уже были предоставлены. Чтобы эти изменения стали актуальными, необходимо отозвать старые токены и попросить пользователя пройти повторную аутентификацию.

Блокировка пользователя

Если пользователь несколько раз подряд предоставит неверные учетные данные, Stronghold на некоторое время прекратит попытки проверить его учетные данные, а вместо этого сразу же вернет ошибку с отказом доступе. Мы называем такое поведение «блокировкой пользователя» (user_lockout). Время, на которое пользователь будет заблокирован, называется «длительностью блокировки» (lockout_duration). Пользователь сможет войти в систему после истечения срока блокировки. Количество неудачных попыток входа, после которых пользователь будет заблокирован, называется «порог блокировки» (lockout_threshold). Счетчик порога блокировки обнуляется через несколько минут без попыток входа или при успешной попытке входа. Время, в течение которого счетчик будет обнулен после отсутствия попыток входа, называется «сброс счетчика блокировки» (lockout_counter_reset). Это позволяет предотвратить атаки с целью подбора пароля.

Функция блокировки пользователя включена по умолчанию. Значения по умолчанию:

  • lockout_threshold - 5 попыток
  • lockout_duration - 15 минут
  • lockout_counter_reset - 15 минут.

Функцию блокировки пользователя можно отключить с помощью команды «auth tune», передав значение disable_lockout true

Этот функционал поддерживается только методами userpass, ldap и approle auth.

Экранирование DN

Важно выполнить правильное экранирования DN (Distinguished Names - Уникальные имена), а именно DN пользователя, DN для поиска и так далее. Единственное экранирование DN, выполняемое этим методом, касается имен пользователей, указанных при входе в систему, когда они вставляются в окончательный bind DN, и оно использует правила экранирования, определенные в RFC 4514. Кроме того, в Active Directory существуют правила экранирования, которые немного отличаются от RFC; в частности, требуется экранирование символа ‘#’, независимо от его положения в DN (RFC требует его экранирования только в случае, если он является первым символом), и ‘=’, который согласно RFC может быть экранирован с помощью обратного слэша, но не включен в набор обязательных символов для экранирования. Если вы используете Active Directory и эти символы присутствуют в ваших именах пользователей, убедитесь, что они экранированы, а также корректно экранированы в ваших настроенных DN. Для справки см. RFC 4514 и эту статью TechNet о символах для экранирования в Active Directory.