Generative KI-Funktionen von Elastic Stack in GDC mit Air Gap bereitstellen

Übersicht

In diesem Leitfaden wird die End-to-End-Bereitstellung des Elastic Stack (Elasticsearch und Kibana) auf einem freigegebenen Cluster von Google Distributed Cloud (GDC) mit Air Gap beschrieben. Außerdem wird gezeigt, wie Sie ihn in einen LLM-Dienst (Large Language Model) einbinden, um die generativen KI-Funktionen von Elastic zu bewerten.

Bei der Bereitstellung wird der offizielle Elastic Cloud on Kubernetes (ECK)-Operator verwendet, der im GDC mit Air Gap-Marketplace verfügbar ist. Der Operator wird in einem Projekt-Namespace in einem freigegebenen Kubernetes-Cluster installiert und automatisiert die Bereitstellung und das Lebenszyklusmanagement von Elasticsearch- und Kibana-Ressourcen. Nach der Bereitstellung stellt Kibana eine Verbindung zu einem LLM-Inferenzendpunkt her, z. B. zu verwaltetem Gemini auf GDC oder einem selbstverwalteten OpenAI-kompatiblen Dienst wie Ollama oder vLLM. Diese Integration unterstützt die generativen KI-Funktionen von Elastic, einschließlich des Elastic AI-Agents, benutzerdefinierter Agents, die mit Agent Builder erstellt wurden, und Attack Discovery.

Dieser Leitfaden wurde für Elastic v9.4.0, den Elastic Kubernetes Operator v3.4.0 und GDC v1.16.2 geschrieben. Einige Anleitungen können sich leicht unterscheiden, wenn Sie verschiedene Versionen verwenden.

Architektur

Die Architektur besteht aus einer Elastic Stack-Bereitstellung, die vom ECK-Operator in einem Projekt-Namespace in einem freigegebenen GDC-Nutzercluster verwaltet wird. Kibana kommuniziert über das interne GDC-Netzwerk mit einem LLM-Inferenzendpunkt.

Architekturdiagramm des Elastic Stack.

Zu den wichtigsten Komponenten dieser Architektur gehören:

  • GDC-Cluster: Ein gemeinsamer Kubernetes-Cluster mit Organisationsbereich, der an mehrere Projekte angehängt ist und GDC Marketplace-Softwarepakete und Anwendungsarbeitslasten hostet.
  • GDC-Projekt: Der Projekt-Namespace im freigegebenen Cluster, der den ECK-Operator, den Elasticsearch-Cluster, die Kibana-Instanz und die zugehörigen Kubernetes-Secrets und NetworkPolicies isoliert.
  • Elastic Cloud on Kubernetes (ECK)-Operator: Der Operator wird vom GDC Marketplace im Projekt-Namespace installiert und verwaltet die benutzerdefinierten Ressourcen Elasticsearch und Kibana. Außerdem gleicht er die Elastic-Lizenz ab, die für generative KI-Funktionen und externe LLM-Connectors erforderlich ist.
  • Elasticsearch: Speichert und indexiert Betriebs- und Sicherheitsdaten und führt Such- und ES|QL-Abfragen aus, die von den KI-Funktionen und -Agents von Kibana generiert werden.
  • Kibana: Hier werden die generativen KI-Funktionen von Elastic gehostet, darunter der Elastic AI-Agent (für die Untersuchung von Protokollen in natürlicher Sprache und die ES|QL-Generierung), der Agent Builder (zum Erstellen benutzerdefinierter konversationeller Agents und Tools für Elasticsearch-Daten) und Attack Discovery (für die Korrelation von Sicherheitswarnungen und die Analyse von Angriffsketten). Kibana verwaltet auch die LLM-Aktions-Connectors, die den mehrstufigen Toolaufruf zwischen dem Nutzer, Elasticsearch und dem LLM-Dienst orchestrieren.
  • LLM-Dienst: Ein verwalteter LLM-Dienst (z. B. Gemini on GDC) oder ein selbstverwalteter Inferenzserver (z. B. Ollama oder vLLM), der Kibana über das interne GDC-Netzwerk Chatvervollständigungs- und Toolaufruffunktionen bereitstellt.

Hinweis

