Wazuh + AWS Bedrock: ИИ-аналитика в Docker (Часть 1)

Введение

В предыдущей статье мы встроили локальную модель Ollama прямо в чат Wazuh Dashboard через ML Commons. Этот подход обеспечивает полный контроль над данными без облачных зависимостей. В этой серии мы пойдём параллельным путём: будем использовать AWS Bedrock — конкретно Claude Sonnet 4.5 — как бэкенд для инференса, при этом все данные безопасности остаются строго внутри локальной Docker-сети.

Архитектура, которую мы строим, состоит из трёх слоёв:

  • Часть 1 (эта статья) — конверсационный агент ML Commons на базе Bedrock Claude с PPLTool для запросов к алертам на естественном языке и чат-виджетом в Wazuh Dashboard
  • Часть 2opensearch-mcp-server-py как сайдкар-сервис в Docker, открывающий данные Wazuh Indexer через Model Context Protocol для внешних клиентов вроде Claude Desktop
  • Часть 3 — RAG-пайплайн с использованием Bedrock Titan Embeddings v2 и k-NN-индекса OpenSearch для семантического поиска по threat intelligence и SOC-плейбукам

По итогам Части 1 у вас будет работающий AI-аналитик в чате Wazuh Dashboard, который отвечает на вопросы об алертах на естественном языке, генерирует сводки угроз и транслирует запросы на английском языке в PPL-запросы к индексам wazuh-alerts-*.

Требования

  • Docker и Docker Compose v2
  • Репозиторий wazuh-docker, ветка v4.14.3
  • AWS-аккаунт с включённым доступом к моделям Bedrock (Claude Sonnet 4.5 и Titan Embed Text v2) в нужном регионе
  • IAM-пользователь с разрешением bedrock:InvokeModel на соответствующие ARN моделей
  • Минимум 8 ГБ RAM для Docker

Примечание о конфиденциальности. Содержимое алертов, отправляемых в Bedrock для инференса, обрабатывается AWS в соответствии с их условиями работы с данными. Для сред со строгими требованиями по месту хранения данных используйте подход с локальным Ollama из предыдущей статьи.

Подготовка стека

Клонирование и генерация сертификатов

git clone https://github.com/wazuh/wazuh-docker.git -b v4.14.3
cd wazuh-docker/single-node
docker compose -f generate-indexer-certs.yml run --rm generator

Создание файла .env

Все учётные данные хранятся в единственном файле .env в корне директории single-node/. Сразу добавьте его в .gitignore.

cat > single-node/.env << 'EOF'
# AWS Bedrock
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
AWS_SESSION_TOKEN=
AWS_REGION=us-east-1
EOF

Добавляем файл .env в .gitignore:

echo ".env" >> .gitignore

Оставьте AWS_SESSION_TOKEN пустым, если используете долгосрочные учётные данные IAM-пользователя. Для временных STS-credentials вставьте значение session token.

Скачивание Dashboard-плагинов ML Commons

Образ Wazuh Dashboard не включает UI-плагины ML Commons. Мы извлекаем их из соответствующего архива OpenSearch Dashboards.

curl https://artifacts.opensearch.org/releases/bundle/opensearch-dashboards/2.19.4/opensearch-dashboards-2.19.4-linux-x64.tar.gz -o opensearch-dashboards.tar.gz
tar -xzf opensearch-dashboards.tar.gz
 
mkdir -p config/wazuh_dashboard/plugins
cp -r opensearch-dashboards-2.19.4/plugins/observabilityDashboards  config/wazuh_dashboard/plugins/
cp -r opensearch-dashboards-2.19.4/plugins/mlCommonsDashboards       config/wazuh_dashboard/plugins/
cp -r opensearch-dashboards-2.19.4/plugins/assistantDashboards       config/wazuh_dashboard/plugins/
 
rm -rf opensearch-dashboards-2.19.4* opensearch-dashboards.tar.gz

Скачивание Indexer-плагинов ML Commons

На стороне Indexer нужны два дополнительных плагина: opensearch-skills (содержит PPLTool, VectorDBTool и другие инструменты агентов) и opensearch-flow-framework.

mkdir -p config/wazuh_indexer/plugins
cd config/wazuh_indexer/plugins
 
curl -L -O https://repo1.maven.org/maven2/org/opensearch/plugin/opensearch-flow-framework/2.19.4.0/opensearch-flow-framework-2.19.4.0.zip
curl -L -O https://repo1.maven.org/maven2/org/opensearch/plugin/opensearch-skills/2.19.4.0/opensearch-skills-2.19.4.0.zip
 
mkdir -p opensearch-flow-framework opensearch-skills
unzip opensearch-flow-framework-2.19.4.0.zip -d opensearch-flow-framework/
unzip opensearch-skills-2.19.4.0.zip         -d opensearch-skills/
rm -f *.zip
 
