在 GDC 网闸隔离配置上部署 Elastic Stack 的生成式 AI 功能

概览

本指南概述了在 Google Distributed Cloud (GDC) 网闸隔离共享集群上端到端部署 Elastic Stack(Elasticsearch 和 Kibana)的过程,并演示了如何将其与大语言模型 (LLM) 服务集成,以评估 Elastic 的生成式 AI 功能。

该部署使用 GDC 网闸隔离配置 Marketplace 中提供的官方 Elastic Cloud on Kubernetes (ECK) Operator。该 Operator 会安装到共享 Kubernetes 集群上的项目命名空间中,并自动执行 Elasticsearch 和 Kibana 资源的预配和生命周期管理。部署后,Kibana 会连接到 LLM 推理端点(例如 GDC 上的代管式 Gemini 或 Ollama 或 vLLM 等自行管理的 OpenAI 兼容服务)。这种集成可为 Elastic 的生成式 AI 功能提供支持,包括 Elastic AI 智能体、使用 Agent Builder 构建的自定义智能体以及 Attack Discovery。

本指南是针对 Elastic v9.4.0、Elastic Kubernetes Operator v3.4.0 和 GDC v1.16.2 编写的。如果您使用的是其他版本,某些说明可能会略有不同。

架构

该架构包含一个由 ECK 操作员在 GDC 共享用户集群上的项目命名空间内管理的 Elastic Stack 部署。Kibana 通过内部 GDC 网络与 LLM 推理端点进行通信。

Elastic Stack 架构图。

此架构的关键组件包括:

  • GDC 共享集群:一个组织范围的共享 Kubernetes 集群,附加到多个项目,托管 GDC Marketplace 软件包和应用工作负载。
  • GDC 项目:共享集群上的项目命名空间,用于隔离 ECK 运算符、Elasticsearch 集群、Kibana 实例以及关联的 Kubernetes Secret 和 NetworkPolicy。
  • Elastic Cloud on Kubernetes (ECK) Operator:从 GDC Marketplace 安装到项目命名空间中,该 Operator 负责管理 Elasticsearch 和 Kibana 自定义资源,并协调生成式 AI 功能和外部 LLM 连接器所需的 Elastic 许可。
  • Elasticsearch:存储和索引运营和安全数据,并执行由 Kibana 的 AI 功能和代理生成的搜索和 ES|QL 查询。
  • Kibana:托管 Elastic 的生成式 AI 功能,包括 Elastic AI 智能体(用于自然语言日志调查和 ES|QL 生成)、智能体构建器(用于基于 Elasticsearch 数据构建自定义对话智能体和工具)和攻击发现(用于安全提醒关联和攻击链分析)。Kibana 还负责管理 LLM 操作连接器,这些连接器负责协调用户、Elasticsearch 和 LLM 服务之间的多步工具调用。
  • LLM 服务:通过内部 GDC 网络向 Kibana 提供聊天补全和工具调用功能的受管 LLM 服务(例如 GDC 上的 Gemini)或自管推理服务器(例如 Ollama 或 vLLM)。

准备工作