Prüfen Sie vor der Bereitstellung, ob Ihre GDC-Umgebung die folgenden Voraussetzungen und Annahmen erfüllt:

  • Gemeinsam genutzter Kubernetes-Cluster und GDC-Projekt: Sie haben Zugriff auf einen vorhandenen gemeinsam genutzten GDC-Cluster und ein GDC-Projekt, das mit diesem Cluster verknüpft ist. Das Projekt dient als Namespace-Container für alle Kubernetes-Ressourcen, die in dieser Anleitung bereitgestellt werden. Achten Sie darauf, dass der Cluster über genügend Rechen- und nichtflüchtige Speicherressourcen verfügt, um die ECK-Operator-, Elasticsearch- und Kibana-Arbeitslasten zu hosten.
  • LLM-Dienst: Sie haben Netzwerkzugriff auf einen LLM-Dienst, entweder auf den verwalteten Gemini-Dienst (falls in Ihrer GDC-Umgebung verfügbar) oder auf einen selbstverwalteten OpenAI-kompatiblen LLM-Dienst (z. B. gemma4:31b, der über Ollama oder vLLM in einem GPU-Projekt bereitgestellt wird). Die Bereitstellung der LLM-Infrastruktur selbst wird in diesem Leitfaden nicht behandelt. Die spezifischen Modell- und API-Anforderungen werden jedoch unter Anforderungen für den LLM-Dienst beschrieben.
  • Rufen Sie die Elastic-Version ab, die in Ihrer GDC-Umgebung enthalten ist:

    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')
    
  • Konfigurieren Sie Umgebungsvariablen, auf die in diesem Leitfaden verwiesen wird. Ändern Sie diese Werte nach Bedarf, um sie an Ihre spezifische Umgebung anzupassen:

    # 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"
    
  • Konfigurieren Sie gdcloud:

    gdcloud config set project ${PROJECT_ID}
    gdcloud config set core/zone ${ZONE}
    
  • Fordern Sie die erforderlichen IAM-Rollen auf Projektebene für Ihren Nutzer im Ziel-GDC-Projekt (${PROJECT_ID}) an oder binden Sie sie. Gemäß der GDC Marketplace-Dokumentation zum Lebenszyklus und den GDC-Netzwerkanforderungen sind mindestens die folgenden Projektrollen erforderlich:

    • Namespace-Administrator (namespace-admin): Vollständiger Administratorzugriff zum Erstellen und Verwalten von Kubernetes-Arbeitslasten, Secrets, Diensten und benutzerdefinierten Ressourcen im Projekt-Namespace auf dem freigegebenen Cluster.
    • Projektbetrachter (project-viewer): Lesezugriff zum Aufrufen des vorhandenen Projekts und seiner Ressourcen in der GDC-Konsole und ‑CLI.
    • Marketplace-Bearbeiter (marketplace-editor): Berechtigung zum Installieren, Aktualisieren und Deinstallieren von GDC Marketplace-Softwarepaketen im Projekt.
    • Project NetworkPolicy Admin (project-networkpolicy-admin): Berechtigung zum Konfigurieren globaler ProjectNetworkPolicy-Regeln, die projektübergreifenden und externen Traffic zu den LoadBalancer-Diensten von Kibana und Elasticsearch zulassen.

    Bitten Sie Ihren IAM-Administrator der Organisation oder Ihren IAM-Administrator des Projekts, Ihnen diese Rollen zuzuweisen. Wenn Sie IAM-Administratorberechtigungen haben, können Sie die folgenden Befehle ausführen:

    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
    
  • Rufen Sie die kubeconfig-Datei für den freigegebenen Cluster ab und konfigurieren Sie einen ku-Shell-Alias für kubectl:

    KUBECONFIG=kubeconfig-${CLUSTER_NAME}.yaml gdcloud clusters \
      get-credentials ${CLUSTER_NAME} \
      --zone ${ZONE}
    
    alias ku="kubectl --kubeconfig=kubeconfig-${CLUSTER_NAME}.yaml"
    

Elastic Marketplace-Paket installieren

In diesem Abschnitt installieren Sie den ECK-Operator (Elastic Cloud on Kubernetes) aus dem GDC mit Air Gap-Marketplace in Ihrem Projekt-Namespace. Folgen Sie dazu der Anleitung zum GDC-Marketplace-Lebenszyklus.

Über die gdcloud-Befehlszeile (oder die GDC-Konsole) installieren

  1. Listen Sie die verfügbaren Marketplace-Softwarepakete auf, um den Dienstnamen und die verfügbare Version zu bestätigen:

    gdcloud marketplace services list
    

    Die Ausgabe sollte in etwa so aussehen:

    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>
    
  2. Installieren Sie das Elastic Marketplace-Paket auf dem freigegebenen Zielcluster:

    gdcloud marketplace services install ${MARKETPLACE_SERVICE_NAME} \
      --cluster=${CLUSTER_NAME} \
      --zone=${ZONE} \
      --project ${PROJECT_ID}
    

    *(Alternativ können Sie in der GDC-Konsole Ihr Projekt auswählen ${PROJECT_ID}, zu Marketplace navigieren, den Eintrag Elastic Cloud on Kubernetes auswählen, auf Installieren klicken, den Cluster ${CLUSTER_NAME} auswählen und auf Installieren klicken.)*

  3. Prüfen Sie, ob das StatefulSet elastic-operator und der Pod in Ihrem Projekt-Namespace (${PROJECT_ID}) ausgeführt werden:

    ku get statefulset,pods -n ${PROJECT_ID}
    

    Die Ausgabe sollte in etwa so aussehen:

    NAME                                READY   AGE
    statefulset.apps/elastic-operator   1/1     3m
    
    NAME                   READY   STATUS    RESTARTS   AGE
    pod/elastic-operator-0 1/1     Running   0          3m
    

Elastic Stack bereitstellen

Elastic Stack-Manifest erstellen

Das GDC Marketplace-Paket stellt den ECK-Operator bereit, der vorkonfiguriert ist, um Elasticsearch- und Kibana-Container-Images aus der lokalen, Air-Gap-geschützten Harbor-Registry abzurufen.

Erstellen Sie elastic-stack.yaml, um einen Elasticsearch-Cluster mit einem Knoten, eine Kibana-UI-Instanz, LoadBalancer-HTTP-Dienste und eine allow-all NetworkPolicy in Ihrem Projekt-Namespace (${PROJECT_ID}) bereitzustellen. Das Manifest enthält auch eine Lizenz Secret, die vom ECK-Operator abgeglichen wird. Diese ist hier als 30-tägige Enterprise-Testversion (eck-trial-license) konfiguriert, falls Sie noch keine eigene Enterprise-Lizenz haben:

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

Deployment anwenden und prüfen

Wenden Sie das Manifest an:

ku apply -f elastic-stack.yaml

Wenden Sie eine globale ProjectNetworkPolicy an, damit GDC-Firewallregeln eingehenden Traffic zu den LoadBalancer-IP-Adressen sowie projektübergreifenden Traffic (z. B. Kommunikation mit dem LLM-Dienst) zulassen:

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