cd ../../..

Обновление opensearch_dashboards.yml

Добавьте следующие строки в config/wazuh_dashboard/opensearch_dashboards.yml:

assistant.chat.enabled: true
observability.query_assist.enabled: true
opensearch.requestTimeout: 300000
opensearch.shardTimeout: 300000
opensearch.pingTimeout: 300000

Обновление docker-compose.yml

Добавьте монтирование плагинов через volumes в сервисы wazuh.indexer и wazuh.dashboard.

Для wazuh.indexer в раздел volumes:

- ./config/wazuh_indexer/plugins/opensearch-flow-framework:/usr/share/wazuh-indexer/plugins/opensearch-flow-framework
- ./config/wazuh_indexer/plugins/opensearch-skills:/usr/share/wazuh-indexer/plugins/opensearch-skills

Для wazuh.dashboard в раздел volumes:

- ./config/wazuh_dashboard/plugins/assistantDashboards:/usr/share/wazuh-dashboard/plugins/assistantDashboards
- ./config/wazuh_dashboard/plugins/mlCommonsDashboards:/usr/share/wazuh-dashboard/plugins/mlCommonsDashboards
- ./config/wazuh_dashboard/plugins/observabilityDashboards:/usr/share/wazuh-dashboard/plugins/observabilityDashboards

Должен получиться примерно такой вид:

# Wazuh App Copyright (C) 2017, Wazuh Inc. (License GPLv2)
services:
  wazuh.manager:
    image: wazuh/wazuh-manager:4.14.3
    hostname: wazuh.manager
    restart: always
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 655360
        hard: 655360
    ports:
      - "1514:1514"
      - "1515:1515"
      - "514:514/udp"
      - "55000:55000"
    environment:
      - INDEXER_URL=https://wazuh.indexer:9200
      - INDEXER_USERNAME=admin
      - INDEXER_PASSWORD=SecretPassword
      - FILEBEAT_SSL_VERIFICATION_MODE=full
      - SSL_CERTIFICATE_AUTHORITIES=/etc/ssl/root-ca.pem
      - SSL_CERTIFICATE=/etc/ssl/filebeat.pem
      - SSL_KEY=/etc/ssl/filebeat.key
      - API_USERNAME=wazuh-wui
      - API_PASSWORD=MyS3cr37P450r.*-
    volumes:
      - wazuh_api_configuration:/var/ossec/api/configuration
      - wazuh_etc:/var/ossec/etc
      - wazuh_logs:/var/ossec/logs
      - wazuh_queue:/var/ossec/queue
      - wazuh_var_multigroups:/var/ossec/var/multigroups
      - wazuh_integrations:/var/ossec/integrations
      - wazuh_active_response:/var/ossec/active-response/bin
      - wazuh_agentless:/var/ossec/agentless
      - wazuh_wodles:/var/ossec/wodles
      - filebeat_etc:/etc/filebeat
      - filebeat_var:/var/lib/filebeat
      - ./config/wazuh_indexer_ssl_certs/root-ca-manager.pem:/etc/ssl/root-ca.pem
      - ./config/wazuh_indexer_ssl_certs/wazuh.manager.pem:/etc/ssl/filebeat.pem
      - ./config/wazuh_indexer_ssl_certs/wazuh.manager-key.pem:/etc/ssl/filebeat.key
      - ./config/wazuh_cluster/wazuh_manager.conf:/wazuh-config-mount/etc/ossec.conf

  wazuh.indexer:
    image: wazuh/wazuh-indexer:4.14.3
    hostname: wazuh.indexer
    restart: always
    ports:
      - "9200:9200"
    environment:
      - "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    volumes:
      - wazuh-indexer-data:/var/lib/wazuh-indexer
      - ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-indexer/config/certs/root-ca.pem
      - ./config/wazuh_indexer_ssl_certs/wazuh.indexer-key.pem:/usr/share/wazuh-indexer/config/certs/wazuh.indexer.key
      - ./config/wazuh_indexer_ssl_certs/wazuh.indexer.pem:/usr/share/wazuh-indexer/config/certs/wazuh.indexer.pem
      - ./config/wazuh_indexer_ssl_certs/admin.pem:/usr/share/wazuh-indexer/config/certs/admin.pem
      - ./config/wazuh_indexer_ssl_certs/admin-key.pem:/usr/share/wazuh-indexer/config/certs/admin-key.pem
      - ./config/wazuh_indexer/wazuh.indexer.yml:/usr/share/wazuh-indexer/config/opensearch.yml
      - ./config/wazuh_indexer/internal_users.yml:/usr/share/wazuh-indexer/config/opensearch-security/internal_users.yml
      - ./config/wazuh_indexer/plugins/opensearch-flow-framework:/usr/share/wazuh-indexer/plugins/opensearch-flow-framework
      - ./config/wazuh_indexer/plugins/opensearch-skills:/usr/share/wazuh-indexer/plugins/opensearch-skills

  wazuh.dashboard:
    image: wazuh/wazuh-dashboard:4.14.3
    hostname: wazuh.dashboard
    restart: always
    ports:
      - 443:5601
    environment:
      - INDEXER_USERNAME=admin
      - INDEXER_PASSWORD=SecretPassword
      - WAZUH_API_URL=https://wazuh.manager
      - DASHBOARD_USERNAME=kibanaserver
      - DASHBOARD_PASSWORD=kibanaserver
      - API_USERNAME=wazuh-wui
      - API_PASSWORD=MyS3cr37P450r.*-
    volumes:
      - ./config/wazuh_indexer_ssl_certs/wazuh.dashboard.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem
      - ./config/wazuh_indexer_ssl_certs/wazuh.dashboard-key.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem
      - ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-dashboard/certs/root-ca.pem
      - ./config/wazuh_dashboard/opensearch_dashboards.yml:/usr/share/wazuh-dashboard/config/opensearch_dashboards.yml
      - ./config/wazuh_dashboard/wazuh.yml:/usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml
      - wazuh-dashboard-config:/usr/share/wazuh-dashboard/data/wazuh/config
      - wazuh-dashboard-custom:/usr/share/wazuh-dashboard/plugins/wazuh/public/assets/custom
      - ./config/wazuh_dashboard/plugins/assistantDashboards:/usr/share/wazuh-dashboard/plugins/assistantDashboards
      - ./config/wazuh_dashboard/plugins/mlCommonsDashboards:/usr/share/wazuh-dashboard/plugins/mlCommonsDashboards
      - ./config/wazuh_dashboard/plugins/observabilityDashboards:/usr/share/wazuh-dashboard/plugins/observabilityDashboards
    depends_on:
      - wazuh.indexer
    links:
      - wazuh.indexer:wazuh.indexer
      - wazuh.manager:wazuh.manager

