Ролевая модель 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)
Для работы bootstrap в HA-развертываниях OS требуется настройка переменных окружения. Процесс настройки описан в начале документа Opensearch HA.
Конфигурация плагина безопасности применяется автоматически через инструмент
securityadmin.sh. Логика реализована в opensearch-docker-entrypoint.sh:
- При каждом старте security-конфиги синхронизируются из неизменяемой копии образа
(
/usr/share/opensearch/pamir-security-dist) в рабочий каталог$OPENSEARCH_PATH_CONF/pamir-security(который лежит в volumepamir_opensearch-configи перекрывает образ). Затем вычисляется контрольная сумма (checksum) фактического содержимого конфигов. - Bootstrap выполняется, если файл-маркер
$OPENSEARCH_HOME/data/.pamir_security_initializedотсутствует (первый запуск) или checksum изменился относительно записанного в маркере (обновление образа или ручная правка конфигов). Иначе — пропускается. - 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. - После успеха в маркер записывается новый checksum — при последующих запусках без изменений bootstrap пропускается.
Благодаря синхронизации из образа и сравнению checksum обновление образа OpenSearch
автоматически переприменяет security-конфигурацию — ручное удаление volume
pamir_opensearch-config и файла-маркера больше не требуется. Это же касается ручных
правок конфигов (см. Вариант 1).
Стенды со старого образа (маркер без checksum) переприменяют конфигурацию один раз при
первом апгрейде автоматически. Синхронизируется только подкаталог pamir-security —
сгенерированные сертификаты и opensearch.yml в volume не затрагиваются.
Переменные окружения, управляющие bootstrap:
| Переменная | По умолчанию | Описание |
|---|---|---|
PAMIR_SKIP_SECURITY_INIT | false | Пропустить bootstrap полностью |
PAMIR_SECURITY_AUTO_REAPPLY | true | Авто-переприменять security при изменении checksum. false — не переприменять (при наличии маркера всегда skip) |
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. Подробнее о получении пользовательских сертификатов см. в разделе Конфигурация кластера.
Изменение ролей, пользователей или маппинга
Для изменения конфигурации безопасности необходимо:
Каталог конфигурации 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.
-
Скопировать текущую конфигурацию из образа в рабочий каталог:
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. Смонтированный каталог задаётся переменнойPAMIR_SECURITY_CONFIG_DIR, чтобы entrypoint использовал ваш источник (и не перезаписывал его конфигами из образа):docker-compose.additional.ymlservices:
opensearch:
environment:
PAMIR_SECURITY_CONFIG_DIR: /pamir-security-override
volumes:
- ./data/opensearch/security-config:/pamir-security-override:ro -
Перезапустить контейнер 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 конфигурации и файл-маркер, чтобы всё пересоздалось из образа с нуля.
-
Остановить контейнер и удалить 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 -
Удалить файл-маркер инициализации. Маркер хранится в 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 -
Запустить контейнер 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 (тип конфигурации):
| Тип | Файл |
|---|---|
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: []