Überwachen Sie die Pods, Dienste und benutzerdefinierten Ressourcen, bis sowohl Elasticsearch als auch Kibana den Status Running und Ready erreichen und LoadBalancer-IP-Adressen auf ${CLUSTER_NAME} erhalten:

ku get pods,svc,elasticsearch,kibana -n ${PROJECT_ID}

Sobald die Ressourcen bereit sind, sollte die Ausgabe in etwa so aussehen:

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

Exportieren Sie die externen LoadBalancer-IP-Adressen, die Elasticsearch und Kibana zugewiesen sind, zur Verwendung in nachfolgenden Schritten:

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"

Anmeldedaten abrufen und auf die Kibana-Benutzeroberfläche zugreifen

elastic-Superuser-Passwort abrufen

ECK generiert automatisch ein Kubernetes-Secret (elastic-cluster-es-elastic-user), das das Standard-Superuser-Passwort für elastic enthält. Rufen Sie sie ab und decodieren Sie sie mit:

export ELASTIC_PASSWORD=$(ku get secret elastic-cluster-es-elastic-user \
  -n ${PROJECT_ID} \
  -o jsonpath='{.data.elastic}' | base64 -d)
echo "${ELASTIC_PASSWORD}"

Verbindung prüfen und anmelden

  1. Prüfen Sie, ob Elasticsearch von Ihrer Workstation aus über HTTPS erreichbar ist:

    curl -sk -u "elastic:${ELASTIC_PASSWORD}" "https://${ELASTICSEARCH_IP}:9200"
    
  2. Öffnen Sie https://${KIBANA_IP}:5601 in Ihrem Browser, akzeptieren Sie die Warnung zum selbst signierten Zertifikat und melden Sie sich mit folgenden Anmeldedaten an:

    • Nutzername: elastic
    • Passwort: <output from ELASTIC_PASSWORD above>

Elastic-Lizenz prüfen

Für die generativen KI-Funktionen von Elastic (einschließlich Elastic AI Agent, Agent Builder und Attack Discovery) und externe LLM-Connectors ist eine Enterprise-Lizenz (oder eine aktive 30-tägige Enterprise-Testlizenz) erforderlich.

Wenn ein Elasticsearch-Cluster vom ECK-Operator verwaltet wird, gleicht der license-controller von ECK die Clusterlizenz kontinuierlich mit Kubernetes-Secrets im Operator-Namespace (${PROJECT_ID}) ab. Wenn Sie noch keine Enterprise-Produktionslizenz haben, weist das in elastic-stack.yaml (license.k8s.elastic.co/type: enterprise_trial) enthaltene eck-trial-license-Secret ECK an, automatisch eine 30-tägige Enterprise-Testlizenz bereitzustellen und zu verwalten. (Hinweis: Wenn Sie einen Testzeitraum manuell über POST /_license/start_trial ohne ein ECK-Lizenz-Secret starten, wird der Cluster im nächsten Abgleichszyklus von ECK auf basic zurückgesetzt.)

So prüfen Sie die aktive Lizenz über die Befehlszeile:

curl -sk -u "elastic:${ELASTIC_PASSWORD}" \
  "https://${ELASTICSEARCH_IP}:9200/_license" | jq .

*(Alternativ können Sie in Kibana das Navigationsmenü öffnen und zu Management > Dev Tools gehen und dann auf dem Tab „Console“ GET /_license ausführen.)*

Die Ausgabe sollte in etwa so aussehen und "status": "active" und "type": "enterprise" (oder "type": "trial", wenn Sie die Testlizenz verwenden) enthalten:

{
  "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
  }
}

Integriertes Beispiel-Dataset laden

Wenn Sie die KI-Funktionen von Elastic bewerten möchten, ohne benutzerdefinierte Pipelines für die Datenaufnahme zu schreiben oder externen Internetzugriff zu benötigen, können Sie das in Kibana integrierte Beispiel-Webprotokoll-Dataset (kibana_sample_data_logs) laden. Da das Beispiel-Datenarchiv im Kibana-Container-Image enthalten ist, wird es in einer Air-Gap-Umgebung in Sekundenschnelle installiert. Das Dataset enthält 14.074 Webzugriffs-Logdokumente mit Client-IP-Adressen (clientip), HTTP-Statuscodes (response), Quell- und Zielländern (geo.src, geo.dest), übertragenen Byte (bytes), URLs und User-Agent-Strings.

Weitere integrierte Datasets (flights und ecommerce) finden Sie unter Beispieldaten in Kibana hinzufügen und elastic-datasets-builtin.md.

Dataset laden

Führen Sie den folgenden Befehl aus, um kibana_sample_data_logs über den internen Beispieldaten-Endpunkt von Kibana zu laden:

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"

Beispiel-Dataset prüfen

Prüfen Sie, ob das Beispieldataset logs in Kibana als installed gekennzeichnet ist, und ob der zugrunde liegende Datenstream 14,074 Dokumente in Elasticsearch enthält:

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"

Die Ausgabe sollte in etwa so aussehen:

{
  "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

Anforderungen an den LLM-Dienst

In einer GDC-Umgebung mit Air Gap sind in der Cloud gehostete Elastic Managed LLMs und externe öffentliche SaaS-LLM-Anbieter nicht erreichbar. Je nach GDC-Umgebung:

  • Verwaltetes Gemini in GDC: In GDC-Umgebungen, die Gemini als verwalteten LLM-Dienst anbieten, können Sie Kibana direkt mit dem verwalteten Gemini-Dienst verbinden, sofern verfügbar.
  • Selbstverwalteter LLM-Dienst: Alternativ können Sie Elastic mit einem selbstverwalteten LLM-Dienst verbinden, der in Ihrer GDC-Umgebung gehostet wird (z. B. auf GDC-GPU-Knoten).

Die Bereitstellung eines selbstverwalteten LLM-Dienstes wird in diesem Leitfaden nicht behandelt. Achten Sie beim Bereitstellen oder Auswählen eines selbstverwalteten LLM-Dienstes für Elastic darauf, dass er die Anforderungen erfüllt, die sich aus den offiziellen Leitfäden von Elastic ergeben:

Zusammenfassung der technischen Anforderungen

Die folgenden Punkte fassen die wichtigsten Anforderungen an API, Modellfunktionen, Kontextfenster und Laufzeit zusammen, die für einen selbstverwalteten LLM-Dienst erforderlich sind, damit er zuverlässig mit dem KI-Agenten, dem Agent Builder und der Attack Discovery von Elastic funktioniert. Die in den folgenden Abschnitten genannten spezifischen Konfigurationsparameternamen (z. B. num_ctx, num_predict, repeat_penalty, repeat_last_n, OLLAMA_FLASH_ATTENTION und OLLAMA_NUM_PARALLEL) werden am Beispiel von Ollama angegeben. Wenden Sie die entsprechenden Einstellungen an, wenn Sie eine andere Inferenz-Engine wie vLLM verwenden.

  • OpenAI-kompatible Chat-Vervollständigungs-API (/v1/chat/completions) mit nativen Funktionsaufrufen:
    • Jeder selbstverwaltete Inferenzserver (z. B. Ollama oder vLLM), der einen OpenAI-kompatiblen /v1/chat/completions-Endpunkt (einschließlich Server-Sent Events-Streaming über "stream": true) bereitstellt, kann über den OpenAI-Connector von Kibana integriert werden. Der Anbieter muss dabei auf Other (OpenAI Compatible Service) (Andere (OpenAI-kompatibler Dienst)) festgelegt sein.
    • Aktivieren Sie den nativen Funktionsaufruf ("enableNativeFunctionCalling": true) für den Kibana-Connector, wenn Sie Modelle verwenden, die den Aufruf von cloudnativen Tools unterstützen (z. B. gemma4:31b). Standardmäßig ("enableNativeFunctionCalling": false) verwendet Kibana den promptbasierten simulierten Funktionsaufruf, der bei verschachtelten Toolaufrufen weniger zuverlässig ist und mit Parsing-Fehlern (z. B. Error: Missing name for tool use) fehlschlagen kann. Wenn Sie den nativen Funktionsaufruf aktivieren, wird die Formatierung des Toolaufrufs direkt an das OpenAI-tools-Protokoll des Inferenzservers delegiert.
  • Anweisungsoptimiertes Modell mit nativen Tool- und Funktionsaufruffunktionen:
    • Der Elastic KI-Agent (elastic-ai-agent) und benutzerdefinierte Agent Builder-Agenten basieren auf mehrstufigen Toolaufrufen (platform.core.list_indices, platform.core.get_index_mapping, platform.core.search, platform.core.execute_esql und platform.core.cases). Sie verwenden diese Tools, um Indexzuordnungen zu ermitteln, ES|QL-Abfragen für Elasticsearch zu generieren und auszuführen und Ergebnisse zu synthetisieren.
    • Das ausgewählte Modell muss auf Anweisungen abgestimmt sein und in der Lage sein, gültige Aufrufe von cloudnativen Tools, strukturierte JSON-Ausgaben und ES|QL-Abfragen zu generieren. Weitere Informationen zu den Kriterien für die Modellbewertung finden Sie in der LLM-Leistungsmatrix für Observability und der LLM-Leistungsmatrix für Elastic Security von Elastic.
  • Kontextfenster von mindestens 128,000 Tokens (z. B. num_ctx: 131072 in Ollama):
    • In der allgemeinen Dokumentation zu selbstverwalteten benutzerdefinierten LLMs von Elastic wird ein Minimum von 64.000 Tokens angegeben. Für den Elastic KI-Agent (elastic-ai-agent) wird jedoch dringend ein Kontextfenster von 128.000 Tokens (131,072 Tokens) empfohlen.
    • Bei mehrstufigen Prüfungen kombiniert der Agent Systemanweisungen und Tooldefinitionen mit Indexschemas, ES|QL-Dokumentation und Rohsuchergebnissen (z. B. Abrufen von bis zu 100 Beispieldokumenten, um Feldwerte vor der Aggregation zu prüfen). Eine einzelne Untersuchung kann mehr als 70.000 Tokens im Unterhaltungsverlauf umfassen. Bei einem Kontextfenster mit 64.000 Tokens wird der Prompt dann mitten in der Unterhaltung abgeschnitten.
    • Beachten Sie, dass in Ollama eine PARAMETER num_ctx-Anweisung, die in die Modelfile eines Modells eingebettet ist, die Umgebungsvariable OLLAMA_CONTEXT_LENGTH des Containers überschreibt. Achten Sie darauf, dass sowohl OLLAMA_CONTEXT_LENGTH=131072 als auch PARAMETER num_ctx 131072 in der Modelfile des Modells konfiguriert sind. Auf einer NVIDIA A100- oder H100-GPU mit 80 GB passt gemma4:31b bei einem Kontext von 128K problemlos in den VRAM.
  • Tokenausgabelimit und mäßige Wiederholungsstrafe (z. B. num_predict: 8192 und repeat_penalty: 1.05 in Ollama):
    • Der OpenAI-Stream-Adapter von Kibana behandelt "finish_reason": "length" als schwerwiegenden Token limit reached-Fehler und sendet in jeder Anfrage "temperature": 0 (Greedy-Decodierung).
    • Konfigurieren Sie für Modelle mit logischen Fähigkeiten (z. B. gemma4:31b, das interne Denk-Tokens generiert, bevor es Toolaufrufe ausgibt) ein Tokenausgabelimit von mindestens 8,192 Tokens und eine milde Wiederholungsstrafe. Legen Sie in Ollama beispielsweise num_predict: 8192, repeat_penalty: 1.05 und repeat_last_n: 64 fest. Vermeiden Sie zu aggressive Strafen für Wiederholungen (z. B. 1.15+ über große Fenster), da das Modell sonst bestraft wird, wenn es eine ES|QL-Abfrage aus seinem internen Reasoning-Block in das endgültige Toolaufruf-JSON kopiert.
  • GPU-Beschleunigung, Flash Attention und parallele KV-Cache-Slots (< 60 Sekunden pro Runde):
    • Der OpenAI-Connector von Kibana erzwingt ein standardmäßiges HTTP-Zeitlimit von 60 Sekunden pro Streaming-Turn.
    • Da ein einzelner Agenten-Turn verschachtelte LLM-Unteraufrufe auslösen kann (z. B. um Suchanfragen in natürlicher Sprache in ES|QL-Abfragen zu übersetzen, während die Hauptunterhaltung des Agenten aktiv ist), kann das Ausführen eines Inferenzservers mit nur einem KV-Cache-Slot dazu führen, dass Unteraufrufe den im Cache gespeicherten Kontext der Hauptunterhaltung entfernen und nachfolgende Turns verlangsamen.
    • Legen Sie in Ollama OLLAMA_FLASH_ATTENTION=1 und OLLAMA_NUM_PARALLEL=2 so fest, dass Flash Attention die Auswertung von Langkontexten beschleunigt und die Hauptunterhaltung und die verschachtelten Unteraufrufe separate KV-Cache-Slots im GPU-Speicher beibehalten.

LLM-Connector in Kibana konfigurieren

Sobald Ihr OpenAI-kompatibler LLM-Dienst ausgeführt wird und vom Kibana-Pod aus erreichbar ist, konfigurieren Sie einen OpenAI-Connector in Kibana entweder über die Befehlszeile oder die Kibana-Benutzeroberfläche.

Option 1: Connector über die Befehlszeile erstellen

  1. Führen Sie den folgenden POST /api/actions/connector-Befehl aus, um einen OpenAI-Connector (.gen-ai) zu erstellen, der auf Ihren LLM-Dienst (${LLM_API_URL}) verweist, wobei native Funktionsaufrufe aktiviert sind ("enableNativeFunctionCalling": true). Kibana generiert und weist automatisch eine eindeutige Connector-ID zu:

    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 .
    
  2. Prüfen Sie, ob der Connector ${LLM_CONNECTOR_NAME} vorhanden ist und keine Secrets fehlen. Listen Sie dazu die Connectors von Kibana auf und filtern Sie nach Name:

    curl -sk -u "elastic:${ELASTIC_PASSWORD}" \
      "https://${KIBANA_IP}:5601/api/actions/connectors" \
      -H "kbn-xsrf: true" \
      | jq ".[] | select(.name == \"${LLM_CONNECTOR_NAME}\")"
    

    Die Ausgabe sollte in etwa so aussehen:

    {
      "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
    }
    
  3. Um die End-to-End-Konnektivität zwischen Kibana und dem LLM-Dienst über die CLI zu testen, suchen Sie die automatisch generierte Connector-ID anhand des Namens und rufen Sie den _execute-Endpunkt auf:

    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 .
    

    Die Ausgabe sollte in etwa so aussehen und "status": "ok" sowie die Antwort des Modells enthalten:

    {
      "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>"
    }
    

Option 2: Connector über die Kibana-Benutzeroberfläche erstellen

  1. Melden Sie sich in Kibana (https://${KIBANA_IP}:5601) an.
  2. Öffnen Sie das Hauptnavigationsmenü und rufen Sie Verwaltung > Benachrichtigungen und Statistiken > Connectors auf.
  3. Klicken Sie auf Connector erstellen und wählen Sie OpenAI aus.
  4. Konfigurieren Sie die Connector-Felder:
    • Connector-Name: Geben Sie den Wert von ${LLM_CONNECTOR_NAME} ein (z. B. Gemma4-31B).
    • OpenAI-Anbieter auswählen: Wählen Sie Other (OpenAI Compatible Service) (Andere (OpenAI-kompatibler Dienst)) aus.
    • URL: Geben Sie den Wert von ${LLM_API_URL} (den /v1/chat/completions-Endpunkt Ihres LLM-Dienstes) ein.
    • Standardmodell: Geben Sie den Wert von ${LLM_MODEL_NAME} ein (z. B. gemma4:31b).
    • API-Schlüssel: Geben Sie den Wert von ${LLM_API_KEY} ein (einen beliebigen nicht leeren String, wenn auf Ihrem Server keine Bearer-Authentifizierung erzwungen wird, oder den API-Schlüssel Ihres Servers).
    • Native Funktionsaufrufe aktivieren: Aktivieren Sie den Ein/Aus-Button.
  5. Klicken Sie auf Speichern und testen, um den Connector zu speichern und sofort den Tab Test zu öffnen.
  6. Geben Sie auf dem Tab Test einen Beispiel-Prompt ein (z. B. Reply with the single word Pong.) und klicken Sie auf Ausführen, um zu prüfen, ob Kibana eine gültige Antwort vom LLM-Dienst erhält.

LLM-Integrationen testen

Nachdem das Dataset kibana_sample_data_logs installiert und Ihr LLM-Connector verifiziert wurde, können Sie die KI-Funktionen von Elastic in Kibana testen.

Elastic KI-Agent

Der Elastic AI Agent (elastic-ai-agent) ist der standardmäßige Konversations-Agent, der in Kibana integriert ist, um Daten in Elasticsearch mithilfe natürlicher Sprache zu untersuchen und zu analysieren. Der Agent wird von Ihrem konfigurierten LLM-Connector und den integrierten Agent Builder-Tools unterstützt. Er erkennt automatisch Elasticsearch-Indizes und ‑Zuweisungen (platform.core.list_indices, platform.core.get_index_mapping), generiert und führt ES|QL-Abfragen (platform.core.search, platform.core.execute_esql) aus und fasst Ergebnisse zusammen, ohne dass Sie das zugrunde liegende Schema im Voraus kennen müssen. Sie können auch eigene benutzerdefinierte Agenten mit maßgeschneiderten Anweisungen und Tools erstellen. Weitere Informationen finden Sie unter Benutzerdefinierte Agenten mit Agent Builder.

So öffnen und konfigurieren Sie den Elastic KI-Agenten in der Kibana-Benutzeroberfläche:

  1. Öffnen Sie das Hauptnavigationsmenü in Kibana und wählen Sie KI-Agent aus. Alternativ können Sie in der Kopfzeile oben auf die Schaltfläche Ask AI klicken oder direkt zu https://${KIBANA_IP}:5601/app/chat navigieren.
  2. Achten Sie darauf, dass Elastic AI Agent als aktiver Agent und Ihr LLM-Connector (${LLM_CONNECTOR_NAME}) als Modell-Connector ausgewählt ist.

Beispiel 1: Fehlgeschlagene HTTP-Anfragen und potenzielle Scanner untersuchen

Bitten Sie den Elastic KI-Agent, die wichtigsten Client-IP-Adressen zu identifizieren, die in kibana_sample_data_logs HTTP-Antworten generieren, die nicht 200 sind, sowie deren Herkunftsländer und ob für eine dieser IP-Adressen auch erfolgreicher HTTP-200-Traffic verzeichnet wurde:

  • Option 1: Über die Kibana-Benutzeroberfläche: Geben Sie die folgende Frage in den Chat-Prompt des KI-Agenten ein:

    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?

  • Option 2: Über die Befehlszeile:

    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
      }'
    

Was passiert hinter den Kulissen:

  1. Der KI-Agent ruft platform.core.get_index_mapping(indices=["kibana_sample_data_logs"]) auf, um die verfügbaren Felder (clientip, geo.src, response, bytes, request usw.) zu prüfen.
  2. Es ruft platform.core.search auf, um Beispieldokumente zu prüfen und zu bestätigen, dass HTTP-Statuscodes in response gespeichert sind. Anschließend werden ES|QL-Aggregationen ausgeführt, um Nicht-200-Anfragen nach clientip und geo.src zu sortieren und zu prüfen, welche der Top-IPs auch response == "200"-Traffic aufgezeichnet haben:

    FROM kibana_sample_data_logs
    | WHERE response != "200"
    | STATS failed_count = COUNT(*) BY clientip, geo.src
    | SORT failed_count DESC
    | LIMIT 10
    
    FROM 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
    

Die Antwort des Elastic KI-Agenten sollte in etwa so aussehen:

Die Client-IP-Adresse mit den meisten fehlgeschlagenen HTTP-Anfragen (Statuscodes ungleich 200) in kibana_sample_data_logs ist 30.156.16.164. Sie stammt aus den USA und hat 100 fehlgeschlagene Anfragen verzeichnet.

Andere Top-IP-Adressen mit fehlgeschlagenen Anfragen, die ebenfalls aus den USA stammen, sind:

  • 213.235.30.99: 6 Fehler
  • 221.241.228.46: 6 Fehler
  • 209.94.234.133: 6 Fehler
  • 7.127.73.138: 5 Fehler
  • 247.240.202.244: 5 Fehler
  • 130.125.65.24: 5 Fehler
  • 164.85.94.243: 4 Fehler
  • 81.194.200.150: 4 Fehler
  • 30.145.143.210: 4 Fehler

Bei erfolgreichen Anfragen hatten alle oben aufgeführten IP-Adressen mindestens einige erfolgreiche HTTP 200-Antworten, mit Ausnahme von 30.156.16.164, die keine erfolgreichen Anfragen hatte.

Beispiel 2: Trafficvolumen und Nutzlastbyte nach HTTP-Statuscode analysieren

Fragen Sie den Elastic KI-Agent nach der Aufschlüsselung der HTTP-Antwortstatuscodes für alle Anfragen in kibana_sample_data_logs und der Gesamtzahl der übertragenen Byte für jeden Statuscode:

  • Option 1: Über die Kibana-Benutzeroberfläche: Starten Sie eine neue Unterhaltung im KI-Agenten und geben Sie die folgende Frage ein:

    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?

  • Option 2: Über die Befehlszeile:

    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
      }'
    

