Ролевая модель 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
- API Gateway перехватывает запрос и выполняет
auth_requestк сервису аутентификации Памир. - Auth-сервис аутентифицирует пользователя и возвращает заголовки
X-User-LoginиX-User-Opensearch-Role. - nginx пробрасывает эти заголовки в бэкенд-сервис (например, OpenSearch Dashboards или напрямую в OpenSearch).
- Плагин безопасности OpenSearch принимает заголовки как доверенные (proxy auth domain) и определяет права
пользователя через
roles_mapping.yml.
Метод proxy_auth безопасен только при условии, что порт OpenSearch (9200) недоступен напрямую из сети —
только через API Gateway. Иначе любой клиент сможет подделать заголовки и получить произвольные права.
Встроенная конфигурация из коробки
Конфигурация плагина безопасности интегрирована в образ Opensearch и состоит из четырёх файлов.
Конфигурация аутентификации (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-CD | pamir-app-*, .kibana* — полный доступ; управление шаблонами и пайплайнами |
pamir_analytic | Аналитик | pamir-app-*, .kibana* — только чтение и поиск |
pamir_service | Сервисные компоненты (Logstash, Fluent Bit, миграции) | Все индексы — запись и создание; без удаления и администрирования кластера |
pamir_healthcheck | Docker healthcheck | Только cluster:monitor/health |
pamir_exporter | Экспортер метрик | Мониторинг кластера и индексов (read-only) |
Пример определения роли аналитика:
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.
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" # по умолчанию
Итоговый поток данных при аутентификации пользователя через Памир:
- Auth-сервис Памир возвращает в ответ заголовки
X-User-Login(логин пользователя) иX-User-Opensearch-Role(роль, напримерpamir_admin). - nginx пробрасывает эти заголовки к OpenSearch.
- Плагин безопасности OpenSearch принимает значение
X-User-Opensearch-Roleкакbackend_role. - Через
roles_mapping.ymlbackend-роль маппируется на роль OpenSearch (напримерpamir_admin). - Роль OpenSearch определяет допустимые операции согласно
roles.yml.
Изменение конфигурации
Механизм применения (bootstrap)
Конфигурация плагина безопасности применяется автоматически при первом запуске контейнера
через инструмент securityadmin.sh. Логика реализована в opensearch-docker-entrypoint.sh:
- Если файл-маркер
$OPENSEARCH_HOME/data/.pamir_security_initializedотсутствует — выполняется bootstrap. - OpenSearch запускается в фоне.
- Ожидается открытие transport-порта (9300) локальной ноды.
- Ожидается достижение кворума manager-нод (большинство от
PAMIR_MANAGER_HOSTS). - Ожидается готовность хотя бы одной data-ноды (если задан
PAMIR_DATA_HOSTS). - Последовательно применяются файлы конфигурации через
securityadmin.sh:config.yml→audit.yml→roles.yml→internal_users.yml→roles_mapping.yml. - После успеха создаётся маркер — при последующих запусках bootstrap пропускается.
Переменные окружения, управляющие bootstrap:
| Переменная | По умолчанию | Описание |
|---|---|---|
PAMIR_SKIP_SECURITY_INIT | false | Пропустить bootstrap полностью |
PAMIR_SECURITY_CONFIG_DIR | $OPENSEARCH_PATH_CONF/pamir-security | Путь к директории с конфигами |
PAMIR_MANAGER_HOSTS | localhost | Список manager-нод через запятую |
PAMIR_DATA_HOSTS | (не задан) | Список data-нод через запятую (если manager ≠ data) |
OPENSEARCH_STARTUP_TIMEOUT | 120 | Таймаут ожидания старта ноды (сек) |
OPENSEARCH_CLUSTER_TIMEOUT | 300 | Таймаут ожидания кворума (сек) |
PAMIR_DATA_TIMEOUT | 300 | Таймаут ожидания data-ноды (сек) |
PAMIR_SHARD_WAIT | 15 | Пауза после появления data-ноды (сек) |
PAMIR_SA_RETRIES | 10 | Количество попыток применения каждого файла |
PAMIR_SA_RETRY_WAIT | 20 | Интервал между попытками (сек) |
Сертификаты для securityadmin.sh определяются автоматически:
- Пользовательские (приоритет):
pamir-admin.pem,pamir-admin-key.pem,pamir-root-ca.pemв директории конфигурации OpenSearch. - Demo-сертификаты (fallback):
kirk.pem,kirk-key.pem,root-ca.pem.
Использование demo-сертификатов не рекомендуется в production. Подробнее о получении пользовательских сертификатов см. в разделе Конфигурация кластера.
Изменение ролей, пользователей или маппинга
Для изменения конфигурации безопасности необходимо:
Вариант 1: Пересоздать контейнер (рекомендуется)
Конфигурация безопасности встроена в образ контейнера OpenSearch (путь внутри образа:
/usr/share/opensearch/config/pamir-security/). Чтобы переопределить её, необходимо
смонтировать изменённые файлы через volume.
-
Скопировать текущую конфигурацию из образа в рабочий каталог:
mkdir -p data/opensearch/security-config
docker compose cp \
opensearch:/usr/share/opensearch/config/pamir-security/. \
data/opensearch/security-config/ -
Внести нужные изменения в скопированные файлы в
data/opensearch/security-config/. -
Смонтировать изменённую директорию в сервис через
docker-compose.additional.yml:docker-compose.additional.ymlservices:
opensearch:
volumes:
- ./data/opensearch/security-config:/usr/share/opensearch/config/pamir-security:ro -
Удалить файл-маркер инициализации внутри Docker volume:
docker compose exec opensearch \
rm /usr/share/opensearch/data/.pamir_security_initialized -
Перезапустить контейнер 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 (тип конфигурации):
| Тип | Файл |
|---|---|
config | config.yml |
audit | audit.yml |
roles | roles.yml |
internalusers | internal_users.yml |
rolesmapping | roles_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.
-
Добавить определение роли в
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: [] -
Добавить маппинг в
data/opensearch/security-config/roles_mapping.yml:my_custom_role:
users: []
backend_roles:
- "my_custom_role" # значение заголовка X-User-Opensearch-Role
hosts: [] -
Применить конфигурацию одним из описанных выше способов.
Интеграция с внешним IdP (LDAP/AD, SAML, OIDC)
Для привязки ролей OpenSearch к группам внешнего каталога укажите DN группы в backend_roles
маппинга. Значение должно совпадать с тем, что передаёт IdP в заголовке X-User-Opensearch-Role
или в атрибутах LDAP/SAML/JWT.
Пример маппинга на AD-группу:
pamir_admin:
users:
- "pamir-admin-user"
backend_roles:
- "pamir_admin"
- "CN=pamir_admins,OU=Groups,DC=example,DC=com"
hosts: []