Panoramica
Questa guida illustra il deployment end-to-end di Elastic Stack (Elasticsearch e Kibana) su un cluster condiviso Google Distributed Cloud (GDC) con air gap e mostra come integrarlo con un servizio di modello linguistico di grandi dimensioni (LLM) per valutare le funzionalità di AI generativa di Elastic.
Il deployment utilizza l'operatore Elastic Cloud on Kubernetes (ECK) ufficiale disponibile nel Marketplace GDC con air gap. L'operatore si installa in uno spazio dei nomi del progetto su un cluster Kubernetes condiviso e automatizza il provisioning e la gestione del ciclo di vita delle risorse Elasticsearch e Kibana. Una volta eseguito il deployment, Kibana si connette a un endpoint di inferenza LLM (come Gemini gestito su GDC o un servizio compatibile con OpenAI autogestito come Ollama o vLLM). Questa integrazione supporta le funzionalità di AI generativa di Elastic, tra cui Elastic AI Agent, gli agenti personalizzati creati con Agent Builder e Attack Discovery.
Questa guida è stata scritta per Elastic v9.4.0, Elastic Kubernetes Operator v3.4.0 e GDC v1.16.2. Alcune istruzioni potrebbero variare leggermente se utilizzi versioni diverse.
Architettura
L'architettura è costituita da un deployment di Elastic Stack gestito dall'operatore ECK all'interno di uno spazio dei nomi del progetto su un cluster utente condiviso di GDC. Kibana comunica sulla rete GDC interna con un endpoint di inferenza LLM.