Was passiert hinter den Kulissen:

  1. Der Agent prüft das Schema kibana_sample_data_logs über platform.core.get_index_mapping und identifiziert die Felder response.keyword und bytes.
  2. Es ruft platform.core.search auf, wodurch die folgende ES|QL-Aggregation generiert und ausgeführt wird:

    FROM kibana_sample_data_logs
    | STATS request_count = COUNT(*), total_bytes = SUM(bytes) BY response.keyword
    | LIMIT 100
    

Die Antwort des Elastic KI-Agenten sollte in etwa so aussehen:

Die Aufschlüsselung der HTTP-Antwortstatuscodes und der insgesamt in kibana_sample_data_logs übertragenen Daten sieht so aus:

Statuscode Anzahl der Anfragen Übertragene Daten insgesamt (Byte)
200 12.832 75.681.246
404 801 4.044.443
503 441 0

Benutzerdefinierte KI-Agenten mit Agent Builder

Mit dem Agent Builder können Sie benutzerdefinierte KI-Agenten erstellen, die auf bestimmte operative Workflows zugeschnitten sind. Dazu kombinieren Sie benutzerdefinierte Systemanweisungen mit integrierten Tools (wie platform.core.cases, platform.core.execute_esql und platform.core.search) und benutzerdefinierten Tools (esql, index_search, workflow und mcp).

