Wazuh Indexer API - запросы и работа с данными

Wazuh Indexer построен на базе OpenSearch и предоставляет REST API для поиска, анализа и управления данными безопасности. Через API можно выполнять DSL-запросы к алертам, строить агрегации для аналитических отчетов, использовать PPL и SQL для интерактивного анализа, а также управлять шаблонами индексов и маппингами полей. Понимание работы с Indexer API необходимо для построения кастомных дашбордов, автоматизации и интеграции Wazuh с внешними системами.

Индексные паттерны Wazuh

Wazuh Indexer хранит данные в нескольких типах индексов, каждый из которых имеет свою функцию и период ротации.

Основные индексы

ИндексНазначениеРотация
wazuh-alerts-*Алерты, сгенерированные при срабатывании правилЕжедневно
wazuh-archives-*Все события, включая не вызвавшие алертовЕжедневно
wazuh-monitoring-*Статусы подключения агентовЕженедельно
wazuh-statistics-*Метрики производительности сервера WazuhЕженедельно

Индексы состояния (State indices)

Начиная с Wazuh 4.14, данные инвентаризации и уязвимостей хранятся в отдельных индексах состояния:

ИндексСодержимое
wazuh-states-vulnerabilities-*Обнаруженные уязвимости и их критичность
wazuh-states-inventory-packages-*Установленное программное обеспечение
wazuh-states-inventory-processes-*Запущенные процессы
wazuh-states-inventory-ports-*Открытые сетевые порты
wazuh-states-inventory-hardware-*CPU, память, оборудование
wazuh-states-inventory-system-*ОС, архитектура, hostname
wazuh-states-inventory-networks-*Сетевые адреса IPv4/IPv6

Просмотр индексов

Список всех индексов Wazuh:

curl -sk -u admin:$PASSWORD \
  "https://localhost:9200/_cat/indices/wazuh-*?v&s=index"

Размер конкретного индекса:

curl -sk -u admin:$PASSWORD \
  "https://localhost:9200/_cat/indices/wazuh-alerts-*?v&h=index,docs.count,store.size&s=index"

Поиск алертов с помощью DSL-запросов

OpenSearch Query DSL (Domain Specific Language) - основной язык запросов для поиска данных в Wazuh Indexer.

Базовый поиск (match)

Поиск алертов по описанию правила:

GET wazuh-alerts-*/_search
{
  "size": 10,
  "query": {
    "match": {
      "rule.description": "authentication failure"
    }
  },
  "sort": [
    { "timestamp": { "order": "desc" } }
  ]
}

Точное совпадение (term)

Поиск алертов определенного уровня:

GET wazuh-alerts-*/_search
{
  "query": {
    "term": {
      "rule.level": 12
    }
  }
}

Составной запрос (bool)

Комбинирование условий с помощью must, should, must_not и filter:

GET wazuh-alerts-*/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "rule.groups": "authentication_failed" } }
      ],
      "filter": [
        { "range": { "timestamp": { "gte": "now-24h" } } },
        { "term": { "agent.name": "web-server-01" } }
      ],
      "must_not": [
        { "term": { "rule.level": 3 } }
      ]
    }
  },
  "sort": [{ "timestamp": "desc" }],
  "size": 50
}

Диапазонный запрос (range)

Поиск алертов за определенный период:

GET wazuh-alerts-*/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "range": {
            "timestamp": {
              "gte": "2025-01-01T00:00:00",
              "lte": "2025-01-31T23:59:59",
              "format": "yyyy-MM-dd'T'HH:mm:ss"
            }
          }
        },
        {
          "range": {
            "rule.level": { "gte": 10 }
          }
        }
      ]
    }
  }
}

Поиск по вложенным полям

Wazuh хранит данные MITRE ATT&CK в структурированных полях:

GET wazuh-alerts-*/_search
{
  "query": {
    "bool": {
      "must": [
        { "term": { "rule.mitre.id": "T1110" } },
        { "term": { "rule.mitre.tactic": "Credential Access" } }
      ]
    }
  },
  "_source": ["timestamp", "rule.description", "rule.level", "agent.name"]
}

Wildcard и regex

Поиск с использованием подстановочных символов:

GET wazuh-alerts-*/_search
{
  "query": {
    "wildcard": {
      "data.srcip": "192.168.1.*"
    }
  }
}

Поиск с регулярным выражением:

GET wazuh-alerts-*/_search
{
  "query": {
    "regexp": {
      "data.url": ".*\\.(php|asp|jsp)\\?.*id=.*"
    }
  }
}

Агрегации

Агрегации позволяют получать статистику и аналитические отчеты по данным Wazuh без необходимости выгружать все документы.

Terms - топ значений

Топ-10 правил, которые срабатывали чаще всего:

GET wazuh-alerts-*/_search
{
  "size": 0,
  "query": {
    "range": { "timestamp": { "gte": "now-7d" } }
  },
  "aggs": {
    "top_rules": {
      "terms": {
        "field": "rule.id",
        "size": 10,
        "order": { "_count": "desc" }
      },
      "aggs": {
        "rule_description": {
          "terms": { "field": "rule.description.keyword", "size": 1 }
        }
      }
    }
  }
}

Date histogram - временная шкала

Количество алертов по часам за последние сутки:

GET wazuh-alerts-*/_search
{
  "size": 0,
  "query": {
    "range": { "timestamp": { "gte": "now-24h" } }
  },
  "aggs": {
    "alerts_over_time": {
      "date_histogram": {
        "field": "timestamp",
        "fixed_interval": "1h"
      },
      "aggs": {
        "by_level": {
          "range": {
            "field": "rule.level",
            "ranges": [
              { "key": "low", "from": 0, "to": 7 },
              { "key": "medium", "from": 7, "to": 12 },
              { "key": "critical", "from": 12, "to": 16 }
            ]
          }
        }
      }
    }
  }
}

Cardinality - уникальные значения

Количество уникальных агентов, IP-адресов и правил:

GET wazuh-alerts-*/_search
{
  "size": 0,
  "query": {
    "range": { "timestamp": { "gte": "now-24h" } }
  },
  "aggs": {
    "unique_agents": {
      "cardinality": { "field": "agent.id" }
    },
    "unique_source_ips": {
      "cardinality": { "field": "data.srcip" }
    },
    "unique_rules": {
      "cardinality": { "field": "rule.id" }
    }
  }
}

Вложенные агрегации

Топ-5 агентов с разбивкой по тактикам MITRE ATT&CK:

GET wazuh-alerts-*/_search
{
  "size": 0,
  "aggs": {
    "by_agent": {
      "terms": { "field": "agent.name", "size": 5 },
      "aggs": {
        "by_tactic": {
          "terms": { "field": "rule.mitre.tactic", "size": 5 }
        },
        "max_level": {
          "max": { "field": "rule.level" }
        }
      }
    }
  }
}

Шаблоны индексов и маппинги полей

Просмотр текущего шаблона

curl -sk -u admin:$PASSWORD \
  "https://localhost:9200/_template/wazuh?pretty"

Основные поля Wazuh-алертов

ПолеТипОписание
timestampdateВремя события
rule.idkeywordИдентификатор правила
rule.levelintegerУровень критичности (0-15)
rule.descriptiontext/keywordОписание правила
rule.groupskeywordГруппы правила
rule.mitre.idkeywordMITRE ATT&CK technique ID
rule.mitre.tactickeywordMITRE ATT&CK тактика
agent.idkeywordИдентификатор агента
agent.namekeywordИмя агента
agent.ipipIP-адрес агента
data.srcipipIP-адрес источника
data.dstipipIP-адрес назначения
data.srcportintegerПорт источника
data.dstportintegerПорт назначения
locationkeywordИсточник лога
full_logtextПолный текст лог-записи

Обновление шаблона