volumes:
  wazuh_api_configuration:
  wazuh_etc:
  wazuh_logs:
  wazuh_queue:
  wazuh_var_multigroups:
  wazuh_integrations:
  wazuh_active_response:
  wazuh_agentless:
  wazuh_wodles:
  filebeat_etc:
  filebeat_var:
  wazuh-indexer-data:
  wazuh-dashboard-config:
  wazuh-dashboard-custom:

Запуск стека

docker compose up -d
docker compose ps

Также можно мониторить процесс запуска через docker compose logs -f.

Все три контейнера wazuh.indexer, wazuh.manager, wazuh.dashboard должны иметь статус Up. Dashboard доступен по адресу https://localhost:443.

Настройка ML Commons

Откройте Wazuh Dashboard, перейдите в Indexer Management -> Dev Tools и выполните следующий запрос.

Активация agent framework

PUT /_cluster/settings
{
  "persistent": {
    "plugins.ml_commons.only_run_on_ml_node": false,
    "plugins.ml_commons.native_memory_threshold": 99,
    "plugins.ml_commons.agent_framework_enabled": true,
    "plugins.ml_commons.memory_feature_enabled": true,
    "plugins.ml_commons.rag_pipeline_feature_enabled": true,
    "plugins.ml_commons.connector_access_control_enabled": true,
    "plugins.ml_commons.connector.private_ip_enabled": true,
    "plugins.ml_commons.trusted_connector_endpoints_regex": [
      "^https://bedrock-runtime\\.[a-z0-9-]+\\.amazonaws\\.com/.*$"
    ]
  }
}

Создание Bedrock-коннектора

POST /_plugins/_ml/connectors/_create
{
  "name": "Bedrock Claude Sonnet 4.5",
  "description": "Bedrock Claude Sonnet 4.5 for Wazuh security analytics",
  "version": 1,
  "protocol": "aws_sigv4",
  "credential": {
    "access_key": "<AWS_ACCESS_KEY_ID>",
    "secret_key": "<AWS_SECRET_ACCESS_KEY>"
  },
  "parameters": {
    "region": "us-east-1",
    "service_name": "bedrock",
    "response_filter": "$.content[0].text",
    "max_tokens_to_sample": "8000",
    "anthropic_version": "bedrock-2023-05-31",
    "model": "us.anthropic.claude-sonnet-4-5-20250929-v1:0"
  },
  "actions": [{
    "action_type": "predict",
    "method": "POST",
    "headers": { "content-type": "application/json" },
    "url": "https://bedrock-runtime.${parameters.region}.amazonaws.com/model/${parameters.model}/invoke",
    "request_body": "{\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"${parameters.prompt}\"}]},{\"role\":\"assistant\",\"content\":[{\"type\":\"text\",\"text\":\"{\"}]}],\"anthropic_version\":\"${parameters.anthropic_version}\",\"max_tokens\":${parameters.max_tokens_to_sample}}"
  }]
}