在开始部署之前,请确保您的 GDC 环境满足以下前提条件和假设:

  • 共享 Kubernetes 集群和 GDC 项目:您有权访问现有 GDC 共享集群和附加到该集群的 GDC 项目。该项目充当本指南中部署的所有 Kubernetes 资源的命名空间容器。确保集群有足够的计算和永久性存储资源来托管 ECK Operator、Elasticsearch 和 Kibana 工作负载。
  • LLM 服务:您可以通过网络访问 LLM 服务,可以是托管式 Gemini 服务(如果在您的 GDC 环境中可用),也可以是自行管理的 OpenAI 兼容 LLM 服务(例如,在 GPU 项目中通过 Ollama 或 vLLM 提供的 gemma4:31b)。部署 LLM 基础设施本身不在本指南的讨论范围内,但对 LLM 服务的要求中详细介绍了具体的模型和 API 要求。
  • 检索 GDC 环境随附的 Elastic 版本:

    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')
    
  • 配置将在本指南中引用的环境变量。根据需要修改这些值,以匹配您的特定环境:

    # 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"
    
  • 配置 gdcloud:

    gdcloud config set project ${PROJECT_ID}
    gdcloud config set core/zone ${ZONE}
    
  • 在目标 GDC 项目 (${PROJECT_ID}) 中为您的用户请求或绑定所需的项目级 IAM 角色。根据 GDC Marketplace 生命周期文档和 GDC 网络要求,至少需要以下项目角色:

    • 命名空间管理员 (namespace-admin):拥有完整管理员权限,可在共享集群上的项目命名空间内创建和管理 Kubernetes 工作负载、Secret、服务和自定义资源。
    • Project Viewer (project-viewer):在 GDC 控制台和 CLI 中查看现有项目及其资源的读取权限。
    • Marketplace Editor (marketplace-editor):有权在项目中安装、升级和卸载 GDC Marketplace 软件包。
    • Project NetworkPolicy Admin (project-networkpolicy-admin):有权配置全局 ProjectNetworkPolicy 规则,以允许跨项目和外部流量到达 Kibana 和 Elasticsearch LoadBalancer 服务。

    请让您的组织 IAM 管理员或项目 IAM 管理员授予这些角色(如果您拥有 IAM 管理员权限,也可以运行以下命令):

    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=namespace-admin
    
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=project-viewer
    
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=marketplace-editor
    
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=project-networkpolicy-admin
    
  • 检索共享集群的 kubeconfig,并为 kubectl 配置 ku shell 别名:

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

安装 Elastic 市场资源包

在本部分中,您将按照 GDC Marketplace 生命周期指南,从 GDC 网闸隔离配置 Marketplace 将 Elastic Cloud on Kubernetes (ECK) Operator 安装到项目命名空间中。

通过 gdcloud CLI(或 GDC 控制台)安装

  1. 列出可用的 Marketplace 软件包,以确认服务名称和可用版本:

    gdcloud marketplace services list
    

    输出应类似如下所示:

    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. 将 Elastic 市场资源包安装到目标共享集群上:

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

    *(或者,在 GDC 控制台中,选择您的项目 ${PROJECT_ID},前往 Marketplace,选择 Elastic Cloud on Kubernetes 列表,点击安装,选择集群 ${CLUSTER_NAME},然后点击安装。)*

  3. 验证 elastic-operator StatefulSet 和 Pod 是否在项目命名空间 (${PROJECT_ID}) 中运行:

    ku get statefulset,pods -n ${PROJECT_ID}
    

    输出应类似如下所示:

    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

创建 Elastic Stack 清单

GDC 市场资源包会部署 ECK Operator,该 Operator 预配置为从本地经过网闸隔离的 Harbor 注册表中拉取 Elasticsearch 和 Kibana 容器映像。

创建 elastic-stack.yaml,以在项目命名空间 (${PROJECT_ID}) 中部署单节点 Elasticsearch 集群、Kibana 界面实例、LoadBalancer HTTP 服务和 allow-all NetworkPolicy。该清单还包含由 ECK 运算符协调的许可 Secret,此处配置为 30 天企业试用版 (eck-trial-license),以防您还没有自己的企业许可:

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

应用并验证部署

应用清单:

ku apply -f elastic-stack.yaml