Для добавления кастомного индексного паттерна:

# Скачать текущий шаблон
curl -so template.json \
  "https://raw.githubusercontent.com/wazuh/wazuh/v4.14.4/extensions/elasticsearch/7.x/wazuh-template.json"

# Добавить кастомный паттерн в index_patterns
# Загрузить обновленный шаблон
curl -sk -u admin:$PASSWORD \
  -XPUT "https://localhost:9200/_template/wazuh-custom" \
  -H "Content-Type: application/json" \
  -d @template.json

Конфликты маппингов

Если поле имеет разные типы в разных индексах, возникает конфликт маппинга. Проверка:

curl -sk -u admin:$PASSWORD \
  "https://localhost:9200/wazuh-alerts-*/_mapping?pretty" | \
  jq '.. | .type? // empty' | sort | uniq -c | sort -rn

Dev Tools - консоль в Dashboard

Wazuh Dashboard включает консоль Dev Tools (унаследованную от OpenSearch Dashboards), которая позволяет выполнять API-запросы без использования curl.

Доступ к Dev Tools

  1. Откройте Wazuh Dashboard
  2. Перейдите в меню OpenSearch Dashboards - Dev Tools
  3. Или используйте URL: https://<dashboard>:443/app/dev_tools#/console

Примеры использования

В консоли Dev Tools не нужно указывать URL и аутентификацию:

# Проверить состояние кластера
GET _cluster/health

# Поиск последних критических алертов
GET wazuh-alerts-*/_search
{
  "size": 5,
  "query": {
    "bool": {
      "filter": [
        { "range": { "rule.level": { "gte": 12 } } },
        { "range": { "timestamp": { "gte": "now-1h" } } }
      ]
    }
  },
  "sort": [{ "timestamp": "desc" }]
}

# Статистика по агентам
GET wazuh-alerts-*/_search
{
  "size": 0,
  "aggs": {
    "agents": {
      "terms": { "field": "agent.name", "size": 20 }
    }
  }
}

Автодополнение

Dev Tools поддерживает автодополнение имен индексов, полей и ключевых слов DSL. Используйте Ctrl+Space для вызова подсказок.

PPL - Piped Processing Language

PPL предоставляет синтаксис, аналогичный Unix-пайпам, для запросов к данным. Удобен для аналитиков, знакомых с Splunk SPL.

Базовый синтаксис

source = wazuh-alerts-* | where rule.level >= 10 | sort - timestamp | head 20

Фильтрация и агрегация

source = wazuh-alerts-*
| where timestamp > '2025-01-01 00:00:00'
| where rule.level >= 7
| stats count() as alert_count by rule.id, rule.description
| sort - alert_count
| head 10

Выполнение PPL через API

curl -sk -u admin:$PASSWORD \
  -XPOST "https://localhost:9200/_plugins/_ppl" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "source = wazuh-alerts-* | where rule.level >= 12 | stats count() by agent.name | sort - count()"
  }'

Временные фильтры в PPL

source = wazuh-alerts-*
| where timestamp > DATE_SUB(NOW(), INTERVAL 24 HOUR)
| stats count() as alerts by span(timestamp, 1h) as hour
| sort hour

Группировка с несколькими метриками

source = wazuh-alerts-*
| where timestamp > DATE_SUB(NOW(), INTERVAL 7 DAY)
| stats count() as total, max(rule.level) as max_level, dc(agent.id) as agents
  by rule.mitre.tactic
| sort - total

SQL Plugin

OpenSearch SQL plugin позволяет использовать привычный SQL-синтаксис для запросов к данным Wazuh.

Базовые запросы

SELECT timestamp, rule.id, rule.level, rule.description, agent.name
FROM wazuh-alerts-*
WHERE rule.level >= 10
  AND timestamp > NOW() - INTERVAL 24 HOUR
ORDER BY timestamp DESC
LIMIT 50

Агрегации в SQL