Сохраните connector_id из ответа.

Регистрация и деплой модели

POST /_plugins/_ml/models/_register?deploy=true
{
  "name": "claude-sonnet-4-5",
  "function_name": "remote",
  "description": "Bedrock Claude Sonnet 4.5 для Wazuh",
  "connector_id": "<connector_id>"
}

Сохраните model_id из ответа.

Выполните быстрый smoke-тест:

POST /_plugins/_ml/models/<model_id>/_predict
{
  "parameters": {
    "inputs": "Reply with exactly one word: WORKING"
  }
}

В ответе должно быть WORKING.

Создание агента-аналитика безопасности

POST /_plugins/_ml/agents/_register
{
  "name": "wazuh_bedrock_analyst",
  "type": "conversational",
  "description": "Wazuh Bedrock Claude",
  "llm": {
    "model_id": "<model_id>",
    "parameters": {
      "max_iteration": 5,
      "stop_when_no_tool_found": true,
      "response_filter": "$.content[0].text",
      "system_instruction": "You are a senior SOC analyst specialized in Wazuh SIEM. Analyze alerts, identify threats, map findings to MITRE ATT&CK, and provide actionable recommendations. Be concise and precise."
    }
  },
  "memory": { "type": "conversation_index" },
  "tools": [
    {
      "type": "PPLTool",
      "name": "WazuhAlertQuery",
      "description": "Translates natural language security questions to PPL queries over wazuh-alerts-4.x-* indices. Use for: counting alerts, filtering by agent, rule level, source IP, or time range, and top-N aggregations.",
      "parameters": {
        "model_id": "<model_id>",
        "index": "wazuh-alerts-4.x-*",
        "execute": true
      }
    },
    {
      "type": "SearchIndexTool",
      "name": "WazuhAlertSearch",
      "description": "Searches wazuh-alerts-4.x-* using Query DSL. Use for exact field filtering on rule.level, rule.id, agent.name, data.srcip, and @timestamp.",
      "parameters": { "index": "wazuh-alerts-4.x-*" }
    },
    {
      "type": "CatIndexTool",
      "name": "ListWazuhIndices",
      "description": "Lists all wazuh-* indices with document counts and disk size."
    },
    {
      "type": "IndexMappingTool",
      "name": "DiscoverAlertFields",
      "description": "Returns field mappings for any Wazuh index. Use before constructing a query to confirm field names."
    }
  ],
  "app_type": "os_chat"
}

Сохраните agent_id.

Подключение агента к чату Dashboard

docker compose exec wazuh.indexer curl -k -X PUT \
  "https://localhost:9200/.plugins-ml-config/_doc/os_chat" \
  --cert /usr/share/wazuh-indexer/config/certs/admin.pem \
  --key /usr/share/wazuh-indexer/config/certs/admin-key.pem \
  --cacert /usr/share/wazuh-indexer/config/certs/root-ca.pem \
  -H "Content-Type: application/json" \
  -d '{
    "type": "os_chat_root_agent",
    "configuration": {
      "agent_id": "<agent_id>"
    }
  }'

Ожидаемый ответ:

{
   "_index":".plugins-ml-config",
   "_id":"os_chat",
   "_version":2,
   "result":"updated",
   "_shards":{
      "total":1,
      "successful":1,
      "failed":0
   },
   "_seq_no":2,
   "_primary_term":1
}

Тестирование агента

Откройте Wazuh Dashboard и нажмите иконку чата в правом верхнем углу. Попробуйте следующие вопросы:

Show me the top 10 rules by alert count in the last 24 hours

или

Summarize all critical alerts (rule level 7 or above) from the past 6 hours.
Include affected agents, source IPs, and recommended actions.

Агент работает по циклу рассуждений ReAct: сначала может вызвать DiscoverAlertFields для проверки имён полей, затем WazuhAlertQuery для генерации и выполнения PPL-запроса, и наконец синтезирует ответ на естественном языке с помощью Bedrock Claude.

Заключение

В этой части мы развернули полный стек Wazuh в Docker с поддержкой ML Commons, подключили AWS Bedrock Claude Sonnet 4.5 в качестве LLM-бэкенда и создали конверсационного агента с набором инструментов для анализа алертов безопасности на естественном языке. В следующей части мы добавим opensearch-mcp-server-py как сайдкар-сервис, чтобы открыть данные Wazuh Indexer через Model Context Protocol для внешних клиентов.

Навигация по серии:

  • Часть 1: ИИ-аналитика безопасности в Docker (вы здесь)
  • Часть 2: MCP-сервер для внешних клиентов (скоро)
  • Часть 3: RAG-пайплайн с Bedrock Titan Embeddings (скоро)

Рекомендуемое чтение


Смотрите также