Im folgenden Beispiel wird ein Web Security Incident Responder-Agent erstellt, der direkt mit dem Dataset kibana_sample_data_logs arbeitet. Bei einem Prompt in natürlicher Sprache geht der Agent so vor:

  1. Führt ein benutzerdefiniertes ES|QL-Tool (investigate_suspicious_web_clients) aus, um Nicht-200-HTTP-Antworten in kibana_sample_data_logs nach Client-IP zu aggregieren.
  2. Unterscheidet schädliche Aufklärungs- und Exploiting-Aktivitäten (30.156.16.164) von harmlosen Downloadfehlern.
  3. Ruft ein benutzerdefiniertes Kibana-Workflow-Tool (create_security_case) auf, um automatisch einen strukturierten Kibana-Sicherheitsfall mit den Ergebnissen und Abhilfemaßnahmen zu öffnen.

Benutzerdefinierte ES|QL- und Fallerstellungstools erstellen

  1. Erstellen Sie ein benutzerdefiniertes esql-Tool mit dem Namen investigate_suspicious_web_clients, das kibana_sample_data_logs nach den wichtigsten Client-IP-Adressen abfragt, die HTTP-Statuscodes ungleich 200 generieren, zusammen mit ihren Statuscodes, dem Ursprungsland (geo.src), den User Agents (agent) und den angeforderten URLs (request):

    • Option 1: Über die Kibana-Benutzeroberfläche:

      1. Öffnen Sie in Kibana das Hauptnavigationsmenü und rufen Sie Elasticsearch > Agents auf. Klicken Sie dann auf Alle Tools verwalten (oder rufen Sie direkt https://${KIBANA_IP}:5601/app/agent_builder/tools auf) und klicken Sie auf Neues Tool.
      2. Konfigurieren Sie die Tool-Felder:

        • Tool-ID: investigate_suspicious_web_clients
        • Typ: ES|QL
        • 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.
        • Labels: security, logs
        • ES|QL-Abfrage:

          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
          
      3. Klicken Sie auf Speichern.

    • Option 2: Über die Befehlszeile:

      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}'
      
  2. Da das integrierte platform.core.cases-Tool von Agent Builder schreibgeschützt ist (zum Suchen und Abrufen vorhandener Fälle), erstellen Sie einen Kibana-Workflow (create-security-case), der den nativen cases.createCase-Schritt von Kibana aufruft, und registrieren Sie ihn als workflow-Tool mit dem Namen create_security_case:

    • Option 1: Über die Kibana-Benutzeroberfläche:

      1. Öffnen Sie das Hauptnavigationsmenü und rufen Sie Verwaltung > Workflows auf (oder rufen Sie direkt https://${KIBANA_IP}:5601/app/workflows auf) und klicken Sie dann auf Neuen Workflow erstellen.
      2. Fügen Sie im YAML-Editor die folgende Workflowdefinition ein und klicken Sie auf Speichern:

        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-logs
        
      3. Kehren Sie zur Seite Tools im Agent Builder zurück (https://${KIBANA_IP}:5601/app/agent_builder/tools oder rufen Sie Elasticsearch > Agenten auf und klicken Sie auf Alle Tools verwalten) und klicken Sie auf Neues Tool.

      4. Konfigurieren Sie die Felder des Workflow-Tools und klicken Sie auf Speichern:

        • Tool-ID: create_security_case
        • Typ: Workflow
        • Workflow: Wählen Sie Create Security Case (create-security-case) aus.
        • Description: Creates a new Kibana Case in Elastic Security with a title, markdown description of the incident findings, and severity (low, medium, high, critical).
        • Labels: security, cases
    • Option 2: Über die 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}'
      

Benutzerdefinierten Incident Responder-Agenten erstellen

Erstellen Sie den KI-Agenten web-security-incident-responder und weisen Sie ihm die Tools investigate_suspicious_web_clients, create_security_case und platform.core.cases zu:

  • Option 1: Über die Kibana-Benutzeroberfläche:

    1. Öffnen Sie das Hauptnavigationsmenü und rufen Sie Elasticsearch > Agents auf (oder rufen Sie direkt https://${KIBANA_IP}:5601/app/agent_builder/agents auf) und klicken Sie dann auf New agent (Neuer Agent).
    2. Konfigurieren Sie die Agentendetails:

      • Agenten-ID: web-security-incident-responder
      • Display name (Anzeigename): Web Security Incident Responder.
      • Display description (Anzeigename): Investigates suspicious HTTP error activity in web access logs and opens Kibana Security Cases with the findings.
      • Labels: security, incident-response
      • Benutzerdefinierte Anweisungen:

        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.
        
      • Tools: Wählen Sie investigate_suspicious_web_clients, create_security_case und platform.core.cases aus.

    3. Klicken Sie auf Speichern.

  • Option 2: Über die 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}'
    

Agenten ausführen und Kibana-Fall prüfen

  1. So führen Sie eine Unterhaltung mit dem Web Security Incident Responder-Agenten:

    • Option 1: Über die Kibana-Benutzeroberfläche:

      1. Öffnen Sie das Hauptnavigationsmenü und rufen Sie Elasticsearch > Agents auf oder rufen Sie direkt https://${KIBANA_IP}:5601/app/agent_builder auf.
      2. Wählen Sie in der Agentenauswahl Web Security Incident Responder aus und achten Sie darauf, dass Ihr LLM-Connector (${LLM_CONNECTOR_NAME}) ausgewählt ist.
      3. Geben Sie den folgenden Prompt ein:

        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.

    • Option 2: Über die 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
        }'
      

      Die Ausgabe sollte in etwa so aussehen:

      {
        "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."
      }
      
  2. Prüfen Sie den neu erstellten Kibana-Sicherheitsfall:

    • Option 1: Über die Kibana-Benutzeroberfläche:

      1. Öffnen Sie das Hauptnavigationsmenü und rufen Sie Sicherheit > Fälle auf (oder rufen Sie direkt https://${KIBANA_IP}:5601/app/security/cases auf).
      2. Klicken Sie auf Malicious Reconnaissance and Exploitation Attempt from 30.156.16.164, um die formatierte Markdown-Zusammenfassung des Vorfalls, die MITRE ATT&CK-Analyse, Beispiele für schädliche URLs und die Checkliste zur Problembehebung aufzurufen.
    • Option 2: Über die Befehlszeile:

      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}'
      

      Die Ausgabe sollte in etwa so aussehen:

      {
        "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."
      }
      

