Visão geral
Este guia descreve a implantação completa do Elastic Stack (Elasticsearch e Kibana) em um cluster compartilhado com isolamento físico do Google Distributed Cloud (GDC) e demonstra como integrá-lo a um serviço de modelo de linguagem grande (LLM) para avaliar os recursos de IA generativa do Elastic.
A implantação usa o operador oficial do Elastic Cloud no Kubernetes (ECK) disponível no Marketplace do GDC com isolamento físico. O operador é instalado em um namespace de projeto em um cluster compartilhado do Kubernetes e automatiza o provisionamento e o gerenciamento do ciclo de vida dos recursos do Elasticsearch e do Kibana. Depois de implantado, o Kibana se conecta a um endpoint de inferência de LLM (como o Gemini gerenciado no GDC ou um serviço autogerenciado compatível com a OpenAI, como o Ollama ou o vLLM). Essa integração potencializa os recursos de IA generativa da Elastic, incluindo o Agente de IA da Elastic, agentes personalizados criados com o Agent Builder e o Attack Discovery.
Este guia foi escrito para o Elastic v9.4.0, o Elastic Kubernetes Operator v3.4.0 e o GDC v1.16.2. Algumas instruções podem ser um pouco diferentes se você estiver usando versões diferentes.
Arquitetura
A arquitetura consiste em uma implantação do Elastic Stack gerenciada pelo operador do ECK em um namespace de projeto em um cluster de usuário compartilhado do GDC. O Kibana se comunica pela rede interna do GDC com um endpoint de inferência de LLM.