SELECT rule.id, rule.description, COUNT(*) as count
FROM wazuh-alerts-*
WHERE timestamp > NOW() - INTERVAL 7 DAY
GROUP BY rule.id, rule.description
ORDER BY count DESC
LIMIT 10

Выполнение SQL через API

curl -sk -u admin:$PASSWORD \
  -XPOST "https://localhost:9200/_plugins/_sql" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "SELECT agent.name, COUNT(*) as alerts FROM wazuh-alerts-* WHERE rule.level >= 7 GROUP BY agent.name ORDER BY alerts DESC"
  }'

Перевод SQL в DSL

Полезно для отладки - преобразование SQL в эквивалентный DSL-запрос:

curl -sk -u admin:$PASSWORD \
  -XPOST "https://localhost:9200/_plugins/_sql/_explain" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "SELECT * FROM wazuh-alerts-* WHERE rule.level >= 12 LIMIT 10"
  }'

Bulk-операции

Массовые операции полезны для управления данными и автоматизации.

Bulk-поиск (msearch)

Выполнение нескольких запросов за один вызов:

curl -sk -u admin:$PASSWORD \
  -XPOST "https://localhost:9200/_msearch" \
  -H "Content-Type: application/x-ndjson" \
  -d '
{"index":"wazuh-alerts-*"}
{"size":0,"query":{"range":{"rule.level":{"gte":12}}},"aggs":{"count":{"value_count":{"field":"_id"}}}}
{"index":"wazuh-alerts-*"}
{"size":0,"query":{"range":{"timestamp":{"gte":"now-1h"}}},"aggs":{"count":{"value_count":{"field":"_id"}}}}
'

Scroll API для больших выборок

При необходимости получить более 10 000 документов используйте Scroll API:

# Инициализация scroll
curl -sk -u admin:$PASSWORD \
  -XPOST "https://localhost:9200/wazuh-alerts-*/_search?scroll=5m" \
  -H "Content-Type: application/json" \
  -d '{
    "size": 1000,
    "query": { "range": { "timestamp": { "gte": "now-30d" } } },
    "sort": [{ "timestamp": "asc" }]
  }'

# Продолжение scroll (использовать _scroll_id из ответа)
curl -sk -u admin:$PASSWORD \
  -XPOST "https://localhost:9200/_search/scroll" \
  -H "Content-Type: application/json" \
  -d '{
    "scroll": "5m",
    "scroll_id": "<SCROLL_ID>"
  }'

Удаление по запросу (delete by query)

Удаление старых алертов низкого уровня:

curl -sk -u admin:$PASSWORD \
  -XPOST "https://localhost:9200/wazuh-alerts-*/_delete_by_query" \
  -H "Content-Type: application/json" \
  -d '{
    "query": {
      "bool": {
        "filter": [
          { "range": { "timestamp": { "lte": "now-90d" } } },
          { "range": { "rule.level": { "lte": 3 } } }
        ]
      }
    }
  }'

Сравнение с другими SIEM-системами

Elasticsearch Query DSL

Wazuh Indexer (OpenSearch) использует синтаксис, полностью совместимый с Elasticsearch 7.x Query DSL. Основные отличия:

ФункцияWazuh Indexer (OpenSearch)Elasticsearch
Базовый DSLИдентичный синтаксисИдентичный синтаксис
SQL Plugin_plugins/_sql_sql (X-Pack)
PPL_plugins/_pplОтсутствует
Alerting_plugins/_alertingX-Pack Watcher
БезопасностьSecurity Plugin (встроено)X-Pack Security (платно)

Миграция запросов из Elasticsearch в Wazuh Indexer не требует изменений в DSL-синтаксисе.

Splunk SPL

ЗадачаWazuh PPLSplunk SPL
Поискsource = index | where field = 'value'index=main field=value
Агрегацияstats count() by fieldstats count by field
Сортировкаsort - fieldsort - field
Лимитhead Nhead N
Временной фильтрwhere timestamp > ...earliest=-24h

