Метод 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
}
}
Конфигурация
Перед тем как пользователи смогут проходить аутентификацию, необходимо предварительно настроить методы аутентификации. Эти шаги обычно выполняются оператором или средством управления конфигурацией.
- Включить метод аутентификации LDAP:
d8 stronghold auth enable ldap
- Настройте параметры подключения к серверу 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.