应用全局 ProjectNetworkPolicy,以便 GDC 防火墙规则允许入站流量到达负载均衡器 IP,并允许跨项目流量(例如与 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

监控 pod、服务和自定义资源,直到 Elasticsearch 和 Kibana 都达到 Running 和 Ready 状态,并在 ${CLUSTER_NAME} 上获取 LoadBalancer IP:

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

资源准备就绪后,输出应类似于以下内容:

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

导出分配给 Elasticsearch 和 Kibana 的外部 LoadBalancer IP,以便在后续步骤中使用:

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"

检索凭据并访问 Kibana 界面

检索 elastic 超级用户密码

ECK 会自动生成一个 Kubernetes Secret (elastic-cluster-es-elastic-user),其中包含默认的 elastic 超级用户密码。使用以下命令检索并解码:

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

验证连接并登录

  1. 验证是否可以从工作站通过 HTTPS 访问 Elasticsearch:

    curl -sk -u "elastic:${ELASTIC_PASSWORD}" "https://${ELASTICSEARCH_IP}:9200"
    
  2. 在浏览器中打开 https://${KIBANA_IP}:5601,接受自签名证书警告,然后使用以下信息登录:

    • 用户名:elastic
    • 密码:<output from ELASTIC_PASSWORD above>

验证 Elastic 许可

Elastic 的生成式 AI 功能(包括 Elastic AI 智能体、Agent Builder 和 Attack Discovery)以及外部大语言模型连接器需要企业版许可(或有效的 30 天企业版试用许可)。

当 Elasticsearch 集群由 ECK Operator 管理时,ECK 的 license-controller 会持续根据 Operator 命名空间 (${PROJECT_ID}) 中的 Kubernetes Secret 协调集群许可。如果您还没有生产企业版许可,elastic-stack.yaml (license.k8s.elastic.co/type: enterprise_trial) 中包含的 eck-trial-license Secret 会指示 ECK 自动预配和维护 30 天的企业版试用许可。(请注意,在没有 ECK 许可 Secret 的情况下,通过 POST /_license/start_trial 手动启动试用会导致 ECK 在下一个协调周期将集群还原回 basic。)

如需通过 CLI 验证有效许可,请执行以下操作:

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

*(或者,在 Kibana 中,打开导航菜单,依次前往管理 > 开发者工具,然后在“控制台”标签页中执行 GET /_license。)*

输出应类似如下所示,其中显示了 "status": "active" 和 "type": "enterprise"(如果使用的是试用许可,则显示 "type": "trial"):

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

加载内置的示例数据集

如需评估 Elastic 的 AI 功能,而无需编写自定义数据注入流水线或需要外部互联网访问权限,您可以加载 Kibana 的内置示例 Web 日志数据集 (kibana_sample_data_logs)。由于示例数据归档捆绑在 Kibana 容器映像中,因此它会在几秒钟内安装在经过网闸隔离的环境中。该数据集填充了 14,074 个 Web 访问日志文档,其中包含客户端 IP 地址 (clientip)、HTTP 状态代码 (response)、来源国家/地区和目标国家/地区(geo.src、geo.dest)、传输的字节数 (bytes)、网址和用户代理字符串。

如需了解其他内置数据集(flights 和 ecommerce),请参阅在 Kibana 中添加示例数据和 elastic-datasets-builtin.md。

加载数据集

运行以下命令,通过 Kibana 的内部示例数据端点加载 kibana_sample_data_logs:

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"

验证示例数据集

验证 logs 示例数据集在 Kibana 中是否标记为 installed,并确认后备数据流在 Elasticsearch 中是否包含 14,074 个文档:

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"

输出应类似如下所示:

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

对 LLM 服务的要求

在 GDC 网闸隔离配置环境中,无法访问云托管的 Elastic 托管式大语言模型和外部公共 SaaS 大语言模型提供方。根据您的 GDC 环境:

  • GDC 上的代管式 Gemini:在提供 Gemini 作为代管式 LLM 服务的 GDC 环境中,您可以将 Kibana 直接连接到代管式 Gemini 服务(如果可用)。
  • 自行管理的 LLM 服务:或者,您可以将 Elastic 连接到 GDC 环境中托管的自行管理的 LLM 服务(例如,在 GDC GPU 节点上)。

部署自行管理的大语言模型服务不在本指南的讨论范围内。在为 Elastic 配置或选择自行管理的 LLM 服务时,请确保该服务满足从 Elastic 官方指南中得出的要求:

技术要求摘要

以下各点总结了自行管理的 LLM 服务需要满足的关键 API、模型功能、上下文窗口和运行时要求,才能可靠地与 Elastic 的 AI 智能体、智能体构建器和攻击发现功能配合使用。请注意,以下部分中引用的特定配置参数名称(例如 num_ctx、num_predict、repeat_penalty、repeat_last_n、OLLAMA_FLASH_ATTENTION 和 OLLAMA_NUM_PARALLEL)是以 Ollama 为例提供的。如果您使用其他推理引擎(例如 vLLM),请应用等效设置。

  • 具有原生函数调用功能的 OpenAI 兼容对话补全 API (/v1/chat/completions):
    • 任何公开与 OpenAI 兼容的 /v1/chat/completions 端点(包括通过 "stream": true 进行的服务器发送事件流式传输)的自管理推理服务器(例如 Ollama 或 vLLM)都可以使用 Kibana 的 OpenAI 连接器进行集成,并将提供商设置为其他(OpenAI 兼容服务)。
    • 使用支持云原生工具调用(例如 gemma4:31b)的模型时,在 Kibana 连接器上启用原生函数调用 ("enableNativeFunctionCalling": true)。默认情况下 ("enableNativeFunctionCalling": false),Kibana 使用基于提示的模拟函数调用,这种调用在嵌套工具调用期间不太可靠,并且可能会因解析错误(例如 Error: Missing name for tool use)而失败。启用原生函数调用会将工具调用格式设置直接委托给推理服务器的 OpenAI tools 协议。
  • 具有原生工具和函数调用功能的指令调优模型:
    • Elastic AI 智能体 (elastic-ai-agent) 和自定义 Agent Builder 智能体依赖于多步工具调用(platform.core.list_indices、platform.core.get_index_mapping、platform.core.search、platform.core.execute_esql 和 platform.core.cases)。它们使用这些工具来发现索引映射、针对 Elasticsearch 生成和执行 ES|QL 查询,以及综合发现结果。
    • 所选模型必须经过指令调优,并且能够生成有效的云原生工具调用、结构化 JSON 输出和 ES|QL 查询。如需了解模型评估标准,请参阅 Elastic 的可观测性 LLM 性能矩阵和 Elastic Security LLM 性能矩阵。
  • 上下文窗口至少为 128,000 个 token(例如 Ollama 中的 num_ctx: 131072):
    • 虽然 Elastic 的一般自行管理的自定义 LLM 文档中提到最低要求为 64,000 个 token,但强烈建议为 Elastic AI Agent (elastic-ai-agent) 使用 128,000 个 token 的上下文窗口(131,072 个 token)。
    • 在多步骤调查期间,智能体会将系统指令和工具定义与索引架构、ES|QL 文档和原始搜索结果(例如,在汇总之前检索最多 100 个示例文档以检查字段值)相结合。单次调查的对话记录可能会超过 70,000 个 token,这会导致 64,000 个 token 的上下文窗口在对话中途截断提示。
    • 请注意,在 Ollama 中,嵌入到模型 Modelfile 中的 PARAMETER num_ctx 指令会替换容器的 OLLAMA_CONTEXT_LENGTH 环境变量。确保在模型的 Modelfile 中配置了 OLLAMA_CONTEXT_LENGTH=131072 和 PARAMETER num_ctx 131072(在 80 GB NVIDIA A100 或 H100 GPU 上,128K 上下文中的 gemma4:31b 可以轻松地放入 VRAM 中)。
  • 输出 token 限制和轻微重复惩罚(例如 Ollama 中的 num_predict: 8192 和 repeat_penalty: 1.05):
    • Kibana 的 OpenAI 流适配器会将 "finish_reason": "length" 视为严重的 Token limit reached 错误,并在每个请求中发送 "temperature": 0(贪婪解码)。
    • 对于具有推理功能的模型(例如 gemma4:31b,它会在发出工具调用之前生成内部思考词元),请将输出词元数上限配置为至少 8,192 个词元,并设置温和的重复惩罚。例如,在 Ollama 中,设置 num_predict: 8192、repeat_penalty: 1.05 和 repeat_last_n: 64。避免设置过于激进的重复惩罚(例如在较大窗口中设置 1.15+),因为这会在模型将 ES|QL 查询从其内部推理块复制到最终工具调用 JSON 时对模型进行惩罚。
  • GPU 加速、Flash Attention 和并行 KV 缓存槽(每轮 <60 秒):
    • Kibana 的 OpenAI 连接器会为每个流式传输轮次强制执行默认的 60 秒 HTTP 超时。
    • 由于单个代理轮次可能会触发嵌套的 LLM 子调用(例如,在主代理对话处于活跃状态时,将自然语言搜索请求转换为 ES|QL 查询),因此运行仅具有单个 KV 缓存槽的推理服务器可能会导致子调用逐出主对话的缓存上下文,并减慢后续轮次的速度。
    • 在 Ollama 中,设置 OLLAMA_FLASH_ATTENTION=1 和 OLLAMA_NUM_PARALLEL=2,以便 Flash Attention 加速长上下文评估,并让主对话和嵌套子调用在 GPU 内存中维护单独的 KV 缓存槽。

在 Kibana 中配置 LLM 连接器

当与 OpenAI 兼容的 LLM 服务运行并可从 Kibana pod 访问后,请使用 CLI 或 Kibana 界面在 Kibana 中配置 OpenAI 连接器。

方法 1:通过 CLI 创建连接器

  1. 运行以下 POST /api/actions/connector 命令,创建一个指向 LLM 服务 (${LLM_API_URL}) 且启用了原生函数调用 ("enableNativeFunctionCalling": true) 的 OpenAI (.gen-ai) 连接器。Kibana 会自动生成并分配一个唯一的连接器 ID:

    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. 通过列出 Kibana 的连接器并按名称过滤,验证 ${LLM_CONNECTOR_NAME} 连接器是否存在且没有缺失的密钥:

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

    输出应类似如下所示:

    {
      "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. 如需通过 CLI 测试 Kibana 与大语言模型 服务之间的实时端到端连接性,请按名称查找自动生成的连接器 ID,并调用其 _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 .
    

    输出应类似如下所示,其中显示了 "status": "ok" 和模型的回答:

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

方法 2:通过 Kibana 界面创建连接器

  1. 登录 Kibana (https://${KIBANA_IP}:5601)。
  2. 打开主导航菜单,然后依次前往管理 > 提醒和数据分析 > 连接器。
  3. 点击创建连接器,然后选择 OpenAI。
  4. 配置连接器字段:
    • 连接器名称:输入 ${LLM_CONNECTOR_NAME} 的值(例如 Gemma4-31B)。
    • 选择 OpenAI 提供方:选择其他(OpenAI 兼容服务)。
    • 网址:输入 ${LLM_API_URL}(LLM 服务的 /v1/chat/completions 端点)的值。
    • 默认模型:输入 ${LLM_MODEL_NAME} 的值(例如 gemma4:31b)。
    • API 密钥:输入 ${LLM_API_KEY} 的值(如果您的服务器不强制执行不记名身份验证,则输入任何非空字符串,或者输入服务器的 API 密钥)。
    • 启用原生函数调用:开启切换开关。
  5. 点击保存并测试以保存连接器,并立即打开测试标签页。
  6. 在测试标签页中,输入示例提示(例如 Reply with the single word Pong.),然后点击运行,以验证 Kibana 是否从 LLM 服务收到有效响应。

测试 LLM 集成

安装 kibana_sample_data_logs 数据集并验证 LLM 连接器后,您可以在 Kibana 中测试 Elastic 的 AI 功能。

Elastic AI 智能体

Elastic AI 智能体 (elastic-ai-agent) 是集成到 Kibana 中的默认对话智能体,用于使用自然语言探索和分析 Elasticsearch 中的数据。该智能体由您配置的 LLM 连接器和内置 Agent Builder 工具提供支持,可自动发现 Elasticsearch 索引和映射(platform.core.list_indices、platform.core.get_index_mapping),生成并执行 ES|QL 查询(platform.core.search、platform.core.execute_esql),并合成发现结果,而无需您提前了解底层架构。您还可以使用量身定制的指令和工具构建自己的自定义代理;如需了解详情,请参阅使用 Agent Builder 构建自定义代理。

如需在 Kibana 界面中打开并配置 Elastic AI 智能体,请执行以下操作:

  1. 在 Kibana 中打开主导航菜单,然后选择 AI 智能体(或点击顶部标题栏中的问问 AI 按钮,或直接前往 https://${KIBANA_IP}:5601/app/chat)。
  2. 确保选择 Elastic AI Agent 作为活跃 AI 智能体,并选择 LLM 连接器 (${LLM_CONNECTOR_NAME}) 作为模型连接器。

示例 1:调查失败的 HTTP 请求和潜在扫描器

让 Elastic AI 智能体识别在 kibana_sample_data_logs 中生成非 200 HTTP 响应的热门客户端 IP 地址、其来源国家/地区,以及这些 IP 中是否有任何 IP 也具有成功的 HTTP 200 流量:

  • 方法 1:通过 Kibana 界面:在 AI 智能体对话提示中输入以下问题:

    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?

  • 选项 2:通过 CLI:

    curl -sk -u "elastic:${ELASTIC_PASSWORD}" \
      -X POST "https://${KIBANA_IP}:5601/api/agent_builder/converse" \
      -H "kbn-xsrf: true" \
      -H "Content-Type: application/json" \
      -d "{
        \"agent_id\": \"elastic-ai-agent\",
        \"connector_id\": \"${CONNECTOR_ID}\",
        \"input\": \"In kibana_sample_data_logs, which client IP addresses had the most failed HTTP requests (non-200 status codes), what countries did they come from, and did any of those IPs also have successful HTTP 200 requests?\"
      }" | jq '{
        status: .status,
        tools_called: [.steps[] | select(.type == "tool_call") | {tool_id: .tool_id, params: .params}],
        response: .response.message
      }'
    

幕后会发生些什么:

  1. 智能体调用 platform.core.get_index_mapping(indices=["kibana_sample_data_logs"]) 来检查可用字段(clientip、geo.src、response、bytes、request 等)。
  2. 它会调用 platform.core.search 来检查示例文档并确认 HTTP 状态代码存储在 response 中,然后执行 ES|QL 聚合,按 clientip 和 geo.src 对非 200 请求进行排名,并检查哪些热门 IP 也记录了 response == "200" 流量:

    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
    

Elastic AI 智能体的回答应如下所示:

kibana_sample_data_logs 中失败的 HTTP 请求(非 200 状态代码)最多的客户端 IP 地址是 30.156.16.164,该地址来自美国,记录了 100 个失败的请求。

其他具有失败请求的热门 IP 地址(也均来自美国)包括:

  • 213.235.30.99:6 次失败
  • 221.241.228.46:6 次失败
  • 209.94.234.133:6 次失败
  • 7.127.73.138:5 次失败
  • 247.240.202.244:5 次失败
  • 130.125.65.24:5 次失败
  • 164.85.94.243:4 次失败
  • 81.194.200.150:4 次失败
  • 30.145.143.210:4 次失败

关于成功的请求,上面列出的所有 IP 至少有一些成功的 HTTP 200 响应,30.156.16.164 除外,该 IP 没有成功的请求。

示例 2:按 HTTP 状态代码分析流量和载荷字节数

让 Elastic AI 智能体提供 kibana_sample_data_logs 中所有请求的 HTTP 响应状态代码明细,以及每个状态代码传输的总字节数:

  • 选项 1:通过 Kibana 界面:在 AI 智能体中开始新对话,然后输入以下问题:

    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?

  • 选项 2:通过 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
      }'
    

幕后会发生些什么:

  1. 代理通过 platform.core.get_index_mapping 检查 kibana_sample_data_logs 架构,识别 response.keyword 和 bytes 字段。
  2. 它会调用 platform.core.search,后者会生成并执行以下 ES|QL 聚合:

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

Elastic AI 智能体的回答应如下所示:

HTTP 响应状态代码的细分以及 kibana_sample_data_logs 中传输的总数据如下:

状态代码 请求数 传输的总数据量(字节)
200 12,832 75,681,246
404 801 4,044,443
503 441 0

使用 Agent Builder 构建自定义智能体

Agent Builder 可让您将自定义系统指令与内置工具(例如 platform.core.cases、platform.core.execute_esql 和 platform.core.search)以及用户定义的工具(esql、index_search、workflow 和 mcp)相结合,创建针对特定运营工作流量身定制的自定义 AI 智能体。

以下示例构建了一个 Web Security Incident Responder 智能体,该智能体直接对 kibana_sample_data_logs 数据集进行操作。给定自然语言提示后,智能体会执行以下操作:

  1. 运行自定义 ES|QL 工具 (investigate_suspicious_web_clients),以按客户端 IP 汇总 kibana_sample_data_logs 中的非 200 HTTP 响应。
  2. 区分恶意侦察和利用活动 (30.156.16.164) 与良性下载失败。
  3. 调用自定义 Kibana 工作流工具 (create_security_case),自动打开结构化 Kibana 安全案例,其中包含发现结果和修复步骤。

创建自定义 ES|QL 和支持请求创建工具

  1. 创建名为 investigate_suspicious_web_clients 的自定义 esql 工具,该工具会查询 kibana_sample_data_logs 以获取生成非 200 HTTP 状态代码的热门客户端 IP 地址,以及这些地址的状态代码、来源国家/地区 (geo.src)、用户代理 (agent) 和请求的网址 (request):

    • 方法 1:通过 Kibana 界面:

      1. 在 Kibana 中,打开主导航菜单,然后依次前往 Elasticsearch > Agents,然后点击管理所有工具(或直接前往 https://${KIBANA_IP}:5601/app/agent_builder/tools),然后点击新建工具。
      2. 配置工具字段:

        • 工具 ID:investigate_suspicious_web_clients
        • 类型:ES|QL
        • 说明: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.
        • 标签:security、logs
        • 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
          
      3. 点击保存。

    • 选项 2:通过 CLI:

      curl -sk -u "elastic:${ELASTIC_PASSWORD}" \
        -X POST "https://${KIBANA_IP}:5601/api/agent_builder/tools" \
        -H "kbn-xsrf: true" \
        -H "Content-Type: application/json" \
        -d '{
          "id": "investigate_suspicious_web_clients",
          "type": "esql",
          "description": "Finds the top client IP addresses generating non-200 HTTP error responses in kibana_sample_data_logs, returning their error count, status codes, source country, user agents, and requested URL paths.",
          "tags": ["security", "logs"],
          "configuration": {
            "query": "FROM kibana_sample_data_logs | WHERE response != \"200\" | STATS error_count = COUNT(*), status_codes = VALUES(response), country = VALUES(geo.src), user_agents = VALUES(agent), sample_requests = VALUES(request) BY clientip | SORT error_count DESC | LIMIT 5",
            "params": {}
          }
        }' | jq '{id: .id, type: .type}'
      
  2. 由于 Agent Builder 的内置 platform.core.cases 工具是只读的(用于搜索和检索现有案例),因此请创建一个 Kibana 工作流 (create-security-case) 来调用 Kibana 的原生 cases.createCase 步骤,并将其注册为名为 create_security_case 的 workflow 工具:

    • 方法 1:通过 Kibana 界面:

      1. 打开主导航菜单,然后前往管理 > 工作流(或直接前往 https://${KIBANA_IP}:5601/app/workflows),然后点击创建新工作流。
      2. 在 YAML 编辑器中,粘贴以下工作流定义,然后点击保存:

        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. 返回 Agent Builder 工具页面(https://${KIBANA_IP}:5601/app/agent_builder/tools,或前往 Elasticsearch > 代理,然后点击管理所有工具),然后点击新建工具。

      4. 配置工作流工具字段,然后点击保存:

        • 工具 ID:create_security_case
        • 类型:工作流
        • 工作流:选择创建安全案例 (create-security-case)
        • 说明:Creates a new Kibana Case in Elastic Security with a title, markdown description of the incident findings, and severity (low, medium, high, critical).
        • 标签:security、cases
    • 选项 2:通过 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}'
      

创建自定义突发事件响应者代理

创建 web-security-incident-responder 智能体,并为其分配 investigate_suspicious_web_clients、create_security_case 和 platform.core.cases 工具:

  • 方法 1:通过 Kibana 界面:

    1. 打开主导航菜单,然后前往 Elasticsearch > 代理(或直接前往 https://${KIBANA_IP}:5601/app/agent_builder/agents),然后点击新建代理。
    2. 配置代理详细信息:

      • 客服 ID:web-security-incident-responder
      • 显示名称:Web Security Incident Responder
      • 显示说明:Investigates suspicious HTTP error activity in web access logs and opens Kibana Security Cases with the findings.
      • 标签:security、incident-response
      • 自定义指令:

        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.
        
      • 工具:选择 investigate_suspicious_web_clients、create_security_case 和 platform.core.cases。

    3. 点击保存。

  • 选项 2:通过 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}'
    

运行代理并验证 Kibana Case

  1. 与 Web Security Incident Responder 智能体对话:

    • 方法 1:通过 Kibana 界面:

      1. 打开主导航菜单,然后依次前往 Elasticsearch > 代理(或直接前往 https://${KIBANA_IP}:5601/app/agent_builder)。
      2. 从智能体选择器中选择 Web Security Incident Responder,并确保已选择 LLM 连接器 (${LLM_CONNECTOR_NAME})。
      3. 输入以下提示:

        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.

    • 方法 2:通过 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
        }'
      

      输出应类似如下所示:

      {
        "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. 验证新创建的 Kibana 安全事件:

    • 方法 1:通过 Kibana 界面:

      1. 打开主导航菜单,然后依次前往安全 > 案例(或直接前往 https://${KIBANA_IP}:5601/app/security/cases)。
      2. 点击 30.156.16.164 发起的恶意侦察和利用尝试,查看格式化的 Markdown 事件摘要、MITRE ATT&CK 分析、恶意网址示例和补救措施清单。
    • 方法 2:通过 CLI:

      curl -sk -u "elastic:${ELASTIC_PASSWORD}" \
        "https://${KIBANA_IP}:5601/api/cases/_find" \
        -H "kbn-xsrf: true" \
        | jq '.cases[0] | {id: .id, title: .title, severity: .severity, status: .status, tags: .tags, description: .description}'
      

      输出应类似如下所示:

      {
        "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 是 Elastic Security 中的一项 AI 功能,可分析开放式安全提醒,将其映射到 MITRE ATT&CK 框架,并将相关提醒、IP 地址和用户账号关联到可视化攻击链中。由于攻击发现功能需要大型模型才能可靠地合成多提醒结构化 JSON 输出,因此请使用 30B 级或更大的模型(例如 gemma4:31b)。

根据示例日志生成安全提醒

默认情况下,kibana_sample_data_logs 数据集包含原始 Web 访问日志,不会填充 Elastic Security 提醒索引 (.alerts-security.alerts-default)。此外,Kibana 的示例数据生成器会将未来的 @timestamp 值(最多提前 30 天)分配给来自 30.156.16.164 的 100 个扫描和利用请求,这会将它们置于 Elastic Security 检测规则查询的 now-30d 到 now 窗口之外。

如需使用示例日志演示攻击发现功能,请将 30.156.16.164 记录的 @timestamp 往回推移 30 天,使用 Elastic Common Schema (ECS) source.ip 和 user.name 字段丰富这些记录,并创建两条检测规则,以便根据这些事件生成安全提醒:

  1. 将 clientip 映射到 source.ip,设置 user.name (www-data),并将 kibana_sample_data_logs* 中 30.156.16.164 记录的 @timestamp 往回移动 30 天:

    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. 创建两条针对 kibana_sample_data_logs* 的 Elastic Security 检测规则。第一条规则检测 Nmap 和 WordPress 登录探测 (Reconnaissance),第二条规则检测 /etc/passwd 路径遍历和远程命令执行尝试 (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. 等待大约 60 秒,让初始规则执行完毕,然后验证 4 个提醒是否已编入 .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}'
    

    输出应类似如下所示:

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

运行攻击发现

  1. 在 Kibana 中,打开主导航菜单,然后依次前往安全 > 攻击发现(或直接前往 https://${KIBANA_IP}:5601/app/security/attack_discovery)。
  2. 从页面顶部的连接器菜单中选择您的 LLM 连接器 (${LLM_CONNECTOR_NAME})。
  3. 点击生成,分析 .alerts-security.alerts-default 中的未解决提醒。

您还可以通过 CLI 触发和检查 Attack Discovery:

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

生成完成后(大约 60-90 秒),检索发现的攻击链:

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

生成的攻击发现结果会将这四个提醒关联到一个突发事件中,并附带关联的来源 IP、用户账号和 MITRE ATT&CK 策略:

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