PPL в OpenSearch наиболее близок к SPL по синтаксису, что упрощает миграцию для аналитиков из Splunk.

QRadar AQL

ЗадачаWazuh SQLQRadar AQL
Поиск событийSELECT * FROM wazuh-alerts-*SELECT * FROM events
ФильтрацияWHERE rule.level >= 10WHERE severity >= 7
АгрегацияGROUP BY rule.idGROUP BY qid
Временной фильтрWHERE timestamp > NOW() - INTERVAL 1 HOURWHERE starttime > NOW - 1 HOURS

SQL-синтаксис в OpenSearch близок к стандартному SQL, что упрощает адаптацию для пользователей QRadar.

Устранение неполадок

Медленные запросы

Симптом: запросы выполняются дольше 10 секунд.

Диагностика:

# Проверка нагрузки на кластер
curl -sk -u admin:$PASSWORD "https://localhost:9200/_nodes/stats/os,jvm?pretty"

# Проверка медленных запросов (включить slow log)
curl -sk -u admin:$PASSWORD \
  -XPUT "https://localhost:9200/wazuh-alerts-*/_settings" \
  -H "Content-Type: application/json" \
  -d '{
    "index.search.slowlog.threshold.query.warn": "5s",
    "index.search.slowlog.threshold.query.info": "2s"
  }'

# Просмотр slow log
curl -sk -u admin:$PASSWORD \
  "https://localhost:9200/_cat/indices/.opendistro-slow-log-*?v"

Решения:

  • Используйте filter вместо must для условий, не требующих релевантности
  • Ограничивайте временной диапазон запроса - не запрашивайте данные за все время
  • Добавьте "size": 0 если нужны только агрегации
  • Избегайте wildcard и regexp на полях без keyword-маппинга
  • Увеличьте JVM heap, если сборщик мусора работает слишком часто

Конфликты маппингов полей

Симптом: ошибка illegal_argument_exception при запросе.

# Проверка маппинга конкретного поля
curl -sk -u admin:$PASSWORD \
  "https://localhost:9200/wazuh-alerts-*/_mapping/field/data.srcip?pretty"

Решение: поле определено с разными типами в разных индексах. Необходимо обновить шаблон и переиндексировать данные или использовать keyword суффикс:

{ "term": { "rule.description.keyword": "Authentication failure" } }

Индекс не найден (index_not_found_exception)

Симптом: ошибка no such index [wazuh-alerts-*].

Диагностика:

# Проверить существующие индексы
curl -sk -u admin:$PASSWORD "https://localhost:9200/_cat/indices/wazuh-*?v"

# Проверить алиасы
curl -sk -u admin:$PASSWORD "https://localhost:9200/_cat/aliases/wazuh-*?v"

# Проверить, что Filebeat отправляет данные
curl -sk -u admin:$PASSWORD \
  "https://localhost:9200/_cat/indices/wazuh-alerts-*?v&s=index:desc&h=index,docs.count,store.size"

Решения:

  • Убедитесь, что Filebeat запущен и подключен к Indexer
  • Проверьте конфигурацию Filebeat: filebeat.yml должен содержать корректный output.elasticsearch.hosts
  • Проверьте шаблон индекса: _template/wazuh
  • Для архивных данных убедитесь, что logall_json включен в ossec.conf

Превышение лимита результатов

Симптом: ошибка Result window is too large при запросе с "from": 10000.

OpenSearch по умолчанию ограничивает from + size значением 10 000. Решения:

  • Используйте Scroll API для полной выгрузки данных
  • Используйте search_after для пагинации
  • Для аналитики используйте агрегации вместо выгрузки документов
GET wazuh-alerts-*/_search
{
  "size": 100,
  "query": { "match_all": {} },
  "sort": [{ "timestamp": "desc" }, { "_id": "asc" }],
  "search_after": ["2025-01-15T10:30:00.000Z", "abc123"]
}

Дополнительные материалы

Last updated on