Os principais componentes dessa arquitetura incluem:
- Cluster compartilhado do GDC: um cluster compartilhado do Kubernetes com escopo de organização anexado a vários projetos, que hospeda pacotes de software e cargas de trabalho de aplicativos do GDC Marketplace.
- Projeto do GDC: o namespace do projeto no cluster compartilhado que isola o operador do ECK, o cluster do Elasticsearch, a instância do Kibana e os Secrets e as NetworkPolicies do Kubernetes associados.
- Operador do Elastic Cloud no Kubernetes (ECK): instalado do
GDC Marketplace no namespace do projeto, o
operador gerencia os recursos personalizados
ElasticsearcheKibanae reconcilia a licença do Elastic necessária para recursos de IA generativa e conectores de LLM externos. - Elasticsearch: armazena e indexa dados operacionais e de segurança, além de executar consultas de pesquisa e ES|QL geradas pelos recursos de IA e agentes do Kibana.
- Kibana: hospeda os recursos de IA generativa da Elastic, incluindo o agente de IA da Elastic (para investigação de registros em linguagem natural e geração de ES|QL), o Agent Builder (para criar agentes e ferramentas de conversação personalizados com base nos dados do Elasticsearch) e o Attack Discovery (para correlação de alertas de segurança e análise de cadeia de ataques). O Kibana também gerencia os conectores de ação de LLM que orquestram a chamada de ferramentas em várias etapas entre o usuário, o Elasticsearch e o serviço de LLM.
- Serviço de LLM: um serviço de LLM gerenciado (como o Gemini no GDC) ou um servidor de inferência autogerenciado (como Ollama ou vLLM) que fornece recursos de conclusão de chat e chamada de ferramentas para o Kibana pela rede interna do GDC.
Antes de começar
Verifique se o ambiente do GDC atende aos seguintes pré-requisitos e suposições antes de iniciar a implantação:
- Cluster compartilhado do Kubernetes e projeto do GDC: você tem acesso a um cluster compartilhado do GDC e a um projeto do GDC anexado a esse cluster. O projeto serve como o contêiner de namespace para todos os recursos do Kubernetes implantados neste guia. Verifique se o cluster tem recursos de computação e armazenamento permanente suficientes para hospedar as cargas de trabalho do operador ECK, do Elasticsearch e do Kibana.
- Serviço de LLM: você tem acesso de rede a um serviço de LLM, seja o
serviço gerenciado do Gemini (se disponível no seu
ambiente do GDC) ou um serviço de LLM autogerenciado compatível com a OpenAI (por exemplo,
gemma4:31bdisponibilizado pelo Ollama ou vLLM em um projeto de GPU). A implantação da infraestrutura do LLM em si está fora do escopo deste guia, mas os requisitos específicos do modelo e da API estão detalhados em Requisitos para o serviço de LLM. Recupere a versão do Elastic que vem com o ambiente do GDC:
export ELASTIC_VERSION=$(curl -sk "https://harbor.${ORG}.${ZONE}.${GDCH_DOMAIN}/api/v2.0/projects/library/repositories/elasticsearch%252Felasticsearch/artifacts" | jq -r '[.[].tags[]?.name] | sort_by(split(".") | map(tonumber)) | last')Configure as variáveis de ambiente que serão referenciadas neste guia. Modifique esses valores conforme necessário para corresponder ao seu ambiente específico:
# GDC environment configuration export PROJECT_ID="your-project-id" export CLUSTER_NAME="your-shared-cluster-name" export ZONE="your-zone-name" export USER_NAME="your-gdc-user-email" # Marketplace package and Elastic Stack configuration export MARKETPLACE_SERVICE_NAME="elastic" # LLM service configuration export LLM_CONNECTOR_NAME="your-llm-connector-name" export LLM_API_URL="http://your-llm-service-host:11434/v1/chat/completions" export LLM_MODEL_NAME="your-llm-model-name" export LLM_API_KEY="your-llm-api-key"Configurar
gdcloud:gdcloud config set project ${PROJECT_ID} gdcloud config set core/zone ${ZONE}Solicite ou vincule os papéis do IAM necessários para envolvidos no projeto para seu usuário no projeto de destino do GDC (
${PROJECT_ID}). Com base na documentação do ciclo de vida do GDC Marketplace e nos requisitos de rede do GDC, pelo menos os seguintes papéis do projeto são necessários:- Administrador de namespace (
namespace-admin): acesso administrativo total para criar e gerenciar cargas de trabalho, secrets, serviços e recursos personalizados do Kubernetes no namespace do projeto no cluster compartilhado. - Leitor do projeto (
project-viewer): acesso de leitura para visualizar o projeto e os recursos dele no console e na CLI do GDC. - Editor do Marketplace (
marketplace-editor): permissão para instalar, fazer upgrade e desinstalar pacotes de software do GDC Marketplace no projeto. - Administrador de NetworkPolicy do projeto (
project-networkpolicy-admin): permissão para configurar regras globais deProjectNetworkPolicyque permitem que o tráfego entre projetos e externo chegue aos serviços LoadBalancer do Kibana e do Elasticsearch.
Peça ao administrador do IAM da organização ou do projeto para conceder esses papéis (ou execute os seguintes comandos se você tiver privilégios de administrador do IAM):
gdcloud projects add-iam-policy-binding ${PROJECT_ID} \ --member="user:${USER_NAME}" \ --role=namespace-admin gdcloud projects add-iam-policy-binding ${PROJECT_ID} \ --member="user:${USER_NAME}" \ --role=project-viewer gdcloud projects add-iam-policy-binding ${PROJECT_ID} \ --member="user:${USER_NAME}" \ --role=marketplace-editor gdcloud projects add-iam-policy-binding ${PROJECT_ID} \ --member="user:${USER_NAME}" \ --role=project-networkpolicy-admin- Administrador de namespace (
Recupere o kubeconfig do cluster compartilhado e configure um alias de shell
kuparakubectl:KUBECONFIG=kubeconfig-${CLUSTER_NAME}.yaml gdcloud clusters \ get-credentials ${CLUSTER_NAME} \ --zone ${ZONE} alias ku="kubectl --kubeconfig=kubeconfig-${CLUSTER_NAME}.yaml"
Instalar o pacote do Elastic Marketplace
Nesta seção, você vai instalar o operador do Elastic Cloud no Kubernetes (ECK) no namespace do projeto usando o Marketplace do GDC com isolamento físico, seguindo o guia do ciclo de vida do Marketplace do GDC.
Instalar usando a CLI gdcloud (ou o console do GDC)
Liste os pacotes de software disponíveis do Marketplace para confirmar o nome do serviço e a versão disponível:
gdcloud marketplace services listA saída será parecida com esta:
SERVICE LATEST VERSION confluent <CONFLUENT_VERSION> dataproc <DATAPROC_VERSION> elastic <ECK_OPERATOR_VERSION> mariadb <MARIADB_VERSION> mattermost <MATTERMOST_VERSION> mongodb <MONGODB_VERSION> neo4j <NEO4J_VERSION> redis <REDIS_VERSION> starboard <STARBOARD_VERSION> starburst <STARBURST_VERSION> tes <TES_VERSION> vault <VAULT_VERSION>Instale o pacote do marketplace do Elastic no cluster compartilhado de destino:
gdcloud marketplace services install ${MARKETPLACE_SERVICE_NAME} \ --cluster=${CLUSTER_NAME} \ --zone=${ZONE} \ --project ${PROJECT_ID}*(Como alternativa, no console do GDC, selecione o projeto
${PROJECT_ID}, acesse Marketplace, selecione a listagem Elastic Cloud no Kubernetes, clique em Instalar, selecione o cluster${CLUSTER_NAME}e clique em Instalar).*Verifique se o StatefulSet
elastic-operatore o pod estão em execução no namespace do projeto (${PROJECT_ID}):ku get statefulset,pods -n ${PROJECT_ID}A saída será parecida com esta:
NAME READY AGE statefulset.apps/elastic-operator 1/1 3m NAME READY STATUS RESTARTS AGE pod/elastic-operator-0 1/1 Running 0 3m
Como implantar o Elastic Stack
Criar o manifesto do Elastic Stack
O pacote do GDC Marketplace implanta o operador ECK pré-configurado para extrair imagens de contêiner do Elasticsearch e do Kibana do registro local do Harbor com isolamento físico.
Crie elastic-stack.yaml para implantar um cluster do Elasticsearch de um nó, uma instância UI do Kibana, serviços HTTP do LoadBalancer e um allow-all NetworkPolicy no namespace do projeto (${PROJECT_ID}). O manifesto também inclui uma licença Secret reconciliada pelo operador do ECK, configurada aqui como um teste Enterprise de 30 dias (eck-trial-license), caso você ainda não tenha sua própria licença Enterprise:
cat <<EOF > elastic-stack.yaml
apiVersion: v1
kind: Secret
metadata:
name: eck-trial-license
namespace: ${PROJECT_ID}
labels:
license.k8s.elastic.co/type: enterprise_trial
annotations:
elastic.co/eula: accepted
---
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
name: elastic-cluster
namespace: ${PROJECT_ID}
spec:
version: ${ELASTIC_VERSION}
http:
service:
spec:
type: LoadBalancer
nodeSets:
- name: default
count: 1
config:
# Disable mmap to avoid requiring privileged host sysctl modifications
node.store.allow_mmap: false
podTemplate:
spec:
initContainers:
- name: sysctl
securityContext:
privileged: false
allowPrivilegeEscalation: false
runAsNonRoot: true
capabilities:
drop:
- ALL
command: ["sh", "-c", "true"]
containers:
- name: elasticsearch
resources:
requests:
memory: 2Gi
cpu: 500m
limits:
memory: 4Gi
cpu: 2
volumeClaimTemplates:
- metadata:
name: elasticsearch-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
name: kibana-ui
namespace: ${PROJECT_ID}
spec:
version: ${ELASTIC_VERSION}
count: 1
elasticsearchRef:
name: elastic-cluster
http:
service:
spec:
type: LoadBalancer
podTemplate:
spec:
containers:
- name: kibana
resources:
requests:
memory: 1Gi
cpu: 200m
limits:
memory: 2Gi
cpu: 1
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
namespace: ${PROJECT_ID}
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
cidr: 0.0.0.0/0
EOF
Aplicar e verificar a implantação
Aplique o manifesto:
ku apply -f elastic-stack.yaml
Aplique um ProjectNetworkPolicy global para que as regras de firewall do GDC permitam o tráfego de entrada para os IPs do LoadBalancer e o tráfego entre projetos (como a comunicação com o serviço de LLM):
kubectl --kubeconfig=kubeconfig-global.yaml apply -f - <<EOF
apiVersion: networking.global.gdc.goog/v1
kind: ProjectNetworkPolicy
metadata:
namespace: ${PROJECT_ID}
name: allow-inbound-traffic-from-external
spec:
policyType: Ingress
subject:
subjectType: UserWorkload
ingress:
- from:
- ipBlock:
cidr: 0.0.0.0/0
---
apiVersion: networking.global.gdc.goog/v1
kind: ProjectNetworkPolicy
metadata:
namespace: ${PROJECT_ID}
name: allow-inbound-traffic-from-all-projects
spec:
policyType: Ingress
subject:
subjectType: UserWorkload
ingress:
- from:
- projects: {}
EOF
Monitore os pods, serviços e recursos personalizados até que o Elasticsearch e o Kibana alcancem os status Running e Ready e recebam IPs do LoadBalancer em ${CLUSTER_NAME}:
ku get pods,svc,elasticsearch,kibana -n ${PROJECT_ID}
Quando os recursos estiverem prontos, a saída será semelhante a esta:
NAME READY STATUS RESTARTS AGE
pod/elastic-cluster-es-default-0 1/1 Running 0 3m
pod/elastic-operator-0 1/1 Running 0 15m
pod/kibana-ui-kb-869844478d-59ptw 1/1 Running 0 3m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/elastic-cluster-es-default ClusterIP None <none> 9200/TCP 11m
service/elastic-cluster-es-http LoadBalancer 10.101.18.181 <ELASTIC_EXTERNAL_IP> 9200:32159/TCP 11m
service/elastic-cluster-es-internal-http ClusterIP 10.101.18.81 <none> 9200/TCP 11m
service/elastic-cluster-es-transport ClusterIP None <none> 9300/TCP 11m
service/g-svc-g-fre-elastic-cluster-es-http-shared-clus-extern-56d5eb67 ClusterIP None <none> 9200/TCP 11m
service/g-svc-g-fre-kibana-ui-kb-http-shared-cluster-0b-extern-75a78b1f ClusterIP None <none> 5601/TCP 11m
service/kibana-ui-kb-http LoadBalancer 10.101.19.86 <KIBANA_EXTERNAL_IP> 5601:32735/TCP 11m
NAME HEALTH NODES VERSION PHASE AGE
elasticsearch.elasticsearch.k8s.elastic.co/elastic-cluster green 1 <ELASTIC_VERSION> Ready 3m
NAME HEALTH NODES VERSION AGE
kibana.kibana.k8s.elastic.co/kibana-ui green 1 <ELASTIC_VERSION> 3m
Exporte os IPs externos do LoadBalancer atribuídos ao Elasticsearch e ao Kibana para uso nas etapas seguintes:
export ELASTICSEARCH_IP=$(ku get svc elastic-cluster-es-http \
-n ${PROJECT_ID} \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')
export KIBANA_IP=$(ku get svc kibana-ui-kb-http \
-n ${PROJECT_ID} \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo "Kibana External URL: https://${KIBANA_IP}:5601"
echo "Elasticsearch External URL: https://${ELASTICSEARCH_IP}:9200"
Recuperar credenciais e acessar a interface do Kibana
Recuperar a senha de superusuário elastic
O ECK gera automaticamente um secret do Kubernetes
(elastic-cluster-es-elastic-user) que contém a senha padrão do superusuário
elastic. Recupere e decodifique com:
export ELASTIC_PASSWORD=$(ku get secret elastic-cluster-es-elastic-user \
-n ${PROJECT_ID} \
-o jsonpath='{.data.elastic}' | base64 -d)
echo "${ELASTIC_PASSWORD}"
Verificar a conectividade e fazer login
Verifique se o Elasticsearch pode ser acessado por HTTPS na sua estação de trabalho:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" "https://${ELASTICSEARCH_IP}:9200"Abra
https://${KIBANA_IP}:5601no navegador, aceite o aviso de certificado autoassinado e faça login com:- Nome de usuário:
elastic - Senha:
<output from ELASTIC_PASSWORD above>
- Nome de usuário:
Verificar a licença do Elastic
Os recursos de IA generativa da Elastic (incluindo o Elastic AI Agent, o Agent Builder e o Attack Discovery) e os conectores de LLM externos exigem uma licença Enterprise (ou uma licença de teste Enterprise ativa de 30 dias).
Quando um cluster do Elasticsearch é gerenciado pelo operador do ECK, o license-controller do ECK reconcilia continuamente a licença do cluster com os Secrets do Kubernetes no namespace do operador (${PROJECT_ID}). Se você ainda não tiver uma licença Enterprise de produção, o Secret eck-trial-license incluído em elastic-stack.yaml (license.k8s.elastic.co/type:
enterprise_trial) instruirá o ECK a provisionar e manter uma licença de teste Enterprise de 30 dias automaticamente. Iniciar um teste sem custo financeiro manualmente via POST
/_license/start_trial sem um Secret de licença do ECK fará com que o ECK reverta
o cluster para basic no próximo ciclo de reconciliação.
Para verificar a licença ativa pela CLI:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \
"https://${ELASTICSEARCH_IP}:9200/_license" | jq .
*(Como alternativa, no Kibana, abra o menu de navegação e acesse Gerenciamento
> Ferramentas para desenvolvedores e execute GET /_license na guia "Console").*
A saída será semelhante à seguinte, mostrando "status": "active"
e "type": "enterprise" (ou "type": "trial" se você estiver usando a licença de teste):
{
"license": {
"status": "active",
"uid": "d8c2b566-8c2b-436c-a1ea-5502d6790505",
"type": "trial",
"issue_date": "2026-09-17T23:19:25.053Z",
"issue_date_in_millis": 1789687165053,
"expiry_date": "2026-10-17T23:19:25.053Z",
"expiry_date_in_millis": 1792279165053,
"max_nodes": 1000,
"max_resource_units": null,
"issued_to": "elastic-cluster",
"issuer": "elasticsearch",
"start_date_in_millis": -1
}
}
Carregar um conjunto de dados de amostra integrado
Para avaliar os recursos de IA da Elastic sem escrever pipelines personalizados de ingestão de dados
ou exigir acesso externo à Internet, carregue o conjunto de dados de amostra de registros da Web
integrado do Kibana (kibana_sample_data_logs). Como o arquivo de dados de amostra
está incluído na imagem do contêiner do Kibana, ele é instalado em segundos em
um ambiente com isolamento físico. O conjunto de dados preenche 14.074 documentos de registro de acesso à Web
contendo endereços IP do cliente (clientip), códigos de status HTTP (response),
países de origem e destino (geo.src, geo.dest), bytes transferidos
(bytes), URLs e strings de user agent.
Consulte
Adicionar dados de amostra no Kibana
e
elastic-datasets-builtin.md
para mais conjuntos de dados integrados (flights e ecommerce).
Carregar o conjunto de dados
Execute o comando a seguir para carregar kibana_sample_data_logs pelo endpoint de dados de amostra interno do Kibana:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \
-X POST "https://${KIBANA_IP}:5601/api/sample_data/logs" \
-H "kbn-xsrf: true" \
-H "x-elastic-internal-origin: Kibana"
Verificar o conjunto de dados de amostra
Verifique se o conjunto de dados de amostra logs está marcado como installed no Kibana e
confirme se o fluxo de dados de apoio contém documentos 14,074 no
Elasticsearch:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \
"https://${KIBANA_IP}:5601/api/sample_data" \
-H "kbn-xsrf: true" \
-H "x-elastic-internal-origin: Kibana" \
| jq '.[] | select(.id == "logs") | {id, name, status}'
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \
"https://${ELASTICSEARCH_IP}:9200/_cat/indices/kibana_sample_data_logs*?v"
A saída será parecida com esta:
{
"id": "logs",
"name": "Sample web logs",
"status": "installed"
}
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size
yellow open .ds-kibana_sample_data_logs-2026.09.22-000001 0YivG3oFQQmb-mYqmULr_g 1 1 14074 0 8.7mb 8.7mb 8.7mb
Requisitos para o serviço de LLM
Em um ambiente GDC com isolamento físico, os LLMs gerenciados do Elastic hospedados na nuvem e os provedores externos de LLMs SaaS públicos não podem ser acessados. Dependendo do ambiente do GDC:
- Gemini gerenciado no GDC: em ambientes do GDC que oferecem o Gemini como um serviço de LLM gerenciado, é possível conectar o Kibana diretamente ao serviço gerenciado do Gemini, se disponível.
- Serviço de LLM autogerenciado: como alternativa, é possível conectar o Elastic a um serviço de LLM autogerenciado hospedado no ambiente do GDC (por exemplo, em nós de GPU do GDC).
A implantação de um serviço de LLM autogerenciado está fora do escopo deste guia. Ao provisionar ou selecionar um serviço de LLM autogerenciado para o Elastic, verifique se ele atende aos requisitos derivados dos guias oficiais do Elastic:
- Configurar o acesso a LLMs
- LLMs personalizados autogerenciados (consulte também Conectar-se ao vLLM em ambientes com isolamento físico)
- Observabilidade de LLM e IA agêntica
Resumo dos requisitos técnicos
Os pontos a seguir resumem os principais requisitos de API, capacidade do modelo, janela de contexto e tempo de execução necessários para que um serviço de LLM autogerenciado funcione de maneira confiável com o agente de IA, o criador de agentes e a descoberta de ataques da Elastic. Os nomes de parâmetros de configuração específicos citados nas seções a seguir (como num_ctx, num_predict, repeat_penalty, repeat_last_n, OLLAMA_FLASH_ATTENTION e OLLAMA_NUM_PARALLEL) são fornecidos usando o Ollama como exemplo. Aplique as
configurações equivalentes se você usar outro mecanismo de inferência, como o vLLM.
- API de preenchimento de chat compatível com a OpenAI (
/v1/chat/completions) com chamada de função nativa:- Qualquer servidor de inferência autogerenciado (como Ollama ou vLLM) que exponha
um endpoint
/v1/chat/completionscompatível com a OpenAI (incluindo streaming de eventos enviados pelo servidor via"stream": true) pode ser integrado usando o conector da OpenAI do Kibana com o provedor definido como Outro (serviço compatível com a OpenAI). - Ative a chamada de função nativa (
"enableNativeFunctionCalling": true) no conector do Kibana ao usar modelos que oferecem suporte à chamada de ferramenta nativa da nuvem (comogemma4:31b). Por padrão ("enableNativeFunctionCalling": false), o Kibana usa a chamada de função simulada baseada em comandos, que é menos confiável durante chamadas de ferramentas aninhadas e pode falhar com erros de análise (comoError: Missing name for tool use). Ativar a chamada de função nativa delega a formatação da chamada de ferramenta diretamente ao protocolo OpenAItoolsdo servidor de inferência.
- Qualquer servidor de inferência autogerenciado (como Ollama ou vLLM) que exponha
um endpoint
- Modelo ajustado por instruções com recursos nativos de ferramentas e chamadas de função:
- O agente de IA do Elastic (
elastic-ai-agent) e os agentes de criação personalizados do Agent Builder dependem da chamada de ferramentas em várias etapas (platform.core.list_indices,platform.core.get_index_mapping,platform.core.search,platform.core.execute_esqleplatform.core.cases). Eles usam essas ferramentas para descobrir mapeamentos de índice, gerar e executar consultas ES|QL no Elasticsearch e sintetizar descobertas. - O modelo escolhido precisa ser ajustado por instruções e capaz de gerar invocações de ferramentas nativas da nuvem válidas, saídas JSON estruturadas e consultas ES|QL. Consulte a matriz de desempenho de LLM para observabilidade e a matriz de desempenho de LLM para Elastic Security (links em inglês) da Elastic para conferir os critérios de avaliação de modelos.
- O agente de IA do Elastic (
- Janela de contexto de pelo menos
128,000tokens (por exemplo,num_ctx: 131072no Ollama):- Embora a documentação geral de LLM personalizado autogerenciado da Elastic cite um mínimo de 64.000 tokens, uma janela de contexto de 128.000 tokens (
131,072tokens) é altamente recomendada para o Elastic agente de IA (elastic-ai-agent). - Durante investigações de várias etapas, o agente combina instruções do sistema e definições de ferramentas com esquemas de índice, documentação ES|QL e resultados de pesquisa brutos (por exemplo, recuperando até 100 documentos de amostra para inspecionar valores de campo antes da agregação). Uma única investigação pode exceder 70.000 tokens no histórico de conversas, o que faz com que uma janela de contexto de 64.000 tokens trunque o comando no meio da conversa.
- No Ollama, uma diretiva
PARAMETER num_ctxincorporada a umModelfilede modelo substitui a variável de ambienteOLLAMA_CONTEXT_LENGTHdo contêiner. Verifique seOLLAMA_CONTEXT_LENGTH=131072ePARAMETER num_ctx 131072noModelfiledo modelo estão configurados (em uma GPU NVIDIA A100 ou H100 de 80 GB,gemma4:31bno contexto128Kse encaixa confortavelmente na VRAM).
- Embora a documentação geral de LLM personalizado autogerenciado da Elastic cite um mínimo de 64.000 tokens, uma janela de contexto de 128.000 tokens (
- Limite de tokens de saída e penalidade de repetição leve (por exemplo,
num_predict: 8192erepeat_penalty: 1.05no Ollama):- O adaptador de stream da OpenAI do Kibana trata
"finish_reason": "length"como um erro fatalToken limit reachede envia"temperature": 0(decodificação gananciosa) em cada solicitação. - Para modelos capazes de raciocínio (como o
gemma4:31b, que gera tokens de pensamento interno antes de emitir chamadas de ferramentas), configure um limite de tokens de saída de pelo menos8,192tokens e uma penalidade de repetição suave. Por exemplo, no Ollama, definanum_predict: 8192,repeat_penalty: 1.05erepeat_last_n: 64. Evite penalidades de repetição excessivamente agressivas (como1.15+em janelas grandes), porque elas penalizam o modelo ao copiar uma consulta ES|QL do bloco de raciocínio interno para o JSON final de chamada de função.
- O adaptador de stream da OpenAI do Kibana trata
- Aceleração de GPU, Flash Attention e slots de cache KV paralelos (<60s
por turno):
- O conector da OpenAI do Kibana aplica um tempo limite HTTP padrão de 60 segundos por turno de streaming.
- Como uma única rodada do agente pode acionar subchamadas de LLM aninhadas (por exemplo, para traduzir solicitações de pesquisa em linguagem natural em consultas ES|QL enquanto a conversa do agente principal está ativa), executar um servidor de inferência com apenas um slot de cache de KV pode fazer com que as subchamadas removam o contexto armazenado em cache da conversa principal e desacelerem as rodadas subsequentes.
- No Ollama, defina
OLLAMA_FLASH_ATTENTION=1eOLLAMA_NUM_PARALLEL=2para que a atenção rápida acelere a avaliação de contexto longo, e a conversa principal e as subchamadas aninhadas mantenham slots de cache KV separados na memória da GPU.
Configurar o conector de LLM no Kibana
Depois que o serviço de LLM compatível com a OpenAI estiver em execução e acessível no pod do Kibana, configure um conector da OpenAI no Kibana usando a CLI ou a UI do Kibana.
Opção 1: criar o conector usando a CLI
Execute o seguinte comando
POST /api/actions/connectorpara criar um conector do OpenAI (.gen-ai) que aponta para seu serviço de LLM (${LLM_API_URL}) com a chamada de função nativa ativada ("enableNativeFunctionCalling": true). O Kibana gera e atribui automaticamente um ID de conector exclusivo:curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "https://${KIBANA_IP}:5601/api/actions/connector" \ -H "kbn-xsrf: true" \ -H "Content-Type: application/json" \ -d "{ \"name\": \"${LLM_CONNECTOR_NAME}\", \"connector_type_id\": \".gen-ai\", \"config\": { \"apiProvider\": \"Other\", \"apiUrl\": \"${LLM_API_URL}\", \"defaultModel\": \"${LLM_MODEL_NAME}\", \"enableNativeFunctionCalling\": true }, \"secrets\": { \"apiKey\": \"${LLM_API_KEY}\" } }" | jq .Verifique se o conector
${LLM_CONNECTOR_NAME}existe e não tem secrets ausentes listando os conectores do Kibana e filtrando por nome:curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ "https://${KIBANA_IP}:5601/api/actions/connectors" \ -H "kbn-xsrf: true" \ | jq ".[] | select(.name == \"${LLM_CONNECTOR_NAME}\")"A saída será parecida com esta:
{ "id": "<GENERATED_CONNECTOR_ID>", "name": "<LLM_CONNECTOR_NAME>", "config": { "apiProvider": "Other", "apiUrl": "<LLM_API_URL>", "defaultModel": "<LLM_MODEL_NAME>", "enableNativeFunctionCalling": true }, "connector_type_id": ".gen-ai", "is_preconfigured": false, "is_deprecated": false, "referenced_by_count": 0, "is_missing_secrets": false, "is_system_action": false }Para testar a conectividade completa em tempo real entre o Kibana e o serviço de LLM na CLI, procure o ID do conector gerado automaticamente por nome e invoque o endpoint
_execute:export CONNECTOR_ID=$(curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ "https://${KIBANA_IP}:5601/api/actions/connectors" \ -H "kbn-xsrf: true" \ | jq -r ".[] | select(.name == \"${LLM_CONNECTOR_NAME}\") | .id") curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "https://${KIBANA_IP}:5601/api/actions/connector/${CONNECTOR_ID}/_execute" \ -H "kbn-xsrf: true" \ -H "Content-Type: application/json" \ -d "{ \"params\": { \"subAction\": \"invokeAI\", \"subActionParams\": { \"model\": \"${LLM_MODEL_NAME}\", \"messages\": [ { \"role\": \"user\", \"content\": \"Reply with the single word Pong.\" } ] } } }" | jq .A saída será semelhante à seguinte, mostrando
"status": "ok"e a resposta do modelo:{ "status": "ok", "data": { "message": "Pong", "usage": { "prompt_tokens": 23, "prompt_tokens_details": { "cached_tokens": 0 }, "completion_tokens": 38, "total_tokens": 61 } }, "connector_id": "<GENERATED_CONNECTOR_ID>" }
Opção 2: criar o conector na interface do Kibana
- Faça login no Kibana (
https://${KIBANA_IP}:5601). - Abra o menu de navegação principal e acesse Gerenciamento > Alertas e insights > Conectores.
- Clique em Criar conector e selecione OpenAI.
- Configure os campos do conector:
- Nome do conector: insira o valor de
${LLM_CONNECTOR_NAME}(por exemplo,Gemma4-31B). - Selecione um provedor da OpenAI: escolha Outro (serviço compatível com a OpenAI).
- URL: insira o valor de
${LLM_API_URL}(o endpoint/v1/chat/completionsdo seu serviço de LLM). - Modelo padrão: insira o valor de
${LLM_MODEL_NAME}(por exemplo,gemma4:31b). - Chave de API: insira o valor de
${LLM_API_KEY}(qualquer string não vazia, se o servidor não aplicar a autenticação do portador, ou a chave de API do servidor). - Ativar chamada de função nativa: ative o botão de alternância.
- Nome do conector: insira o valor de
- Clique em Salvar e testar para salvar o conector e abrir imediatamente a guia Teste.
- Na guia Teste, insira um comando de amostra (por exemplo,
Reply with the single word Pong.) e clique em Executar para verificar se o Kibana recebe uma resposta válida do serviço de LLM.
Testar as integrações de LLM
Com o conjunto de dados kibana_sample_data_logs instalado e o conector de LLM
verificado, você pode testar os recursos de IA do Elastic no Kibana.
Agente de IA elástico
O
Agente de IA do Elastic
(elastic-ai-agent) é o agente de conversação padrão integrado ao Kibana
para explorar e analisar dados no Elasticsearch usando linguagem natural.
Com o conector de LLM configurado e as ferramentas integradas do Agent Builder, o
agente descobre automaticamente índices e mapeamentos do Elasticsearch
(platform.core.list_indices, platform.core.get_index_mapping), gera e
executa consultas ES|QL (platform.core.search, platform.core.execute_esql)
e sintetiza as descobertas sem que você precise conhecer o esquema subjacente
com antecedência. Você também pode criar seus próprios agentes personalizados com instruções e ferramentas específicas. Consulte Agentes personalizados com o Agent Builder para mais detalhes.
Para abrir e configurar o agente de IA do Elastic na interface do Kibana:
- Abra o menu de navegação principal no Kibana e selecione Agente de IA ou clique no botão Perguntar à IA na barra de cabeçalho superior ou navegue diretamente até
https://${KIBANA_IP}:5601/app/chat. - Verifique se o Agente de IA do Elastic está selecionado como o agente ativo e se o conector de LLM (
${LLM_CONNECTOR_NAME}) está selecionado como o conector de modelo.
Exemplo 1: como investigar solicitações HTTP com falha e possíveis scanners
Peça ao agente de IA da Elastic para identificar os principais endereços IP de clientes que geram respostas HTTP não 200 em kibana_sample_data_logs, os países de origem e se algum desses IPs também teve tráfego HTTP 200 bem-sucedido:
Opção 1: pela interface do Kibana:insira a seguinte pergunta no comando de chat do Agente de IA:
In kibana_sample_data_logs, which client IP addresses had the most failed HTTP requests (non-200 status codes), what countries did they come from, and did any of those IPs also have successful HTTP 200 requests?Opção 2: pela CLI:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "https://${KIBANA_IP}:5601/api/agent_builder/converse" \ -H "kbn-xsrf: true" \ -H "Content-Type: application/json" \ -d "{ \"agent_id\": \"elastic-ai-agent\", \"connector_id\": \"${CONNECTOR_ID}\", \"input\": \"In kibana_sample_data_logs, which client IP addresses had the most failed HTTP requests (non-200 status codes), what countries did they come from, and did any of those IPs also have successful HTTP 200 requests?\" }" | jq '{ status: .status, tools_called: [.steps[] | select(.type == "tool_call") | {tool_id: .tool_id, params: .params}], response: .response.message }'
O que acontece nos bastidores:
- O agente chama
platform.core.get_index_mapping(indices=["kibana_sample_data_logs"])para inspecionar os campos disponíveis (clientip,geo.src,response,bytes,requestetc.). Ele chama
platform.core.searchpara inspecionar documentos de amostra e confirmar que os códigos de status HTTP estão armazenados emresponse. Em seguida, executa agregações ES|QL para classificar solicitações não200porclientipegeo.srce verificar quais dos principais IPs também registraram tráfegoresponse == "200":FROM kibana_sample_data_logs | WHERE response != "200" | STATS failed_count = COUNT(*) BY clientip, geo.src | SORT failed_count DESC | LIMIT 10FROM kibana_sample_data_logs | WHERE clientip IN ("30.156.16.164", "213.235.30.99", "221.241.228.46", "209.94.234.133", "7.127.73.138", "247.240.202.244", "130.125.65.24", "164.85.94.243", "81.194.200.150", "30.145.143.210") AND response == "200" | STATS success_count = COUNT(*) BY clientip
A resposta do agente de IA do Elastic será semelhante a esta:
O endereço IP do cliente com o maior número de solicitações HTTP com falha (códigos de status diferentes de 200) em
kibana_sample_data_logsé 30.156.16.164, que se originou nos Estados Unidos e registrou 100 solicitações com falha.Outros endereços IP principais com solicitações com falha, todos também originados dos Estados Unidos, incluem:
- 213.235.30.99: 6 falhas
- 221.241.228.46: 6 falhas
- 209.94.234.133: 6 falhas
- 7.127.73.138: 5 falhas
- 247.240.202.244: 5 falhas
- 130.125.65.24: 5 falhas
- 164.85.94.243: 4 falhas
- 81.194.200.150: 4 falhas
- 30.145.143.210: 4 falhas
Em relação às solicitações bem-sucedidas, todos os IPs listados acima tiveram pelo menos algumas respostas HTTP 200 bem-sucedidas, exceto 30.156.16.164, que não teve solicitações bem-sucedidas.
Exemplo 2: como analisar o volume de tráfego e os bytes de payload por código de status HTTP
Peça ao agente de IA do Elastic o detalhamento dos códigos de status de resposta HTTP em
todas as solicitações em kibana_sample_data_logs e o total de bytes transferidos para
cada código de status:
Opção 1: pela interface do Kibana:inicie uma nova conversa no Agente de IA e insira a seguinte pergunta:
In kibana_sample_data_logs, what is the breakdown of HTTP response status codes across all requests, and how much total data (in bytes) was transferred for each status code?Opção 2: pela CLI:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "https://${KIBANA_IP}:5601/api/agent_builder/converse" \ -H "kbn-xsrf: true" \ -H "Content-Type: application/json" \ -d "{ \"agent_id\": \"elastic-ai-agent\", \"connector_id\": \"${CONNECTOR_ID}\", \"input\": \"In kibana_sample_data_logs, what is the breakdown of HTTP response status codes across all requests, and how much total data (in bytes) was transferred for each status code?\" }" | jq '{ status: .status, tools_called: [.steps[] | select(.type == "tool_call") | {tool_id: .tool_id, params: .params}], response: .response.message }'
O que acontece nos bastidores:
- O agente inspeciona o esquema
kibana_sample_data_logsusandoplatform.core.get_index_mapping, identificando os camposresponse.keywordebytes. Ele invoca
platform.core.search, que gera e executa a seguinte agregação ES|QL:FROM kibana_sample_data_logs | STATS request_count = COUNT(*), total_bytes = SUM(bytes) BY response.keyword | LIMIT 100
A resposta do Elastic agente de IA será semelhante a esta:
A análise dos códigos de status de resposta HTTP e o total de dados transferidos em
kibana_sample_data_logssão os seguintes:
| Código de status | Contagem de solicitações | Total de dados transferidos (bytes) |
|---|---|---|
| 200 | 12.832 | 75.681.246 |
| 404 | 801 | 4.044.443 |
| 503 | 441 | 0 |
Agentes personalizados com o Agent Builder
O Agent Builder
permite criar agentes de IA personalizados para fluxos de trabalho operacionais específicos,
combinando instruções personalizadas do sistema com ferramentas integradas (como
platform.core.cases, platform.core.execute_esql e platform.core.search)
e ferramentas definidas pelo usuário (esql, index_search, workflow e mcp).
O exemplo a seguir cria um agente de resposta a incidentes de segurança da Web que
opera diretamente no conjunto de dados kibana_sample_data_logs. Com um
comando de linguagem natural, o agente:
- Executa uma ferramenta ES|QL personalizada (
investigate_suspicious_web_clients) para agregar respostas HTTP não200emkibana_sample_data_logspor IP do cliente. - Distingue atividades maliciosas de reconhecimento e exploração
(
30.156.16.164) de falhas de download benignas. - Invoca uma ferramenta personalizada de fluxo de trabalho do Kibana (
create_security_case) para abrir automaticamente um caso de segurança do Kibana estruturado com as descobertas e as etapas de correção.
Criar ferramentas personalizadas de ES|QL e de criação de casos
Crie uma ferramenta
esqlpersonalizada chamadainvestigate_suspicious_web_clientsque consultakibana_sample_data_logspara os principais endereços IP de clientes que geram códigos de status HTTP não200, além dos códigos de status, país de origem (geo.src), user agents (agent) e URLs solicitados (request):Opção 1: pela interface do Kibana:
- No Kibana, abra o menu de navegação principal e acesse
Elasticsearch > Agentes, clique em Gerenciar todas as ferramentas
(ou acesse diretamente
https://${KIBANA_IP}:5601/app/agent_builder/tools) e clique em Nova ferramenta. Configure os campos da ferramenta:
- ID da ferramenta:
investigate_suspicious_web_clients - Tipo: ES|QL
- Descrição:
Finds the top client IP addresses generating non-200 HTTP error responses in kibana_sample_data_logs, returning their error count, status codes, source country, user agents, and requested URL paths. - Marcadores:
security,logs Consulta ES|QL:
FROM kibana_sample_data_logs | WHERE response != "200" | STATS error_count = COUNT(*), status_codes = VALUES(response), country = VALUES(geo.src), user_agents = VALUES(agent), sample_requests = VALUES(request) BY clientip | SORT error_count DESC | LIMIT 5
- ID da ferramenta:
Clique em Salvar.
- No Kibana, abra o menu de navegação principal e acesse
Elasticsearch > Agentes, clique em Gerenciar todas as ferramentas
(ou acesse diretamente
Opção 2: pela CLI:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "https://${KIBANA_IP}:5601/api/agent_builder/tools" \ -H "kbn-xsrf: true" \ -H "Content-Type: application/json" \ -d '{ "id": "investigate_suspicious_web_clients", "type": "esql", "description": "Finds the top client IP addresses generating non-200 HTTP error responses in kibana_sample_data_logs, returning their error count, status codes, source country, user agents, and requested URL paths.", "tags": ["security", "logs"], "configuration": { "query": "FROM kibana_sample_data_logs | WHERE response != \"200\" | STATS error_count = COUNT(*), status_codes = VALUES(response), country = VALUES(geo.src), user_agents = VALUES(agent), sample_requests = VALUES(request) BY clientip | SORT error_count DESC | LIMIT 5", "params": {} } }' | jq '{id: .id, type: .type}'
Como a ferramenta
platform.core.casesintegrada do Agent Builder é somente leitura (usada para pesquisar e recuperar casos existentes), crie um fluxo de trabalho do Kibana (create-security-case) que invoque a etapacases.createCasenativa do Kibana e registre-o como uma ferramentaworkflowchamadacreate_security_case:Opção 1: pela interface do Kibana:
- Abra o menu de navegação principal e acesse Gerenciamento >
Fluxos de trabalho (ou navegue diretamente para
https://${KIBANA_IP}:5601/app/workflows) e clique em Criar um novo fluxo de trabalho. No editor YAML, cole a seguinte definição de fluxo de trabalho e clique em Salvar:
version: "1" name: Create Security Case description: Creates a new Kibana Case in Elastic Security enabled: true triggers: - type: manual inputs: - name: title type: string required: true description: Title of the security case - name: description type: string required: true description: Detailed markdown description of the incident findings - name: severity type: string default: high description: Severity of the case (low, medium, high, critical) steps: - name: create_case type: cases.createCase with: title: ${{ inputs.title }} description: ${{ inputs.description }} severity: ${{ inputs.severity }} owner: securitySolution tags: - agent-builder - web-logsVolte para a página Ferramentas do Agent Builder (
https://${KIBANA_IP}:5601/app/agent_builder/toolsou acesse Elasticsearch > Agentes e clique em Gerenciar todas as ferramentas) e clique em Nova ferramenta.Configure os campos da ferramenta de fluxo de trabalho e clique em Salvar:
- ID da ferramenta:
create_security_case - Tipo: Fluxo de trabalho
- Fluxo de trabalho: selecione Criar caso de segurança
(
create-security-case) - Descrição:
Creates a new Kibana Case in Elastic Security with a title, markdown description of the incident findings, and severity (low, medium, high, critical). - Rótulos:
security,cases
- ID da ferramenta:
- Abra o menu de navegação principal e acesse Gerenciamento >
Fluxos de trabalho (ou navegue diretamente para
Opção 2: pela CLI:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "https://${KIBANA_IP}:5601/api/workflows" \ -H "kbn-xsrf: true" \ -H "Content-Type: application/json" \ -d '{ "workflows": [ { "yaml": "version: \"1\"\nname: Create Security Case\ndescription: Creates a new Kibana Case in Elastic Security\nenabled: true\ntriggers:\n - type: manual\ninputs:\n - name: title\n type: string\n required: true\n description: Title of the security case\n - name: description\n type: string\n required: true\n description: Detailed markdown description of the incident findings\n - name: severity\n type: string\n default: high\n description: Severity of the case (low, medium, high, critical)\nsteps:\n - name: create_case\n type: cases.createCase\n with:\n title: ${{ inputs.title }}\n description: ${{ inputs.description }}\n severity: ${{ inputs.severity }}\n owner: securitySolution\n tags:\n - agent-builder\n - web-logs\n" } ] }' | jq '.created[] | {id: .id, valid: .valid}' curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "https://${KIBANA_IP}:5601/api/agent_builder/tools" \ -H "kbn-xsrf: true" \ -H "Content-Type: application/json" \ -d '{ "id": "create_security_case", "type": "workflow", "description": "Creates a new Kibana Case in Elastic Security with a title, markdown description of the incident findings, and severity (low, medium, high, critical).", "tags": ["security", "cases"], "configuration": { "workflow_id": "create-security-case" } }' | jq '{id: .id, type: .type}'
Criar o agente personalizado de resposta a incidentes
Crie o agente web-security-incident-responder e atribua a ele as ferramentas investigate_suspicious_web_clients, create_security_case e platform.core.cases:
Opção 1: pela interface do Kibana
- Abra o menu de navegação principal e acesse Elasticsearch >
Agentes (ou navegue diretamente para
https://${KIBANA_IP}:5601/app/agent_builder/agents) e clique em Novo agente. Configure os detalhes do agente:
- ID do agente:
web-security-incident-responder - Nome de exibição:
Web Security Incident Responder - Descrição de exibição:
Investigates suspicious HTTP error activity in web access logs and opens Kibana Security Cases with the findings. - Rótulos:
security,incident-response Instruções personalizadas:
You are an automated Web Security Incident Responder. When asked to investigate suspicious web traffic: 1. Call the `investigate_suspicious_web_clients` tool to inspect the top client IPs generating non-200 HTTP responses in `kibana_sample_data_logs`. 2. Identify any client IP exhibiting malicious reconnaissance or exploitation behavior (such as Nmap scans, WordPress login probing, `/etc/passwd` path traversal, or `/level/15/exec` command execution attempts) versus benign download errors. 3. Call the `create_security_case` tool to open a new Kibana Security Case for the malicious client IP. Set an appropriate `title`, `severity` (`high`), and a well-structured Markdown `description` summarizing the source IP, geolocation, total failed requests, observed attack categories (mapped to MITRE ATT&CK where applicable), sample malicious URLs, and recommended remediation steps. 4. Return a concise summary of your investigation and confirm the created Kibana Case details.Ferramentas: selecione
investigate_suspicious_web_clients,create_security_caseeplatform.core.cases.
- ID do agente:
Clique em Salvar.
- Abra o menu de navegação principal e acesse Elasticsearch >
Agentes (ou navegue diretamente para
Opção 2: pela CLI:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "https://${KIBANA_IP}:5601/api/agent_builder/agents" \ -H "kbn-xsrf: true" \ -H "Content-Type: application/json" \ -d '{ "id": "web-security-incident-responder", "name": "Web Security Incident Responder", "description": "Investigates suspicious HTTP error activity in web access logs and opens Kibana Security Cases with the findings.", "labels": ["security", "incident-response"], "avatar_color": "#E7664C", "avatar_symbol": "IR", "configuration": { "instructions": "You are an automated Web Security Incident Responder. When asked to investigate suspicious web traffic:\n1. Call the `investigate_suspicious_web_clients` tool to inspect the top client IPs generating non-200 HTTP responses in `kibana_sample_data_logs`.\n2. Identify any client IP exhibiting malicious reconnaissance or exploitation behavior (such as Nmap scans, WordPress login probing, `/etc/passwd` path traversal, or `/level/15/exec` command execution attempts) versus benign download errors.\n3. Call the `create_security_case` tool to open a new Kibana Security Case for the malicious client IP. Set an appropriate `title`, `severity` (`high`), and a well-structured Markdown `description` summarizing the source IP, geolocation, total failed requests, observed attack categories (mapped to MITRE ATT&CK where applicable), sample malicious URLs, and recommended remediation steps.\n4. Return a concise summary of your investigation and confirm the created Kibana Case details.", "tools": [ { "tool_ids": [ "investigate_suspicious_web_clients", "create_security_case", "platform.core.cases" ] } ] } }' | jq '{id: .id, name: .name}'
Executar o agente e verificar o caso do Kibana
Inicie uma conversa com o agente Web Security Incident Responder:
Opção 1: pela interface do Kibana
- Abra o menu de navegação principal e acesse Elasticsearch >
Agentes (ou navegue diretamente para
https://${KIBANA_IP}:5601/app/agent_builder). - Selecione Web Security Incident Responder no seletor de agentes
e verifique se o conector de LLM (
${LLM_CONNECTOR_NAME}) está selecionado. Insira o seguinte comando:
Investigate the top client IPs generating HTTP errors in our web logs, identify any attacker performing reconnaissance or exploitation, and open a high-severity Kibana Security Case with your findings.
- Abra o menu de navegação principal e acesse Elasticsearch >
Agentes (ou navegue diretamente para
Opção 2: pela CLI:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "https://${KIBANA_IP}:5601/api/agent_builder/converse" \ -H "kbn-xsrf: true" \ -H "Content-Type: application/json" \ -d "{ \"agent_id\": \"web-security-incident-responder\", \"connector_id\": \"${CONNECTOR_ID}\", \"input\": \"Investigate the top client IPs generating HTTP errors in our web logs, identify any attacker performing reconnaissance or exploitation, and open a high-severity Kibana Security Case with your findings.\" }" | jq '{ status: .status, tools_called: [.steps[] | select(.type == "tool_call") | .tool_id], response: .response.message }'A saída será parecida com esta:
{ "status": "completed", "tools_called": [ "investigate_suspicious_web_clients", "create_security_case" ], "response": "I have completed the investigation into the web logs. \n\nThe analysis identified client IP **`30.156.16.164`** (located in the US) as a malicious actor. This IP generated 100 failed requests (HTTP 404) exhibiting clear patterns of reconnaissance and exploitation:\n\n* **Reconnaissance:** Extensive probing for WordPress installations and plugins, as well as Nmap scan signatures (e.g., `/nmaplowercheck...`).\n* **Path Traversal:** Attempts to access sensitive system files via `/etc/passwd`.\n* **Remote Command Execution (RCE):** Attempts to execute commands through paths such as `/level/15/exec`.\n* **Application Probing:** Targeting common management interfaces including Tomcat, Axis2, and JMX consoles.\n\nAs a result, I have opened a **high-severity** Kibana Security Case to track this incident:\n\n* **Case ID:** `1f0e00a5-cc89-4f62-b305-f286fae3d70e`\n* **Title:** Malicious Reconnaissance and Exploitation Attempt from 30.156.16.164\n* **Recommended Action:** Immediately block the IP at the firewall/WAF and verify that no unauthorized access was achieved." }
Verifique o caso de segurança do Kibana recém-criado:
Opção 1: pela interface do Kibana
- Abra o menu de navegação principal e acesse Segurança > Casos
(ou acesse diretamente
https://${KIBANA_IP}:5601/app/security/cases). - Clique em Tentativa de reconhecimento e exploração maliciosa de 30.156.16.164 para ver o resumo do incidente em Markdown formatado, a análise do MITRE ATT&CK, exemplos de URLs maliciosos e a lista de verificação de correção.
- Abra o menu de navegação principal e acesse Segurança > Casos
(ou acesse diretamente
Opção 2: pela CLI:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ "https://${KIBANA_IP}:5601/api/cases/_find" \ -H "kbn-xsrf: true" \ | jq '.cases[0] | {id: .id, title: .title, severity: .severity, status: .status, tags: .tags, description: .description}'A saída será parecida com esta:
{ "id": "1f0e00a5-cc89-4f62-b305-f286fae3d70e", "title": "Malicious Reconnaissance and Exploitation Attempt from 30.156.16.164", "severity": "high", "status": "open", "tags": [ "agent-builder", "web-logs" ], "description": "### Incident Summary\n**Source IP:** `30.156.16.164`\n**Geolocation:** US\n**Total Failed Requests:** 100\n**Status Codes Observed:** 404\n\n### Analysis\nThe client IP exhibited clear patterns of malicious reconnaissance and exploitation attempts:\n\n- **Reconnaissance (MITRE ATT&CK T1595):** Extensive probing for WordPress installations (`/wp-login.php`, various plugin paths) and Nmap scan signatures (`/nmaplowercheck...`).\n- **Path Traversal (MITRE ATT&CK T1083):** Attempted to access sensitive system files via `/cgi-bin/mj_wwwusr?passw=&list=GLOBAL&user=&func=help&extra=/../../../../../../../../etc/passwd`.\n- **Remote Command Execution (MITRE ATT&CK T1203):** Attempts to execute commands via paths like `/level/15/exec/-/configure/http`.\n- **Application Probing:** Targeted common management interfaces such as `/tomcat/manager/html/`, `/axis2/axis2-admin/`, and `/jmx-console/`.\n\n### Sample Malicious URLs\n- `/cgi-bin/mj_wwwusr?passw=&list=GLOBAL&user=&func=help&extra=/../../../../../../../../etc/passwd`\n- `/level/15/exec/-/configure/http`\n- `/nmaplowercheck1331872976`\n- `/wp-login.php`\n\n### Recommended Remediation\n1. **Block IP:** Immediately block `30.156.16.164` at the network firewall/WAF.\n2. **Log Review:** Verify that all requests from this IP resulted in 404 errors and no unauthorized access was gained.\n3. **Hardening:** Ensure that sensitive directories (e.g., `/cgi-bin/`, `/manager/`) are properly restricted." }
Descoberta de ataques
A Descoberta de Ataques
é um recurso de IA no Elastic Security que analisa alertas de segurança abertos, os mapeia
para o framework MITRE ATT&CK e correlaciona alertas, endereços IP
e contas de usuário relacionados em cadeias de ataque visuais. Como a descoberta de ataques
requer um modelo grande para sintetizar saídas JSON estruturadas com vários alertas
de maneira confiável, use um modelo de classe 30B ou maior (como gemma4:31b).
Gerar alertas de segurança com base nos registros de amostra
Por padrão, o conjunto de dados kibana_sample_data_logs contém registros brutos de acesso à Web
e não preenche o índice de alertas de segurança do Elastic Security
(.alerts-security.alerts-default). Além disso, o gerador de dados de amostra do Kibana
atribui valores futuros de @timestamp (até 30 dias à frente) às 100 solicitações de verificação e
exploração de 30.156.16.164, o que as coloca fora da
janela de now-30d a now consultada pelas regras de detecção do Elastic Security.
Para demonstrar a Descoberta de Ataques com os registros de amostra, mude o @timestamp dos registros de 30.156.16.164 para 30 dias atrás, enriqueça-os com os campos source.ip e user.name do Elastic Common Schema (ECS) e crie duas regras de detecção que geram alertas de segurança com base nesses eventos:
Mapeie
clientipparasource.ip, definauser.name(www-data) e mude@timestamppara 30 dias atrás nos registros30.156.16.164emkibana_sample_data_logs*:curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "https://${ELASTICSEARCH_IP}:9200/kibana_sample_data_logs*/_update_by_query?conflicts=proceed&refresh=true" \ -H "Content-Type: application/json" \ -d '{ "query": { "term": { "clientip": "30.156.16.164" } }, "script": { "source": "ctx._source.source = [\"ip\": ctx._source.clientip]; ctx._source.user = [\"name\": \"www-data\"]; ctx._source[\"@timestamp\"] = ZonedDateTime.parse(ctx._source[\"@timestamp\"]).minusDays(30).toString();", "lang": "painless" } }' | jq '{updated: .updated, failures: .failures}'Crie duas regras de detecção do Elastic Security direcionadas a
kibana_sample_data_logs*. A primeira regra detecta sondagem de login do Nmap e do WordPress (Reconnaissance), e a segunda detecta travessia de caminho do/etc/passwde tentativas de execução de comando remoto (Initial Access):curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "https://${KIBANA_IP}:5601/api/detection_engine/rules" \ -H "kbn-xsrf: true" \ -H "Content-Type: application/json" \ -d '{ "name": "Suspicious Web Reconnaissance and Vulnerability Scanning", "description": "Detects Nmap active scanning and WordPress login/plugin probing in web access logs.", "risk_score": 47, "severity": "medium", "type": "query", "language": "kuery", "query": "clientip: \"30.156.16.164\" and (request: *nmap* or request: *wp-login*)", "index": ["kibana_sample_data_logs*"], "from": "now-30d", "interval": "1h", "max_signals": 2, "enabled": true, "threat": [ { "framework": "MITRE ATT&CK", "tactic": { "id": "TA0043", "name": "Reconnaissance", "reference": "https://attack.mitre.org/tactics/TA0043/" }, "technique": [ { "id": "T1595", "name": "Active Scanning", "reference": "https://attack.mitre.org/techniques/T1595/" } ] } ] }' | jq '{id: .id, name: .name, enabled: .enabled}' curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "https://${KIBANA_IP}:5601/api/detection_engine/rules" \ -H "kbn-xsrf: true" \ -H "Content-Type: application/json" \ -d '{ "name": "Web Path Traversal and Remote Command Execution Attempt", "description": "Detects path traversal attempts targeting /etc/passwd and Cisco IOS privilege level 15 remote command execution (/level/15/exec).", "risk_score": 73, "severity": "high", "type": "query", "language": "kuery", "query": "clientip: \"30.156.16.164\" and (request: *passwd* or request: *exec*)", "index": ["kibana_sample_data_logs*"], "from": "now-30d", "interval": "1h", "max_signals": 2, "enabled": true, "threat": [ { "framework": "MITRE ATT&CK", "tactic": { "id": "TA0001", "name": "Initial Access", "reference": "https://attack.mitre.org/tactics/TA0001/" }, "technique": [ { "id": "T1190", "name": "Exploit Public-Facing Application", "reference": "https://attack.mitre.org/techniques/T1190/" } ] } ] }' | jq '{id: .id, name: .name, enabled: .enabled}'Aguarde cerca de 60 segundos para que a execução inicial da regra seja concluída e verifique se quatro alertas foram indexados em
.alerts-security.alerts-default:curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ "https://${ELASTICSEARCH_IP}:9200/.alerts-security.alerts-default/_search?size=4" \ | jq '.hits.hits[]._source | {rule: .["kibana.alert.rule.name"], source_ip: .source.ip, user_name: .user.name, request: .request}'A saída será parecida com esta:
{ "rule": "Web Path Traversal and Remote Command Execution Attempt", "source_ip": "30.156.16.164", "user_name": "www-data", "request": "/cgi-bin/mj_wwwusr?passw=&list=GLOBAL&user=&func=help&extra=/../../../../../../../../etc/passwd" } { "rule": "Web Path Traversal and Remote Command Execution Attempt", "source_ip": "30.156.16.164", "user_name": "www-data", "request": "/cgi-bin/mj_wwwusr?passw=&list=GLOBAL&user=&func=help&extra=/../../../../../../../../etc/passwd" } { "rule": "Suspicious Web Reconnaissance and Vulnerability Scanning", "source_ip": "30.156.16.164", "user_name": "www-data", "request": "/nmaplowercheck1331872976" } { "rule": "Suspicious Web Reconnaissance and Vulnerability Scanning", "source_ip": "30.156.16.164", "user_name": "www-data", "request": "/nmaplowercheck1331873003" }
Executar a Descoberta de Ataques
- No Kibana, abra o menu de navegação principal e acesse Segurança >
Descoberta de ataques (ou navegue diretamente para
https://${KIBANA_IP}:5601/app/security/attack_discovery). - Selecione o conector de LLM (
${LLM_CONNECTOR_NAME}) no menu de conectores na parte de cima da página. - Clique em Gerar para analisar os alertas abertos no
.alerts-security.alerts-default.
Também é possível acionar e inspecionar a Descoberta de Ataques na CLI:
export EXECUTION_UUID=$(curl -sk -u "elastic:${ELASTIC_PASSWORD}" \
-X POST "https://${KIBANA_IP}:5601/api/attack_discovery/_generate" \
-H "kbn-xsrf: true" \
-H "elastic-api-version: 2023-10-31" \
-H "Content-Type: application/json" \
-d "{
\"alertsIndexPattern\": \".alerts-security.alerts-default\",
\"anonymizationFields\": [
{\"id\": \"_id\", \"field\": \"_id\", \"allowed\": true},
{\"id\": \"@timestamp\", \"field\": \"@timestamp\", \"allowed\": true},
{\"id\": \"kibana.alert.original_time\", \"field\": \"kibana.alert.original_time\", \"allowed\": true},
{\"id\": \"message\", \"field\": \"message\", \"allowed\": true},
{\"id\": \"kibana.alert.rule.name\", \"field\": \"kibana.alert.rule.name\", \"allowed\": true},
{\"id\": \"kibana.alert.rule.description\", \"field\": \"kibana.alert.rule.description\", \"allowed\": true},
{\"id\": \"kibana.alert.severity\", \"field\": \"kibana.alert.severity\", \"allowed\": true},
{\"id\": \"kibana.alert.risk_score\", \"field\": \"kibana.alert.risk_score\", \"allowed\": true},
{\"id\": \"kibana.alert.rule.threat.tactic.id\", \"field\": \"kibana.alert.rule.threat.tactic.id\", \"allowed\": true},
{\"id\": \"kibana.alert.rule.threat.tactic.name\", \"field\": \"kibana.alert.rule.threat.tactic.name\", \"allowed\": true},
{\"id\": \"kibana.alert.rule.threat.technique.id\", \"field\": \"kibana.alert.rule.threat.technique.id\", \"allowed\": true},
{\"id\": \"kibana.alert.rule.threat.technique.name\", \"field\": \"kibana.alert.rule.threat.technique.name\", \"allowed\": true},
{\"id\": \"source.ip\", \"field\": \"source.ip\", \"allowed\": true},
{\"id\": \"user.name\", \"field\": \"user.name\", \"allowed\": true}
],
\"apiConfig\": {
\"actionTypeId\": \".gen-ai\",
\"connectorId\": \"${CONNECTOR_ID}\"
},
\"replacements\": {},
\"size\": 20,
\"subAction\": \"invokeAI\"
}" | jq -r '.execution_uuid')
echo "Attack Discovery execution UUID: ${EXECUTION_UUID}"
Quando a geração terminar (aproximadamente 60 a 90 segundos), recupere a cadeia de ataque descoberta:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \
"https://${KIBANA_IP}:5601/api/attack_discovery/_find" \
-H "kbn-xsrf: true" \
-H "elastic-api-version: 2023-10-31" \
| jq '.data[0]'
A descoberta de ataque gerada correlaciona os quatro alertas em um único incidente com IP de origem, conta de usuário e táticas do MITRE ATT&CK vinculados:
{
"alert_ids": [
"05c30118398ea2a8b70c55ce81fdb559dbe14c460a8a95253bd81719415a28b5",
"73ebaeca440b463ffa5812da5b68a2485d465a648ffaf16385a54d8bae9a53d2",
"60ae9f16b40cec1bdc27674ade0e5eb69680ccca3d956fbf2a75418843799987",
"de503ae52444db7712d062c54750f413b90a75521cdfff0de9bdf41a64141346"
],
"alert_rule_uuid": "attack_discovery_ad_hoc_rule_id",
"alert_start": "2026-10-03T01:05:00.384Z",
"alert_updated_at": "2026-10-03T01:05:03.460Z",
"alert_workflow_status": "open",
"alert_workflow_status_updated_at": "2026-10-03T01:05:03.460Z",
"connector_id": "<GENERATED_CONNECTOR_ID>",
"connector_name": "<GENERATED_CONNECTOR_ID>",
"details_markdown": "- The attack sequence began at `2026-10-01T12:43:41.756Z` when an actor using IP `30.156.16.164` performed active reconnaissance scanning against the server.\n- Following the initial scan, the attacker attempted to exploit a path traversal vulnerability at `2026-10-01T12:43:51.756Z` and again at `2026-10-01T12:44:02.756Z`, specifically targeting the `/etc/passwd` file via the `mj_wwwusr` CGI endpoint.\n- The activity concluded with further reconnaissance scanning at `2026-10-01T12:44:21.756Z` from the same source IP `30.156.16.164`.\n- All requests were associated with the user `www-data` and resulted in 404 errors, suggesting the attempts were unsuccessful.",
"entity_summary_markdown": "Activity targeting user `www-data` from source IP `30.156.16.164`.",
"generation_uuid": "<EXECUTION_UUID>",
"id": "1f71ed004c7340d5088d242ca2483525a164a9ea9a80a3f90bd1f514d43231bc",
"mitre_attack_tactics": [
"Reconnaissance",
"Initial Access"
],
"risk_score": 240,
"summary_markdown": "An attacker from `30.156.16.164` performed reconnaissance and path traversal attempts against the system.",
"timestamp": "2026-10-03T01:05:00.384Z",
"title": "Web Reconnaissance and Exploitation Attempt",
"user_id": "elastic",
"user_name": "elastic",
"users": [
{
"id": "elastic",
"name": "elastic"
}
],
"index": ".internal.adhoc.alerts-security.attack.discovery.alerts-default-000001"
}