Angriffserkennung

Attack Discovery ist eine KI-Funktion in Elastic Security, die offene Sicherheitswarnungen analysiert, sie dem MITRE ATT&CK-Framework zuordnet und zugehörige Warnungen, IP-Adressen und Nutzerkonten in visuelle Angriffsketten korreliert. Da für Attack Discovery ein großes Modell erforderlich ist, um strukturierte JSON-Ausgaben mit mehreren Benachrichtigungen zuverlässig zu synthetisieren, sollten Sie ein Modell der 30B-Klasse oder größer (z. B. gemma4:31b) verwenden.

Sicherheitswarnungen aus den Beispielprotokollen generieren

Standardmäßig enthält das Dataset kibana_sample_data_logs Rohprotokolle für den Webzugriff und füllt nicht den Elastic Security-Warnungsindex (.alerts-security.alerts-default) aus. Darüber hinaus weist der Beispieldatengenerator von Kibana den 100 Scan- und Exploitanfragen von 30.156.16.164 zukünftige @timestamp-Werte (bis zu 30 Tage im Voraus) zu, sodass sie außerhalb des Zeitfensters von now-30d bis now liegen, das von den Elastic Security-Erkennungsregeln abgefragt wird.

Wenn Sie die Angriffserkennung mit den Beispielprotokollen demonstrieren möchten, verschieben Sie die @timestamp der Datensätze von 30.156.16.164 um 30 Tage zurück, reichern Sie sie mit den Elastic Common Schema (ECS)-Feldern source.ip und user.name an und erstellen Sie zwei Erkennungsregeln, die Sicherheitswarnungen aus diesen Ereignissen generieren:

  1. Ordnen Sie clientip source.ip zu, legen Sie user.name (www-data) fest und verschieben Sie @timestamp für die 30.156.16.164-Einträge in kibana_sample_data_logs* um 30 Tage zurück:

    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}'
    
  2. Erstellen Sie zwei Elastic Security-Erkennungsregeln für kibana_sample_data_logs*. Die erste Regel erkennt Nmap- und WordPress-Anmeldeversuche (Reconnaissance) und die zweite Regel erkennt /etc/passwd-Pfaddurchlauf- und Remote-Befehlsausführungsversuche (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}'
    
  3. Warten Sie etwa 60 Sekunden, bis die erste Regelausführung abgeschlossen ist, und prüfen Sie dann, ob 4 Benachrichtigungen in .alerts-security.alerts-default indexiert wurden:

    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}'
    

    Die Ausgabe sollte in etwa so aussehen:

    {
      "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"
    }
    

Angriffserkennung ausführen

  1. Öffnen Sie in Kibana das Hauptnavigationsmenü und rufen Sie Sicherheit > Angriffserkennung auf oder rufen Sie direkt https://${KIBANA_IP}:5601/app/security/attack_discovery auf.
  2. Wählen Sie im Connector-Menü oben auf der Seite Ihren LLM-Connector (${LLM_CONNECTOR_NAME}) aus.
  3. Klicken Sie auf Generieren, um die offenen Benachrichtigungen in .alerts-security.alerts-default zu analysieren.

Sie können Attack Discovery auch über die Befehlszeile auslösen und prüfen:

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}"

Sobald die Generierung abgeschlossen ist (ca. 60–90 Sekunden), rufen Sie die erkannte Angriffskette ab:

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]'

Die generierte Angriffserkennung korreliert die vier Warnungen zu einem einzigen Vorfall mit verknüpfter Quell-IP, Nutzerkonto und MITRE ATT&CK-Taktiken:

{
  "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"
}