I componenti chiave di questa architettura includono:
- Cluster GDC condiviso: un cluster Kubernetes condiviso con ambito a livello di organizzazione collegato a più progetti, che ospita pacchetti software e workload delle applicazioni di Marketplace GDC.
- Progetto GDC: lo spazio dei nomi del progetto nel cluster condiviso che isola l'operatore ECK, il cluster Elasticsearch, l'istanza Kibana e i secret e i criteri di rete Kubernetes associati.
- Operatore Elastic Cloud on Kubernetes (ECK): installato dal Marketplace GDC nello spazio dei nomi del progetto, l'operatore gestisce le risorse personalizzate
ElasticsearcheKibanae riconcilia la licenza Elastic richiesta per le funzionalità di AI generativa e i connettori LLM esterni. - Elasticsearch: archivia e indicizza i dati operativi e di sicurezza ed esegue query di ricerca ed ES|QL generate dagli agenti e dalle funzionalità di AI di Kibana.
- Kibana: ospita le funzionalità di AI generativa di Elastic, tra cui Elastic AI Agent (per l'analisi dei log in linguaggio naturale e la generazione di ES|QL), Agent Builder (per la creazione di agenti e strumenti conversazionali personalizzati sui dati di Elasticsearch) e Attack Discovery (per la correlazione degli avvisi di sicurezza e l'analisi della catena di attacco). Kibana gestisce anche i connettori di azioni LLM che orchestrano la chiamata di strumenti in più passaggi tra l'utente, Elasticsearch e il servizio LLM.
- Servizio LLM: un servizio LLM gestito (come Gemini su GDC) o un server di inferenza autogestito (come Ollama o vLLM) che fornisce funzionalità di completamento della chat e chiamata di strumenti a Kibana sulla rete GDC interna.
Prima di iniziare
Prima di iniziare il deployment, assicurati che l'ambiente GDC soddisfi i seguenti prerequisiti e presupposti:
- Cluster Kubernetes condiviso e progetto GDC: hai accesso a un cluster condiviso GDC esistente e a un progetto GDC collegato a quel cluster. Il progetto funge da container dello spazio dei nomi per tutte le risorse Kubernetes di cui viene eseguito il deployment in questa guida. Assicurati che il cluster disponga di risorse di computing e archiviazione permanente sufficienti per ospitare i workload dell'operatore ECK, di Elasticsearch e di Kibana.
- Servizio LLM: hai accesso di rete a un servizio LLM, ovvero il servizio Gemini gestito (se disponibile nel tuo ambiente GDC) o un servizio LLM compatibile con OpenAI autogestito (ad esempio,
gemma4:31bfornito tramite Ollama o vLLM in un progetto GPU). Il deployment dell'infrastruttura LLM non rientra nell'ambito di questa guida, ma i requisiti specifici di modello e API sono descritti in dettaglio in Requisiti per il servizio LLM. Recupera la versione di Elastic fornita con il tuo ambiente 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')Configura le variabili di ambiente a cui verrà fatto riferimento in questa guida. Modifica questi valori in base alle esigenze del tuo ambiente specifico:
# 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"Configura
gdcloud:gdcloud config set project ${PROJECT_ID} gdcloud config set core/zone ${ZONE}Richiedi o associa i ruoli IAM a livello di progetto richiesti per il tuo utente nel progetto GDC di destinazione (
${PROJECT_ID}). In base alla documentazione sul ciclo di vita di GDC Marketplace e ai requisiti di networking GDC, sono necessari almeno i seguenti ruoli di progetto:- Namespace Admin (
namespace-admin): accesso amministrativo completo per creare e gestire workload, secret, servizi e risorse personalizzate di Kubernetes all'interno dello spazio dei nomi del progetto sul cluster condiviso. - Project Viewer (
project-viewer): accesso in lettura per visualizzare il progetto esistente e le relative risorse nella console e nell'interfaccia a riga di comando di GDC. - Marketplace Editor (
marketplace-editor): autorizzazione per installare, eseguire l'upgrade e disinstallare i pacchetti software di GDC Marketplace nel progetto. - Project NetworkPolicy Admin (
project-networkpolicy-admin): autorizzazione per configurare le regoleProjectNetworkPolicyglobali che consentono al traffico tra progetti ed esterno di raggiungere i servizi LoadBalancer di Kibana ed Elasticsearch.
Chiedi all'amministratore IAM dell'organizzazione o all'amministratore IAM del progetto di concedere questi ruoli (o esegui i seguenti comandi se disponi dei privilegi amministrativi):
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- Namespace Admin (
Recupera il kubeconfig per il cluster condiviso e configura un alias della shell
kuperkubectl:KUBECONFIG=kubeconfig-${CLUSTER_NAME}.yaml gdcloud clusters \ get-credentials ${CLUSTER_NAME} \ --zone ${ZONE} alias ku="kubectl --kubeconfig=kubeconfig-${CLUSTER_NAME}.yaml"
Installa il pacchetto marketplace Elastic
In questa sezione, installerai l'operatore Elastic Cloud on Kubernetes (ECK) nello spazio dei nomi del tuo progetto dal Marketplace GDC con air gap seguendo la guida al ciclo di vita del Marketplace GDC.
Installa tramite l'interfaccia a riga di comando gdcloud (o la console GDC)
Elenca i pacchetti software di Marketplace disponibili per confermare il nome del servizio e la versione disponibile:
gdcloud marketplace services listL'output dovrebbe essere simile al seguente:
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>Installa il pacchetto marketplace Elastic nel cluster condiviso di destinazione:
gdcloud marketplace services install ${MARKETPLACE_SERVICE_NAME} \ --cluster=${CLUSTER_NAME} \ --zone=${ZONE} \ --project ${PROJECT_ID}*(In alternativa, nella console GDC, seleziona il tuo progetto
${PROJECT_ID}, vai a Marketplace, seleziona la scheda Elastic Cloud on Kubernetes, fai clic su Installa, seleziona il cluster${CLUSTER_NAME}e fai clic su Installa).*Verifica che il pod e lo StatefulSet
elastic-operatorsiano in esecuzione nello spazio dei nomi del progetto (${PROJECT_ID}):ku get statefulset,pods -n ${PROJECT_ID}L'output dovrebbe essere simile al seguente:
NAME READY AGE statefulset.apps/elastic-operator 1/1 3m NAME READY STATUS RESTARTS AGE pod/elastic-operator-0 1/1 Running 0 3m
Esegui il deployment di Elastic Stack
Crea il manifest di Elastic Stack
Il pacchetto Marketplace GDC esegue il deployment dell'operatore ECK preconfigurato per eseguire il pull delle immagini container di Elasticsearch e Kibana dal registry Harbor locale con air gap.
Crea elastic-stack.yaml per eseguire il deployment di un cluster Elasticsearch a 1 nodo, un'istanza dell'interfaccia utente Kibana, servizi HTTP LoadBalancer e un allow-all NetworkPolicy nello spazio dei nomi del progetto (${PROJECT_ID}). Il manifest include anche una licenza Secret riconciliata dall'operatore ECK, configurata qui come prova Enterprise di 30 giorni (eck-trial-license) nel caso in cui non disponi ancora di una licenza 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
Applica e verifica il deployment
Applica il manifest:
ku apply -f elastic-stack.yaml
Applica un ProjectNetworkPolicy globale in modo che le regole firewall GDC consentano il traffico in entrata verso gli IP LoadBalancer e il traffico tra progetti (ad esempio la comunicazione con il servizio 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
Monitora i pod, i servizi e le risorse personalizzate finché Elasticsearch e Kibana non raggiungono lo stato Running e Ready e ottengono gli IP LoadBalancer su ${CLUSTER_NAME}:
ku get pods,svc,elasticsearch,kibana -n ${PROJECT_ID}
Una volta pronte le risorse, l'output dovrebbe essere simile al seguente:
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
Esporta gli IP esterni del bilanciatore del carico assegnati a Elasticsearch e Kibana per utilizzarli nei passaggi successivi:
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"
Recupera le credenziali e accedi alla UI di Kibana
Recupera la password del super utente elastic
ECK genera automaticamente un secret Kubernetes (elastic-cluster-es-elastic-user) contenente la password predefinita del super utente elastic. Recuperalo e decodificalo con:
export ELASTIC_PASSWORD=$(ku get secret elastic-cluster-es-elastic-user \
-n ${PROJECT_ID} \
-o jsonpath='{.data.elastic}' | base64 -d)
echo "${ELASTIC_PASSWORD}"
Verifica la connettività e accedi
Verifica che Elasticsearch sia raggiungibile tramite HTTPS dalla tua workstation:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" "https://${ELASTICSEARCH_IP}:9200"Apri
https://${KIBANA_IP}:5601nel browser, accetta l'avviso relativo al certificato autofirmato e accedi con:- Nome utente:
elastic - Password:
<output from ELASTIC_PASSWORD above>
- Nome utente:
Verifica la licenza Elastic
Le funzionalità di AI generativa di Elastic (tra cui Elastic AI Agent, Agent Builder e Attack Discovery) e i connettori LLM esterni richiedono una licenza Enterprise (o una licenza di prova Enterprise attiva di 30 giorni).
Quando un cluster Elasticsearch è gestito dall'operatore ECK, license-controller di ECK riconcilia continuamente la licenza del cluster con i secret Kubernetes nello spazio dei nomi dell'operatore (${PROJECT_ID}). Se non disponi ancora di una licenza Enterprise di produzione, il secret eck-trial-license incluso in elastic-stack.yaml (license.k8s.elastic.co/type:
enterprise_trial) indica a ECK di eseguire il provisioning e la manutenzione automatica di una licenza di prova Enterprise di 30 giorni. Tieni presente che l'avvio manuale di una prova tramite POST
/_license/start_trial senza un secret di licenza ECK causerà il ripristino del cluster a basic nel ciclo di riconciliazione successivo.
Per verificare la licenza attiva tramite l'interfaccia a riga di comando:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \
"https://${ELASTICSEARCH_IP}:9200/_license" | jq .
*(In alternativa, in Kibana, apri il menu di navigazione e vai a Management
> Dev Tools, quindi esegui GET /_license nella scheda Console).*
L'output dovrebbe essere simile al seguente, con "status": "active"
e "type": "enterprise" (o "type": "trial" se utilizzi la licenza di prova):
{
"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
}
}
Caricare un set di dati di esempio integrato
Per valutare le funzionalità di AI di Elastic senza scrivere pipeline di importazione dei dati personalizzate o richiedere l'accesso a internet esterno, puoi caricare il set di dati di log web di esempio (kibana_sample_data_logs) integrato di Kibana. Poiché l'archivio dei dati di esempio è incluso nell'immagine container di Kibana, l'installazione avviene in pochi secondi in un ambiente air-gap. Il set di dati compila 14.074 documenti di log di accesso web contenenti indirizzi IP client (clientip), codici di stato HTTP (response), paesi di origine e di destinazione (geo.src, geo.dest), byte trasferiti (bytes), URL e stringhe user agent.
Consulta
Aggiungere dati di esempio in Kibana
e
elastic-datasets-builtin.md
per altri set di dati integrati (flights e ecommerce).
Carica il set di dati
Esegui questo comando per caricare kibana_sample_data_logs tramite l'endpoint dei dati di esempio interni di 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"
Verifica il set di dati di esempio
Verifica che il set di dati di esempio logs sia contrassegnato come installed in Kibana e
conferma che il stream di dati di supporto contenga 14,074 documenti in
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"
L'output dovrebbe essere simile al seguente:
{
"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
Requisiti per il servizio LLM
In un ambiente GDC con air gap, gli LLM gestiti da Elastic ospitati nel cloud e i provider di LLM SaaS pubblici esterni non sono raggiungibili. A seconda del tuo ambiente GDC:
- Gemini gestito su GDC: negli ambienti GDC che offrono Gemini come servizio LLM gestito, puoi connettere Kibana direttamente al servizio Gemini gestito, se disponibile.
- Servizio LLM autogestito: in alternativa, puoi connettere Elastic a un servizio LLM autogestito ospitato nel tuo ambiente GDC (ad esempio, sui nodi GPU GDC).
Il deployment di un servizio LLM autogestito non rientra nell'ambito di questa guida. Quando esegui il provisioning o selezioni un servizio LLM con gestione indipendente per Elastic, assicurati che soddisfi i requisiti derivati dalle guide ufficiali di Elastic:
- Configurare l'accesso ai LLM
- LLM personalizzati autogestiti (vedi anche Connettersi a vLLM in ambienti con air gap)
- Osservabilità di LLM e AI agentica
Riepilogo dei requisiti tecnici
I seguenti punti riassumono i requisiti chiave relativi ad API, funzionalità del modello, finestra di contesto e runtime necessari per il funzionamento affidabile di un servizio LLM autogestito con l'agente AI, l'Agent Builder e l'Attack Discovery di Elastic. Tieni presente che i nomi dei parametri di configurazione specifici citati nelle sezioni seguenti (ad esempio num_ctx, num_predict, repeat_penalty, repeat_last_n, OLLAMA_FLASH_ATTENTION e OLLAMA_NUM_PARALLEL) vengono forniti utilizzando Ollama come esempio. Applica le impostazioni equivalenti se utilizzi un altro motore di inferenza come vLLM.
- API di completamento della chat compatibile con OpenAI (
/v1/chat/completions) con chiamata di funzione nativa:- Qualsiasi server di inferenza autogestito (come Ollama o vLLM) che espone un endpoint
/v1/chat/completionscompatibile con OpenAI (incluso lo streaming di eventi inviati dal server tramite"stream": true) può essere integrato utilizzando il connettore OpenAI di Kibana con il provider impostato su Altro (servizio compatibile con OpenAI). - Abilita la chiamata di funzioni native (
"enableNativeFunctionCalling": true) sul connettore Kibana quando utilizzi modelli che supportano la chiamata di strumenti nativi del cloud (comegemma4:31b). Per impostazione predefinita ("enableNativeFunctionCalling": false), Kibana utilizza la chiamata di funzioni simulate basata su prompt, che è meno affidabile durante le chiamate di strumenti nidificati e può non riuscire con errori di analisi (comeError: Missing name for tool use). L'attivazione della chiamata di funzioni native delega la formattazione della chiamata di strumenti direttamente al protocollo OpenAItoolsdel server di inferenza.
- Qualsiasi server di inferenza autogestito (come Ollama o vLLM) che espone un endpoint
- Modello ottimizzato per le istruzioni con funzionalità native di chiamata di funzioni e strumenti:
- Elastic AI Agent (
elastic-ai-agent) e gli agenti personalizzati di Agent Builder si basano sulla chiamata di strumenti in più passaggi (platform.core.list_indices,platform.core.get_index_mapping,platform.core.search,platform.core.execute_esqleplatform.core.cases). Utilizzano questi strumenti per scoprire i mapping degli indici, generare ed eseguire query ES|QL su Elasticsearch e sintetizzare i risultati. - Il modello scelto deve essere ottimizzato per le istruzioni e in grado di generare chiamate di strumenti cloud-native valide, output JSON strutturati e query ES|QL. Consulta la matrice delle prestazioni LLM per l'osservabilità e la matrice delle prestazioni LLM per Elastic Security per i criteri di valutazione del modello.
- Elastic AI Agent (
- Finestra contestuale di almeno
128,000token (ad esempio,num_ctx: 131072in Ollama):- Sebbene la documentazione LLM personalizzata autogestita generale di Elastic citi un minimo di 64.000 token, una finestra contestuale di 128.000 token (
131,072token) è fortemente consigliata per Elastic agente AI (elastic-ai-agent). - Durante le indagini in più fasi, l'agente combina le istruzioni di sistema e le definizioni degli strumenti con gli schemi di indice, la documentazione ES|QL e i risultati di ricerca non elaborati (ad esempio, recuperando fino a 100 documenti di esempio per ispezionare i valori dei campi prima dell'aggregazione). Una singola indagine può superare i 70.000 token nella cronologia delle conversazioni, il che fa sì che una finestra contestuale di 64.000 token tronchi il prompt a metà conversazione.
- Tieni presente che in Ollama, un'istruzione
PARAMETER num_ctxintegrata nelModelfiledi un modello esegue l'override della variabile di ambienteOLLAMA_CONTEXT_LENGTHdel container. Assicurati che siaOLLAMA_CONTEXT_LENGTH=131072siaPARAMETER num_ctx 131072inModelfiledel modello siano configurati (su una GPU NVIDIA A100 o H100 da 80 GB,gemma4:31bal contesto128Ksi adatta comodamente alla VRAM).
- Sebbene la documentazione LLM personalizzata autogestita generale di Elastic citi un minimo di 64.000 token, una finestra contestuale di 128.000 token (
- Limite di token di output e penalità di ripetizione moderata (ad esempio,
num_predict: 8192erepeat_penalty: 1.05in Ollama):- L'adattatore di stream OpenAI di Kibana tratta
"finish_reason": "length"come un erroreToken limit reachedirreversibile e invia"temperature": 0(decodifica greedy) in ogni richiesta. - Per i modelli in grado di ragionare (come
gemma4:31b, che genera token di pensiero interni prima di emettere chiamate di strumenti), configura un limite di token di output di almeno8,192token e una penalità di ripetizione moderata. Ad esempio, in Ollama, impostanum_predict: 8192,repeat_penalty: 1.05erepeat_last_n: 64. Evita penalità per ripetizione eccessivamente aggressive (ad esempio1.15+su finestre di grandi dimensioni), in quanto penalizzano il modello quando copia una query ES|QL dal blocco di ragionamento interno nel JSON della chiamata di strumento finale.
- L'adattatore di stream OpenAI di Kibana tratta
- Accelerazione GPU, Flash Attention e slot di cache KV paralleli (<60 s per turno):
- Il connettore OpenAI di Kibana applica un timeout HTTP predefinito di 60 secondi per ogni turno di streaming.
- Poiché un singolo turno dell'agente può attivare chiamate secondarie LLM nidificate (ad esempio, per tradurre le richieste di ricerca in linguaggio naturale in query ES|QL mentre la conversazione principale dell'agente è attiva), l'esecuzione di un server di inferenza con un solo slot di cache KV può causare l'espulsione del contesto memorizzato nella cache della conversazione principale e rallentare i turni successivi.
- In Ollama, imposta
OLLAMA_FLASH_ATTENTION=1eOLLAMA_NUM_PARALLEL=2in modo che Flash Attention acceleri la valutazione del contesto lungo e la conversazione principale e le chiamate secondarie nidificate mantengano slot di cache KV separati nella memoria della GPU.
Configura il connettore LLM in Kibana
Una volta che il servizio LLM compatibile con OpenAI è in esecuzione e raggiungibile dal pod Kibana, configura un connettore OpenAI in Kibana utilizzando l'interfaccia a riga di comando o l'interfaccia utente di Kibana.
Opzione 1: crea il connettore tramite l'interfaccia a riga di comando
Esegui il seguente comando
POST /api/actions/connectorper creare un connettore OpenAI (.gen-ai) che punta al tuo servizio LLM (${LLM_API_URL}) con la chiamata di funzioni nativa abilitata ("enableNativeFunctionCalling": true). Kibana genera e assegna automaticamente un ID connettore univoco: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 .Verifica che il connettore
${LLM_CONNECTOR_NAME}esista e non abbia secret mancanti elencando i connettori di Kibana e filtrando per nome:curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ "https://${KIBANA_IP}:5601/api/actions/connectors" \ -H "kbn-xsrf: true" \ | jq ".[] | select(.name == \"${LLM_CONNECTOR_NAME}\")"L'output dovrebbe essere simile al seguente:
{ "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 }Per testare la connettività end-to-end in tempo reale tra Kibana e il servizio LLM dalla CLI, cerca l'ID del connettore generato automaticamente per nome e richiama il relativo 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 .L'output dovrebbe essere simile al seguente, che mostra
"status": "ok"e la risposta del modello:{ "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>" }
Opzione 2: crea il connettore tramite l'interfaccia utente di Kibana
- Accedi a Kibana (
https://${KIBANA_IP}:5601). - Apri il menu di navigazione principale e vai a Gestione > Avvisi e insight > Connettori.
- Fai clic su Crea connettore e seleziona OpenAI.
- Configura i campi del connettore:
- Nome connettore: inserisci il valore di
${LLM_CONNECTOR_NAME}(ad esempioGemma4-31B). - Seleziona un provider OpenAI: seleziona Altro (servizio compatibile con OpenAI).
- URL: inserisci il valore di
${LLM_API_URL}(l'endpoint/v1/chat/completionsdel servizio LLM). - Modello predefinito: inserisci il valore di
${LLM_MODEL_NAME}(ad esempio,gemma4:31b). - Chiave API: inserisci il valore di
${LLM_API_KEY}(qualsiasi stringa non vuota se il server non applica l'autenticazione bearer o la chiave API del server). - Attiva la chiamata di funzione nativa: attiva/disattiva l'opzione.
- Nome connettore: inserisci il valore di
- Fai clic su Salva e testa per salvare il connettore e aprire immediatamente la scheda Test.
- Nella scheda Test, inserisci un prompt di esempio (ad esempio,
Reply with the single word Pong.) e fai clic su Esegui per verificare che Kibana riceva una risposta valida dal servizio LLM.
Testare le integrazioni LLM
Con il set di dati kibana_sample_data_logs installato e il connettore LLM
verificato, puoi testare le funzionalità di AI di Elastic in Kibana.
Elastic AI Agent
Elastic AI Agent
(elastic-ai-agent) è l'agente conversazionale predefinito integrato in Kibana
per esplorare e analizzare i dati in Elasticsearch utilizzando il linguaggio naturale.
Basato sul connettore LLM configurato e sugli strumenti integrati di Agent Builder, l'agente rileva automaticamente gli indici e i mapping di Elasticsearch
(platform.core.list_indices, platform.core.get_index_mapping), genera ed
esegue query ES|QL (platform.core.search, platform.core.execute_esql)
e sintetizza i risultati senza richiedere di conoscere in anticipo lo schema sottostante. Puoi anche creare i tuoi agenti personalizzati con istruzioni e strumenti su misura. Per maggiori dettagli, consulta Agenti personalizzati con Agent Builder.
Per aprire e configurare Elastic AI Agent nell'interfaccia utente di Kibana:
- Apri il menu di navigazione principale in Kibana e seleziona Agente AI (o fai clic sul pulsante
Chiedi all'AI nella barra dell'intestazione in alto o vai direttamente a
https://${KIBANA_IP}:5601/app/chat). - Assicurati che Elastic AI Agent sia selezionato come agente attivo e che il connettore LLM (
${LLM_CONNECTOR_NAME}) sia selezionato come connettore del modello.
Esempio 1: indagine sulle richieste HTTP non riuscite e sui potenziali scanner
Chiedi all'agente AI di Elastic di identificare i principali indirizzi IP client che generano risposte HTTP non 200 in kibana_sample_data_logs, i relativi paesi di origine e se uno di questi IP ha generato anche traffico HTTP 200 riuscito:
Opzione 1: tramite l'interfaccia utente di Kibana:inserisci la seguente domanda nel prompt della chat dell'AI Agent:
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?Opzione 2: tramite l'interfaccia a riga di comando:
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 }'
Cosa succede dietro le quinte:
- L'agente chiama
platform.core.get_index_mapping(indices=["kibana_sample_data_logs"])per ispezionare i campi disponibili (clientip,geo.src,response,bytes,requeste così via). Chiama
platform.core.searchper esaminare i documenti di esempio e verificare che i codici di stato HTTP siano archiviati inresponse, quindi esegue le aggregazioni ES|QL per classificare le richieste non200in base aclientipegeo.srce controllare quali degli IP principali hanno registrato anche trafficoresponse == "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
La risposta dell'agente Elastic AI dovrebbe essere simile alla seguente:
L'indirizzo IP client con il maggior numero di richieste HTTP non riuscite (codici di stato non 200) in
kibana_sample_data_logsè 30.156.16.164, che ha avuto origine negli Stati Uniti e ha registrato 100 richieste non riuscite.Altri indirizzi IP principali con richieste non riuscite, tutti provenienti anche dagli Stati Uniti, includono:
- 213.235.30.99: 6 errori
- 221.241.228.46: 6 errori
- 209.94.234.133: 6 errori
- 7.127.73.138: 5 errori
- 247.240.202.244: 5 tentativi non riusciti
- 130.125.65.24: 5 errori
- 164.85.94.243: 4 errori
- 81.194.200.150: 4 errori
- 30.145.143.210: 4 tentativi non riusciti
Per quanto riguarda le richieste riuscite, tutti gli IP elencati sopra hanno ricevuto almeno alcune risposte HTTP 200 riuscite, ad eccezione di 30.156.16.164, che non ha ricevuto richieste riuscite.
Esempio 2: analisi del volume di traffico e dei byte di payload in base al codice di stato HTTP
Chiedi all'agente AI di Elastic la suddivisione dei codici di stato della risposta HTTP per tutte le richieste in kibana_sample_data_logs e i byte totali trasferiti per ogni codice di stato:
Opzione 1: tramite la UI di Kibana: avvia una nuova conversazione in AI Agent e inserisci la seguente domanda:
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?Opzione 2: tramite la 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 }'
Cosa succede dietro le quinte:
- L'agente ispeziona lo schema
kibana_sample_data_logstramiteplatform.core.get_index_mapping, identificando i campiresponse.keywordebytes. Richiama
platform.core.search, che genera ed esegue la seguente aggregazione ES|QL:FROM kibana_sample_data_logs | STATS request_count = COUNT(*), total_bytes = SUM(bytes) BY response.keyword | LIMIT 100
La risposta di Elastic AI Agent dovrebbe essere simile alla seguente:
La suddivisione dei codici di stato della risposta HTTP e dei dati totali trasferiti in
kibana_sample_data_logsè la seguente:
| Codice di stato | Conteggio delle richieste | Dati totali trasferiti (byte) |
|---|---|---|
| 200 | 12.832 | 75.681.246 |
| 404 | 801 | 4.044.443 |
| 503 | 441 | 0 |
Agenti personalizzati con Agent Builder
Agent Builder
ti consente di creare agenti AI personalizzati su misura per workflow operativi specifici
combinando istruzioni di sistema personalizzate con strumenti integrati (come
platform.core.cases, platform.core.execute_esql e platform.core.search)
e strumenti definiti dall'utente (esql, index_search, workflow e mcp).
L'esempio seguente crea un agente Web Security Incident Responder che opera direttamente sul set di dati kibana_sample_data_logs. In base a un prompt in linguaggio naturale, l'agente:
- Esegue uno strumento ES|QL personalizzato (
investigate_suspicious_web_clients) per aggregare le risposte HTTP non200inkibana_sample_data_logsper IP client. - Distingue le attività di ricognizione e sfruttamento dannose (
30.156.16.164) dagli errori di download innocui. - Richiama uno strumento personalizzato Kibana Workflow (
create_security_case) per aprire automaticamente un Kibana Security Case strutturato con i risultati e i passaggi di correzione.
Crea strumenti personalizzati per ES|QL e la creazione di richieste
Crea uno strumento
esqlpersonalizzato denominatoinvestigate_suspicious_web_clientsche esegue query sukibana_sample_data_logsper trovare gli indirizzi IP client principali che generano codici di stato HTTP non200, insieme ai relativi codici di stato, paese di origine (geo.src), user agent (agent) e URL richiesti (request):Opzione 1: tramite la UI di Kibana:
- In Kibana, apri il menu di navigazione principale e vai a
Elasticsearch > Agenti, quindi fai clic su Gestisci tutti gli strumenti
(o vai direttamente a
https://${KIBANA_IP}:5601/app/agent_builder/tools) e fai clic su Nuovo strumento. Configura i campi dello strumento:
- ID strumento:
investigate_suspicious_web_clients - Type: ES|QL
- Descrizione:
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. - Etichette:
security,logs Query 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 strumento:
Fai clic su Salva.
- In Kibana, apri il menu di navigazione principale e vai a
Elasticsearch > Agenti, quindi fai clic su Gestisci tutti gli strumenti
(o vai direttamente a
Opzione 2: tramite l'interfaccia a riga di comando:
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}'
Poiché lo strumento
platform.core.casesintegrato di Agent Builder è di sola lettura (utilizzato per la ricerca e il recupero di richieste esistenti), crea un flusso di lavoro Kibana (create-security-case) che richiama il passaggiocases.createCasenativo di Kibana e registralo come strumentoworkflowdenominatocreate_security_case:Opzione 1: tramite la UI di Kibana:
- Apri il menu di navigazione principale e vai a Gestione > Workflow (o vai direttamente a
https://${KIBANA_IP}:5601/app/workflows), quindi fai clic su Crea un nuovo workflow. Nell'editor YAML, incolla la seguente definizione di workflow e fai clic su Salva:
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-logsTorna alla pagina Strumenti di Agent Builder (
https://${KIBANA_IP}:5601/app/agent_builder/toolso vai a Elasticsearch > Agent e fai clic su Gestisci tutti gli strumenti) e fai clic su Nuovo strumento.Configura i campi dello strumento del flusso di lavoro e fai clic su Salva:
- ID strumento:
create_security_case - Type: Workflow
- Workflow: seleziona Crea richiesta di assistenza per la sicurezza
(
create-security-case) - Descrizione:
Creates a new Kibana Case in Elastic Security with a title, markdown description of the incident findings, and severity (low, medium, high, critical). - Etichette:
security,cases
- ID strumento:
- Apri il menu di navigazione principale e vai a Gestione > Workflow (o vai direttamente a
Opzione 2: tramite l'interfaccia a riga di comando:
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}'
Crea l'agente Incident Responder personalizzato
Crea l'agente web-security-incident-responder e assegnagli gli strumenti investigate_suspicious_web_clients, create_security_case e platform.core.cases:
Opzione 1: tramite la UI di Kibana
- Apri il menu di navigazione principale e vai a Elasticsearch >
Agenti (o vai direttamente a
https://${KIBANA_IP}:5601/app/agent_builder/agents), quindi fai clic su Nuovo agente. Configura i dettagli dell'agente:
- ID agente:
web-security-incident-responder - Nome visualizzato:
Web Security Incident Responder - Descrizione visualizzata:
Investigates suspicious HTTP error activity in web access logs and opens Kibana Security Cases with the findings. - Etichette:
security,incident-response Istruzioni personalizzate:
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.Strumenti: seleziona
investigate_suspicious_web_clients,create_security_caseeplatform.core.cases.
- ID agente:
Fai clic su Salva.
- Apri il menu di navigazione principale e vai a Elasticsearch >
Agenti (o vai direttamente a
Opzione 2: tramite l'interfaccia a riga di comando:
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}'
Esegui l'agente e verifica la richiesta Kibana
Avvia una conversazione con l'agente Web Security Incident Responder:
Opzione 1: tramite la UI di Kibana:
- Apri il menu di navigazione principale e vai a Elasticsearch > Agenti (o vai direttamente a
https://${KIBANA_IP}:5601/app/agent_builder). - Seleziona Web Security Incident Responder dal selettore degli agenti e assicurati che il connettore LLM (
${LLM_CONNECTOR_NAME}) sia selezionato. Digita il seguente prompt:
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.
- Apri il menu di navigazione principale e vai a Elasticsearch > Agenti (o vai direttamente a
Opzione 2: tramite l'interfaccia a riga di comando:
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 }'L'output dovrebbe essere simile al seguente:
{ "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." }
Verifica la richiesta di sicurezza Kibana appena creata:
Opzione 1: tramite la UI di Kibana:
- Apri il menu di navigazione principale e vai a Sicurezza > Richieste
(o vai direttamente a
https://${KIBANA_IP}:5601/app/security/cases). - Fai clic su Tentativo di ricognizione e sfruttamento dannoso da 30.156.16.164 per visualizzare il riepilogo dell'incidente in formato Markdown, l'analisi MITRE ATT&CK, gli URL dannosi di esempio e la checklist di correzione.
- Apri il menu di navigazione principale e vai a Sicurezza > Richieste
(o vai direttamente a
Opzione 2: tramite l'interfaccia a riga di comando:
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}'L'output dovrebbe essere simile al seguente:
{ "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." }
Attack Discovery
Attack Discovery
è una funzionalità di AI di Elastic Security che analizza gli avvisi di sicurezza aperti, li mappa
al framework MITRE ATT&CK e correla avvisi, indirizzi IP e account utente correlati in catene di attacco visive. Poiché Attack Discovery richiede un modello di grandi dimensioni per sintetizzare in modo affidabile output JSON strutturati con più avvisi, utilizza un modello di classe 30B o superiore (come gemma4:31b).
Genera avvisi di sicurezza dai log di esempio
Per impostazione predefinita, il set di dati kibana_sample_data_logs contiene log di accesso web non elaborati
e non compila l'indice degli avvisi di Elastic Security
(.alerts-security.alerts-default). Inoltre, il generatore di dati di esempio di Kibana
assegna valori @timestamp futuri (fino a 30 giorni prima) alle 100 richieste di scansione e
sfruttamento provenienti da 30.156.16.164, che le colloca al di fuori della
finestra da now-30d a now interrogata dalle regole di rilevamento di Elastic Security.
Per dimostrare il rilevamento degli attacchi con i log di esempio, sposta indietro di 30 giorni i @timestamp record di 30.156.16.164, arricchiscili con i campi Elastic Common Schema (ECS) source.ip e user.name e crea due regole di rilevamento che generano avvisi di sicurezza da questi eventi:
Mappa
clientipasource.ip, impostauser.name(www-data) e sposta@timestampindietro di 30 giorni i record30.156.16.164inkibana_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}'Crea due regole di rilevamento di Elastic Security che abbiano come target
kibana_sample_data_logs*. La prima regola rileva i tentativi di accesso a Nmap e WordPress (Reconnaissance), mentre la seconda rileva i tentativi di attraversamento del percorso/etc/passwde di esecuzione di comandi da 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}'Attendi circa 60 secondi per il completamento dell'esecuzione iniziale della regola, quindi verifica che siano stati indicizzati 4 avvisi in
.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}'L'output dovrebbe essere simile al seguente:
{ "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" }
Esegui Attack Discovery
- In Kibana, apri il menu di navigazione principale e vai a Sicurezza >
Rilevamento attacchi (o vai direttamente a
https://${KIBANA_IP}:5601/app/security/attack_discovery). - Seleziona il connettore LLM (
${LLM_CONNECTOR_NAME}) dal menu dei connettori nella parte superiore della pagina. - Fai clic su Genera per analizzare gli avvisi aperti in
.alerts-security.alerts-default.
Puoi anche attivare e ispezionare il rilevamento degli attacchi dalla 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}"
Al termine della generazione (circa 60-90 secondi), recupera la catena di attacco rilevata:
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]'
Il rilevamento degli attacchi generato correla i quattro avvisi in un unico incidente con IP di origine, account utente e tattiche MITRE ATT&CK collegati:
{
"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"
}