Введение
Сразу отмечу, что в качестве Веб-интерфейса я использую OpenSearch-Dashboards. И в основном именно для OpenSearch-Dashboards мне нужна доменная авторизация.
Для того, чтобы настроить аутентификацию и авторизацию доменных пользователей Active Directory в OpenSearch по протоколу LDAP необходимо сконфигурировать файл «/plugins/opensearch-security/securityconfig/config.yml» (в моем случае полный путь к этому файлу такой «/opt/opensearch/plugins/opensearch-security/securityconfig/config.yml»).
Предисловие
Эта статья является продолжением статьи
. Однако, тема этой статьи достаточно узкая и практически не зависит от основной настройки всего стека OpenSearch (материала первой статьи). Поэтому эту статью можно считать самостоятельной.
. Послесловие
На этом всё. Всем спасибо за внимание к статье, надеюсь она окажется для кого-то полезной.
Коротко об объектах настройки конфиденциальности в OpenSearch
В OpenSearch, как и во многих других системах, для распределения привилегий в системе используются учетные записи.
Каждая учетная запись может иметь набор прав доступов, которые наделяют пользователя полномочиями.
Определенный набор прав доступов объединяется в роль. Пользователь может иметь как одну, так и несколько ролей. В случае, когда пользователю назначены несколько ролей, права доступов которых противоречат друг другу, преимущество имеют права, дающие пользователю больший доступ.
Например, если дать права администратора и наблюдателя одновременно, то фактически пользователь будет иметь права администратора.
А вот «Backend» роль сущность несколько абстрактная. «Обычная» роль может иметь «Backend» роль или даже несколько «Backend» ролей. Пользователь тоже может иметь «Backend» роль или даже несколько «Backend» ролей. Если «Backend» роль есть у пользователя, и эта же «Backend» роль есть у «обычной» роли, то такой пользователь становится обладателем этой «обычной» роли.
«Backend» роли становятся актуальными для использования при доменной авторизации. Потому как доменные пользователи не доступны для настройки прав или «обычных» ролей, они могут получить только «Backend» роль автоматически, в зависимости от настроенной конфигурации.
Настройка LDAP
Для наглядности я сразу приведу уже сконфигурированный файл «config.yml» целиком (в моем случае это файл «/opt/opensearch/plugins/opensearch-security/securityconfig/config.yml»).
---
# This is the main OpenSearch Security configuration file where authentication
# and authorization is defined.
_meta:
type: "config"
config_version: 2
config:
dynamic:
# Set filtered_alias_mode to 'disallow' to forbid more than 2 filtered aliases per index
# Set filtered_alias_mode to 'warn' to allow more than 2 filtered aliases per index but warns about it (default)
# Set filtered_alias_mode to 'nowarn' to allow more than 2 filtered aliases per index silently
#filtered_alias_mode: warn
#do_not_fail_on_forbidden: false
#kibana:
# Kibana multitenancy
#multitenancy_enabled: true
#server_username: kibanaserver
#index: '.kibana'
http:
anonymous_auth_enabled: false
xff:
enabled: false
internalProxies: '192.168.0.10|192.168.0.11' # regex pattern
#internalProxies: '.*' # trust all internal proxies, regex pattern
#remoteIpHeader: 'x-forwarded-for'
###### see https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html for regex help
###### more information about XFF https://en.wikipedia.org/wiki/X-Forwarded-For
###### and here https://tools.ietf.org/html/rfc7239
###### and https://tomcat.apache.org/tomcat-8.0-doc/config/valve.html#Remote_IP_Valve
authc:
kerberos_auth_domain:
http_enabled: false
transport_enabled: false
order: 6
http_authenticator:
type: kerberos
challenge: true
config:
# If true a lot of kerberos/security related debugging output will be logged to standard out
krb_debug: false
# If true then the realm will be stripped from the user name
strip_realm_from_principal: true
authentication_backend:
type: noop
basic_internal_auth_domain:
description: "Authenticate via HTTP Basic against internal users database"
http_enabled: true
transport_enabled: true
order: 4
http_authenticator:
type: basic
challenge: true
authentication_backend:
type: intern
proxy_auth_domain:
description: "Authenticate via proxy"
http_enabled: false
transport_enabled: false
order: 3
http_authenticator:
type: proxy
challenge: false
config:
user_header: "x-proxy-user"
roles_header: "x-proxy-roles"
authentication_backend:
type: noop
jwt_auth_domain:
description: "Authenticate via Json Web Token"
http_enabled: false
transport_enabled: false
order: 0
http_authenticator:
type: jwt
challenge: false
config:
signing_key: "base64 encoded HMAC key or public RSA/ECDSA pem key"
jwt_header: "Authorization"
jwt_url_parameter: null
roles_key: null
subject_key: null
authentication_backend:
type: noop
clientcert_auth_domain:
description: "Authenticate via SSL client certificates"
http_enabled: false
transport_enabled: false
order: 2
http_authenticator:
type: clientcert
config:
username_attribute: cn #optional, if omitted DN becomes username
challenge: false
authentication_backend:
type: noop
ldap:
description: "Authenticate via LDAP or Active Directory"
http_enabled: true
transport_enabled: false
order: 5
http_authenticator:
type: basic
challenge: false
authentication_backend:
# LDAP authentication backend (authenticate users against a LDAP or Active Directory)
type: ldap
config:
# enable ldaps
enable_ssl: false
# enable start tls, enable_ssl should be false
enable_start_tls: false
# send client certificate
enable_ssl_client_auth: false
# verify ldap hostname
verify_hostnames: true
hosts:
- server-ad.my.big.domain:389
bind_dn: 'cn=user_for_LDAP,ou=Service Accounts,ou=Moscow,dc=MY,dc=BIG,dc=DOMAIN'
password: 'Au5dUJ9q!54S'
users:
1-userbase:
base: 'CN=Пушкин Александр Сергеевич (PushkinAS),OU=Users,OU=Saint_Petersburg,DC=MY,DC=BIG,DC=DOMAIN'
search: '(sAMAccountName={0})'
2-userbase:
base: 'CN=Горький Максим (GorkiiM),OU=Users,OU=Nizhny_Novgorod,DC=MY,DC=BIG,DC=DOMAIN'
search: '(sAMAccountName={0})'
3-userbase:
base: 'CN=Толстой Лев Николаевич (TolstoiLN),OU=Users,OU=Yasnaya_Polyana,DC=MY,DC=BIG,DC=DOMAIN'
search: '(sAMAccountName={0})'
username_attribute: 'cn'
authz:
roles_from_myldap:
description: "Authorize via LDAP or Active Directory"
http_enabled: true
transport_enabled: false
authorization_backend:
# LDAP authorization backend (gather roles from a LDAP or Active Directory, you have to configure the above LDAP authentication backend settings too)
type: ldap
config:
# enable ldaps
enable_ssl: false
# enable start tls, enable_ssl should be false
enable_start_tls: false
# send client certificate
enable_ssl_client_auth: false
# verify ldap hostname
verify_hostnames: true
hosts:
- server-ad.my.big.domain:389
bind_dn: 'cn=user_for_LDAP,ou=Service Accounts,ou=Moscow,dc=MY,dc=BIG,dc=DOMAIN'
password: 'Au5dUJ9q!54S'
users:
1-userbase:
base: 'CN=Пушкин Александр Сергеевич (PushkinAS),OU=Users,OU=Saint_Petersburg,DC=MY,DC=BIG,DC=DOMAIN'
search: '(sAMAccountName={0})'
2-userbase:
base: 'CN=Горький Максим (GorkiiM),OU=Users,OU=Nizhny_Novgorod,DC=MY,DC=BIG,DC=DOMAIN'
search: '(sAMAccountName={0})'
3-userbase:
base: 'CN=Толстой Лев Николаевич (TolstoiLN),OU=Users,OU=Yasnaya_Polyana,DC=MY,DC=BIG,DC=DOMAIN'
search: '(sAMAccountName={0})'
username_attribute: 'cn'
roles:
1-rolebase:
base: 'CN=Department05-Developers,OU=Groups,OU=Moscow,DC=MY,DC=BIG,DC=DOMAIN'
search: '(member={0})'
2-rolebase:
base: 'CN=Department05-Admins,OU=Groups,OU=Moscow,DC=MY,DC=BIG,DC=DOMAIN'
search: '(member={0})'
3-rolebase:
base: 'CN=Department05-Analysts,OU=Groups,OU=Moscow,DC=MY,DC=BIG,DC=DOMAIN'
search: '(member={0})'
userroleattribute: null
userrolename: memberOf, SamAccountName
rolename: "cn"
resolve_nested_roles: false
roles_from_another_ldap:
description: "Authorize via another Active Directory"
http_enabled: false
transport_enabled: false
authorization_backend:
type: ldap
Описание всех параметров вы можете посмотреть на официальном сайте OpenSearch (
Получаем полный путь к объектам в AD
Полный путь к группе в AD через PowerShell можно получить так:
$group = ([adsisearcher]“(&(objectcategory=group)(cn=name_of_group))”).Findall()
$group
Вместо «name_of_group» подставьте название нужной вам группы.
Полный путь к пользователю в AD через PowerShell можно получить так:
Проблема применения настроек
Выполнение команды для применения настроек удаляет все изменения, сделанные в OpenSearch-Dashboards в разделе «Security», то есть все настройки пользователей и ролей. И это является проблемой, так как в конфигурацию со временем будет необходимо добавлять новых пользователей и удалять старых пользователей.
Видимо разработчики OpenSearch всё же подразумевают, что все пользователи в AD лежат в одном каталоге и «лишних» пользователей там нет. Либо всё-таки подразумевается использование проксирующего LDAP сервера. Другого объяснения я не нашел.
Для того чтобы обойти эту проблему можно вносить изменения не в Web-интерфейсе (OpenSearch-Dashboards), а через файлы конфигураций.
Далее опишу настройку конфигурационных файлов.
Разработка тихого монстра
Задача прилетела в работу через месяц. К этому времени я уже немного размялся на формошлёпстве и почитал про LDAP. Первый прототип получился за 1-2 недели.
Он состоял из небольшого количества JS-кода и двух простейших AJAX-запросов на фронте, а также одного простенького servlet’a на бэке. Использовал я SPNEGO/Kerberos, найдя какую-то захудалую JAR’ку в интернете.
При входе пользователя в систему его перекидывало на login-форму, там то фоном и шёл один из AJAX-запросов, который проверял, кто пришёл и привязана ли его учётка Active Directory к учётной записи в приложении.
Серые тучи несовместимости
Теперь начинается то, ради чего я вообще и решил рассказать эту историю. В первый же день после релиза жизнь моя омрачилась и пропиталась серостью и болью. Всё вокруг потемнело, обедать я стал с чувством некоторой обеспокоенности.
У большинства пользователей фича не просто не работала, но и крашила все приложения. В основном это было связано с очень старыми версиями IE и Windows на компьютерах сотрудников. Ведь тестировал-то всё это дело ИТ-отдел, а у нас у всех свежая винда, обновления, браузеры.
Всё омрачалось тем, что основным разработчиком приложения был крупный подрядчик, который при любом удобном случае указывал, что мы совсем безалаберные орки, которые всё тут сломали, а они высшие эльфы.
Так вот, поговорим про баги и их фиксы или безысходность.
Каждый день мы собирались на совещания с видеосвязью, и пользователи стримили нам свою боль. Через месяц таких мучений был написан подробный гайд о том, как настроить свой IE или Chrome для успешной работы, какие галочки снять, какие поставить, какие настройки поменять в ОС. Занимал он примерно две страницы в Word-документе.
В итоге со временем какие-то баги ушли, с какими-то люди научились жить и ловко работать в нескольких окнах браузера, копируя ссылки, вырезая какие-то параметры из строки и т.д. Но порой мы всё так же собирались, смотрели на чей-то квадратный экран и то как там ничего не работает.
За всё время работы в компании я периодически мониторил метрики использования данной фичи. Вроде как успешно ей пользовалось не более 5-10% пользователей. Надеюсь, теперь у большинства пользователей эта аутентификация работает. Или хотя бы не мешает им жить. Ещё надеюсь, что я немного сгустил краски и на самом деле всё было не так плохо.
В итоге эти запланированные 10 дней вылились в 1-2 месяца насыщенной разработки и настройки и ещё в полгода-год болезненной поддержки.
***
Эту историю наш пользователь написал в рамках участия в конкурсе, посвящённому #фичавгусту, который мы проводим вместе с OTUS. Его участники рассказывают на страницах нашего сайта про самую интересную/смешную/странную/тупую фичу, которую вам доводилось реализовывать. Победителей мы выберем по рейтингу, который будет сформирован на основании оценок пользователей и редакции сайта.
Если вам понравилась эта история, проголосуйте за неё.
Под постом есть блок с реакциями. Если история вам понравилась, ставьте палец вверх. Только эти реакции будут учитываться при составлении рейтинга. Другие эмоции подсчитываться не будут.
А вот история другого участника — он написал кастомный шрифт ради одного символа. Но сначала неделю страдал.