Перейти к основному содержимому
Версия: 2.4

Ролевая модель 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)​

Развертывание в HA

Для работы bootstrap в HA-развертываниях OS требуется настройка переменных окружения. Процесс настройки описан в начале документа Opensearch HA.

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

  1. При каждом старте security-конфиги синхронизируются из неизменяемой копии образа (/usr/share/opensearch/pamir-security-dist) в рабочий каталог $OPENSEARCH_PATH_CONF/pamir-security (который лежит в volume pamir_opensearch-config и перекрывает образ). Затем вычисляется контрольная сумма (checksum) фактического содержимого конфигов.
  2. Bootstrap выполняется, если файл-маркер $OPENSEARCH_HOME/data/.pamir_security_initialized отсутствует (первый запуск) или checksum изменился относительно записанного в маркере (обновление образа или ручная правка конфигов). Иначе — пропускается.
  3. OpenSearch запускается в фоне.
  4. Ожидается открытие transport-порта (9300) локальной ноды.
  5. Ожидается достижение кворума manager-нод (большинство от PAMIR_MANAGER_HOSTS).
  6. Ожидается готовность хотя бы одной data-ноды (если задан PAMIR_DATA_HOSTS).
  7. Последовательно применяются файлы конфигурации через securityadmin.sh: config.yml → audit.yml → roles.yml → internal_users.yml → roles_mapping.yml.
  8. После успеха в маркер записывается новый checksum — при последующих запусках без изменений bootstrap пропускается.
Автоматическое применение при обновлении

Благодаря синхронизации из образа и сравнению checksum обновление образа OpenSearch автоматически переприменяет security-конфигурацию — ручное удаление volume pamir_opensearch-config и файла-маркера больше не требуется. Это же касается ручных правок конфигов (см. Вариант 1). Стенды со старого образа (маркер без checksum) переприменяют конфигурацию один раз при первом апгрейде автоматически. Синхронизируется только подкаталог pamir-security — сгенерированные сертификаты и opensearch.yml в volume не затрагиваются.

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

ПеременнаяПо умолчаниюОписание
PAMIR_SKIP_SECURITY_INITfalseПропустить bootstrap полностью
PAMIR_SECURITY_AUTO_REAPPLYtrueАвто-переприменять security при изменении checksum. false — не переприменять (при наличии маркера всегда skip)
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. Подробнее о получении пользовательских сертификатов см. в разделе Конфигурация кластера.

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

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

Volume больше не нужно удалять вручную

Каталог конфигурации OpenSearch (/usr/share/opensearch/config) вынесен в Docker volume pamir_opensearch-config, так как контейнер работает в режиме read_only и не может писать в свою корневую файловую систему. Docker заполняет этот volume из образа только при первом создании, поэтому раньше при обновлении security-конфигурации volume приходилось удалять вручную.

Теперь подкаталог pamir-security автоматически синхронизируется из образа при каждом старте, а изменения применяются по контрольной сумме (см. Механизм применения). Поэтому при обновлении образа или правке смонтированных конфигов ручное удаление volume pamir_opensearch-config и файла-маркера больше не требуется — достаточно перезапустить контейнер. Ручная процедура полного сброса остаётся как fallback для нестандартных ситуаций (см. блок «Troubleshooting: полный сброс конфигурации» в Варианте 1).

Вариант 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. Смонтированный каталог задаётся переменной PAMIR_SECURITY_CONFIG_DIR, чтобы entrypoint использовал ваш источник (и не перезаписывал его конфигами из образа):

    docker-compose.additional.yml
    services:
    opensearch:
    environment:
    PAMIR_SECURITY_CONFIG_DIR: /pamir-security-override
    volumes:
    - ./data/opensearch/security-config:/pamir-security-override:ro
  4. Перезапустить контейнер OpenSearch:

    pamirctl restart opensearch

    При старте entrypoint вычислит checksum смонтированных файлов; так как он отличается от записанного в маркере, bootstrap автоматически переприменит обновлённую конфигурацию. Удалять volume и файл-маркер вручную не нужно.

Собственная конфигурация сохраняется при обновлении образа

Если вы принесли свою security-конфигурацию (например, задали собственные пароли в internal_users.yml), она не будет затёрта конфигурацией из нового образа. Entrypoint распознаёт пользовательский источник и пропускает синхронизацию из образа в двух случаях (достаточно любого):

  • задана переменная PAMIR_SECURITY_CONFIG_DIR (как в примере выше — рекомендуемый способ, работает при монтировании и :ro, и :rw);
  • каталог pamir-security смонтирован напрямую как bind mount (entrypoint определяет это по точке монтирования).

Ваши изменения при этом по-прежнему применяются: при правке файлов меняется checksum и bootstrap переприменяет конфигурацию. Простое обновление образа (без изменения ваших файлов) не трогает ваш каталог и не триггерит повторное применение.

Обратите внимание: при использовании собственной конфигурации новые роли/пермишены из обновлённого образа в неё не попадут автоматически — вам нужно вручную перенести нужные изменения из образа в свои файлы (сравнив их с эталоном в /usr/share/opensearch/pamir-security-dist/ внутри нового образа).

Troubleshooting: полный сброс конфигурации (fallback)

Если по какой-то причине автоматическое переприменение не сработало (повреждён volume, несовместимое состояние), можно выполнить полный ручной сброс: удалить volume конфигурации и файл-маркер, чтобы всё пересоздалось из образа с нуля.

  1. Остановить контейнер и удалить volume конфигурации:

    cd "$HOME/.pamir"
    # Зафиксировать образ OpenSearch ДО остановки — пригодится для следующего шага
    OS_IMAGE=$(docker inspect --format '{{.Config.Image}}' "$(docker compose ps -q opensearch)")

    docker compose stop opensearch
    docker volume rm pamir_opensearch-config
  2. Удалить файл-маркер инициализации. Маркер хранится в volume данных (pamir_opensearch-data), который не удаляется, поэтому снимаем его отдельно. Контейнер остановлен, поэтому используем вспомогательный контейнер на базе образа OpenSearch (он уже загружен — обращения к внешнему реестру не требуется):

    docker run --rm -v pamir_opensearch-data:/data \
    --entrypoint rm "$OS_IMAGE" \
    -f /usr/share/opensearch/data/.pamir_security_initialized
  3. Запустить контейнер OpenSearch:

    pamirctl start opensearch

    При следующем запуске volume конфигурации будет пересоздан из образа, а 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: []

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