Перейти к основному содержимому
Версия: 2.0 (WIP)

Ролевая модель OpenSearch

В состав Памир входит плагин безопасности OpenSearch (opensearch-security), обеспечивающий аутентификацию и авторизацию всех обращений к API OpenSearch. Данный раздел описывает встроенную ролевую модель, её интеграцию с системой аутентификации Памир через API Gateway, а также порядок изменения конфигурации.

Архитектура аутентификации

Запросы пользователей к OpenSearch проходят через двухуровневую цепочку:

Пользователь


API Gateway (nginx)
│ auth_request → /v3/internal/user (auth-service)
│ ← X-User-Login, X-User-Opensearch-Role


OpenSearch (Security Plugin)
│ proxy_auth: читает заголовки X-User-Login / X-User-Opensearch-Role
│ basic_auth: резервный метод (internal_users.yml)


Авторизация через roles_mapping.yml → roles.yml
  1. API Gateway перехватывает запрос и выполняет auth_request к сервису аутентификации Памир.
  2. Auth-сервис аутентифицирует пользователя и возвращает заголовки X-User-Login и X-User-Opensearch-Role.
  3. nginx пробрасывает эти заголовки в бэкенд-сервис (например, OpenSearch Dashboards или напрямую в OpenSearch).
  4. Плагин безопасности OpenSearch принимает заголовки как доверенные (proxy auth domain) и определяет права пользователя через roles_mapping.yml.
Безопасность proxy auth

Метод proxy_auth безопасен только при условии, что порт OpenSearch (9200) недоступен напрямую из сети — только через API Gateway. Иначе любой клиент сможет подделать заголовки и получить произвольные права.

Встроенная конфигурация из коробки

Конфигурация плагина безопасности интегрирована в образ Opensearch и состоит из четырёх файлов.

Конфигурация аутентификации (config.yml)

Определяет цепочку аутентификации с двумя доменами:

config.yml
config:
dynamic:
http:
anonymous_auth_enabled: false
xff:
enabled: true
internalProxies: '10\.\d+\.\d+\.\d+|172\.(1[6-9]|2\d|3[01])\.\d+\.\d+|192\.168\.\d+\.\d+'
remoteIpHeader: "x-forwarded-for"

authc:
# Основной метод — заголовки от API Gateway
proxy_auth_domain:
http_enabled: true
order: 0
http_authenticator:
type: proxy
challenge: false
config:
user_header: "x-user-login"
roles_header: "x-user-opensearch-role"
authentication_backend:
type: noop

# Резервный метод — HTTP Basic Auth по internal_users.yml
basic_internal_auth_domain:
http_enabled: true
transport_enabled: true
order: 1
http_authenticator:
type: basic
challenge: false
authentication_backend:
type: intern

authz: {}

OpenSearch проверяет домены последовательно. Если первый домен (proxy_auth_domain) не обнаружил заголовок x-user-login, управление передаётся второму домену (basic_internal_auth_domain), который проверяет HTTP Basic Auth по базе внутренних пользователей.

Внутренние пользователи (internal_users.yml)

Внутренние пользователи используются для резервного доступа (Basic Auth) и сервисных учётных записей компонентов системы.

ПользовательНазначение
pamir-root-userСуперпользователь. Только для аварийного доступа.
pamir-admin-userТехнический администратор кластера.
pamir-ib-userАдминистратор информационной безопасности.
pamir-developer-userРазработчик / сервисная УЗ
pamir-analytic-userАналитик (только чтение).
pamir-kibana-userСервисная УЗ для OpenSearch Dashboards.
pamir-logstash-userСервисная УЗ для Logstash.
pamir-flb-userСервисная УЗ для Fluent Bit.
pamir-migrator-userСервисная УЗ для применения миграций.
pamir-hc-userСервисная УЗ для Docker healthcheck.
pamir-exporter-userСервисная УЗ для экспортера Elasticsearch Exporter.

Пароли хранятся в виде bcrypt-хэшей (cost factor 12). Для генерации хэша нового пароля:

# Инструмент OpenSearch
plugins/opensearch-security/tools/hash.sh -p <password>

# Или Python
python3 -c "import bcrypt; print(bcrypt.hashpw(b'password', bcrypt.gensalt(12)).decode())"

Роли (roles.yml)

Определяет 8 ролей с детальными правами на уровне кластера, индексов и тенантов Dashboards.

РольНазначениеДоступ к индексам
pamir_rootСуперпользовательВсе индексы (*) — полный доступ
pamir_adminАдминистратор кластераpamir-app-*, pamir-sys-*, .kibana*, .opensearch-dashboards* — полный доступ
pamir_admin_ibАдминистратор ИБpamir-sec-*, security-auditlog*, audit-*, .opendistro_security, индексы alerting/anomaly/reports
pamir_developerРазработчик / CI-CDpamir-app-*, .kibana* — полный доступ; управление шаблонами и пайплайнами
pamir_analyticАналитикpamir-app-*, .kibana* — только чтение и поиск
pamir_serviceСервисные компоненты (Logstash, Fluent Bit, миграции)Все индексы — запись и создание; без удаления и администрирования кластера
pamir_healthcheckDocker healthcheckТолько cluster:monitor/health
pamir_exporterЭкспортер метрикМониторинг кластера и индексов (read-only)

Пример определения роли аналитика:

roles.yml (фрагмент)
pamir_analytic:
cluster_permissions:
- "cluster:monitor/health"
- "cluster:monitor/state"
- "cluster:monitor/nodes/info"
index_permissions:
- index_patterns:
- "pamir-app-*"
- ".kibana*"
- ".opensearch-dashboards*"
allowed_actions:
- "read"
- "search"
- "get"
- "indices:data/read/*"
- "indices:monitor/settings/get"
- "indices:admin/mappings/get"
tenant_permissions:
- tenant_patterns:
- "global_tenant"
allowed_actions:
- "kibana_all_read"

Маппинг ролей (roles_mapping.yml)

Связывает пользователей и backend-роли (из заголовка X-User-Opensearch-Role) с ролями OpenSearch.

roles_mapping.yml (фрагмент)
pamir_admin:
users:
- "pamir-admin-user" # внутренний пользователь (Basic Auth)
backend_roles:
- "pamir_admin" # роль из заголовка X-User-Opensearch-Role (proxy auth)
# AD/LDAP: - "CN=pamir_admins,OU=Groups,DC=example,DC=com"
hosts: []

pamir_service:
users:
- "pamir-migrator-user"
- "pamir-logstash-user"
- "pamir-flb-user"
backend_roles: []
hosts: []

Поддерживаемые типы привязки:

  • users — конкретные внутренние пользователи OpenSearch (Basic Auth).
  • backend_roles — значение заголовка X-User-Opensearch-Role (proxy auth), либо роли из внешних IdP (LDAP/AD-группы, SAML claims, JWT).
  • hosts — IP-адреса или FQDN хостов (опционально, для ограничения по сети).

Интеграция с ролевой моделью Памир

Интеграция реализована через конфигурацию API Gateway (nginx). Для сервиса, который должен проверять подлинность запросов, в docker-compose выставляются следующие labels:

labels:
# Включить аутентификацию через auth-service
com.github.nginx-proxy.nginx-proxy.expose-service.auth: "true"
# Пробрасывать заголовки аутентификации в бэкенд
com.github.nginx-proxy.nginx-proxy.expose-service.auth-headers: "true"

При обращении к такому сервису nginx выполняет следующую последовательность:

# Внутренний location для запроса к auth-service
location = /_pamir_auth {
internal;
proxy_pass http://<auth-service>:<port>/v3/internal/user;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}

# Location сервиса с включённой аутентификацией
location ^~ /<service-path>/ {
auth_request /_pamir_auth;

# Извлечь заголовки из ответа auth-service
auth_request_set $p_user $upstream_http_x_user_login;
auth_request_set $p_roles $upstream_http_x_user_opensearch_role;

# Пробросить в бэкенд
proxy_set_header X-User-Login $p_user;
proxy_set_header X-User-Opensearch-Role $p_roles;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_pass http://<backend>;
}

Auth-сервис Памир обнаруживается автоматически по label контейнера:

labels:
com.github.nginx-proxy.nginx-proxy.auth-service.enable: "true"
com.github.nginx-proxy.nginx-proxy.auth-service.port: "80" # по умолчанию 80
com.github.nginx-proxy.nginx-proxy.auth-service.path: "/v3/internal/user" # по умолчанию

Итоговый поток данных при аутентификации пользователя через Памир:

  1. Auth-сервис Памир возвращает в ответ заголовки X-User-Login (логин пользователя) и X-User-Opensearch-Role (роль, например pamir_admin).
  2. nginx пробрасывает эти заголовки к OpenSearch.
  3. Плагин безопасности OpenSearch принимает значение X-User-Opensearch-Role как backend_role.
  4. Через roles_mapping.yml backend-роль маппируется на роль OpenSearch (например pamir_admin).
  5. Роль OpenSearch определяет допустимые операции согласно roles.yml.

Изменение конфигурации

Механизм применения (bootstrap)

Конфигурация плагина безопасности применяется автоматически при первом запуске контейнера через инструмент securityadmin.sh. Логика реализована в opensearch-docker-entrypoint.sh:

  1. Если файл-маркер $OPENSEARCH_HOME/data/.pamir_security_initialized отсутствует — выполняется bootstrap.
  2. OpenSearch запускается в фоне.
  3. Ожидается открытие transport-порта (9300) локальной ноды.
  4. Ожидается достижение кворума manager-нод (большинство от PAMIR_MANAGER_HOSTS).
  5. Ожидается готовность хотя бы одной data-ноды (если задан PAMIR_DATA_HOSTS).
  6. Последовательно применяются файлы конфигурации через securityadmin.sh: config.ymlaudit.ymlroles.ymlinternal_users.ymlroles_mapping.yml.
  7. После успеха создаётся маркер — при последующих запусках bootstrap пропускается.

Переменные окружения, управляющие bootstrap:

ПеременнаяПо умолчаниюОписание
PAMIR_SKIP_SECURITY_INITfalseПропустить bootstrap полностью
PAMIR_SECURITY_CONFIG_DIR$OPENSEARCH_PATH_CONF/pamir-securityПуть к директории с конфигами
PAMIR_MANAGER_HOSTSlocalhostСписок manager-нод через запятую
PAMIR_DATA_HOSTS(не задан)Список data-нод через запятую (если manager ≠ data)
OPENSEARCH_STARTUP_TIMEOUT120Таймаут ожидания старта ноды (сек)
OPENSEARCH_CLUSTER_TIMEOUT300Таймаут ожидания кворума (сек)
PAMIR_DATA_TIMEOUT300Таймаут ожидания data-ноды (сек)
PAMIR_SHARD_WAIT15Пауза после появления data-ноды (сек)
PAMIR_SA_RETRIES10Количество попыток применения каждого файла
PAMIR_SA_RETRY_WAIT20Интервал между попытками (сек)

Сертификаты для securityadmin.sh определяются автоматически:

  • Пользовательские (приоритет): pamir-admin.pem, pamir-admin-key.pem, pamir-root-ca.pem в директории конфигурации OpenSearch.
  • Demo-сертификаты (fallback): kirk.pem, kirk-key.pem, root-ca.pem.
warning

Использование demo-сертификатов не рекомендуется в production. Подробнее о получении пользовательских сертификатов см. в разделе Конфигурация кластера.

Изменение ролей, пользователей или маппинга

Для изменения конфигурации безопасности необходимо:

Вариант 1: Пересоздать контейнер (рекомендуется)

Конфигурация безопасности встроена в образ контейнера OpenSearch (путь внутри образа: /usr/share/opensearch/config/pamir-security/). Чтобы переопределить её, необходимо смонтировать изменённые файлы через volume.

  1. Скопировать текущую конфигурацию из образа в рабочий каталог:

    mkdir -p data/opensearch/security-config
    docker compose cp \
    opensearch:/usr/share/opensearch/config/pamir-security/. \
    data/opensearch/security-config/
  2. Внести нужные изменения в скопированные файлы в data/opensearch/security-config/.

  3. Смонтировать изменённую директорию в сервис через docker-compose.additional.yml:

    docker-compose.additional.yml
    services:
    opensearch:
    volumes:
    - ./data/opensearch/security-config:/usr/share/opensearch/config/pamir-security:ro
  4. Удалить файл-маркер инициализации внутри Docker volume:

    docker compose exec opensearch \
    rm /usr/share/opensearch/data/.pamir_security_initialized
  5. Перезапустить контейнер OpenSearch:

    pamirctl start opensearch

    При следующем запуске bootstrap применит конфигурацию из смонтированной директории.

Вариант 2: Применить вручную через securityadmin.sh

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

docker compose exec opensearch bash \
/usr/share/opensearch/plugins/opensearch-security/tools/securityadmin.sh \
-icl -nhnv \
--accept-red-cluster \
-cacert /usr/share/opensearch/config/root-ca.pem \
-cert /usr/share/opensearch/config/kirk.pem \
-key /usr/share/opensearch/config/kirk-key.pem \
-f /usr/share/opensearch/config/pamir-security/roles_mapping.yml \
-t rolesmapping

Допустимые значения параметра -t (тип конфигурации):

ТипФайл
configconfig.yml
auditaudit.yml
rolesroles.yml
internalusersinternal_users.yml
rolesmappingroles_mapping.yml

Вариант 3: REST API OpenSearch Dashboards

Конфигурацию безопасности можно изменить через REST API Security плагина от имени пользователя с ролью pamir_root или pamir_admin_ib. Например, создать нового внутреннего пользователя:

curl -X PUT "https://localhost:9200/_plugins/_security/api/internalusers/new-user" \
-u "pamir-root-user:<password>" \
-k \
-H "Content-Type: application/json" \
-d '{
"password": "SecurePassword123!",
"backend_roles": [],
"attributes": {
"description": "New user"
}
}'
примечание

Изменения через REST API вступают в силу немедленно, но не отражаются в файлах security-config/. При следующем bootstrap (пересоздании контейнера) конфигурация из файлов перезапишет изменения.

Добавление новой роли

  1. Если ещё не сделано, подготовить локальную копию конфигурации согласно Варианту 1.

  2. Добавить определение роли в data/opensearch/security-config/roles.yml:

    my_custom_role:
    cluster_permissions:
    - "cluster:monitor/health"
    index_permissions:
    - index_patterns:
    - "pamir-app-myservice-*"
    allowed_actions:
    - "read"
    - "search"
    tenant_permissions: []
  3. Добавить маппинг в data/opensearch/security-config/roles_mapping.yml:

    my_custom_role:
    users: []
    backend_roles:
    - "my_custom_role" # значение заголовка X-User-Opensearch-Role
    hosts: []
  4. Применить конфигурацию одним из описанных выше способов.

Интеграция с внешним IdP (LDAP/AD, SAML, OIDC)

Для привязки ролей OpenSearch к группам внешнего каталога укажите DN группы в backend_roles маппинга. Значение должно совпадать с тем, что передаёт IdP в заголовке X-User-Opensearch-Role или в атрибутах LDAP/SAML/JWT.

Пример маппинга на AD-группу:

roles_mapping.yml (фрагмент)
pamir_admin:
users:
- "pamir-admin-user"
backend_roles:
- "pamir_admin"
- "CN=pamir_admins,OU=Groups,DC=example,DC=com"
hosts: []

Связанные разделы