Неудачная LDAP аутентификация

Введение

Сразу отмечу, что в качестве Веб-интерфейса я использую 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»).

/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. Его участники рассказывают на страницах нашего сайта про самую интересную/смешную/странную/тупую фичу, которую вам доводилось реализовывать. Победителей мы выберем по рейтингу, который будет сформирован на основании оценок пользователей и редакции сайта.

Если вам понравилась эта история, проголосуйте за неё.

Под постом есть блок с реакциями. Если история вам понравилась, ставьте палец вверх. Только эти реакции будут учитываться при составлении рейтинга. Другие эмоции подсчитываться не будут.

А вот история другого участника — он написал кастомный шрифт ради одного символа. Но сначала неделю страдал.

Похожее:  Use React and Spring Boot to Build a Simple CRUD App | Okta Developer

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *