概览
本指南概述了在 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 推理端点进行通信。

此架构的关键组件包括:
- 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配置kushell 别名: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 控制台)安装
列出可用的 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>将 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},然后点击安装。)*验证
elastic-operatorStatefulSet 和 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}"
验证连接并登录
验证是否可以从工作站通过 HTTPS 访问 Elasticsearch:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" "https://${ELASTICSEARCH_IP}:9200"在浏览器中打开
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)而失败。启用原生函数调用会将工具调用格式设置直接委托给推理服务器的 OpenAItools协议。
- 任何公开与 OpenAI 兼容的
- 具有原生工具和函数调用功能的指令调优模型:
- 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 性能矩阵。
- Elastic AI 智能体 (
- 上下文窗口至少为
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 中)。
- 虽然 Elastic 的一般自行管理的自定义 LLM 文档中提到最低要求为 64,000 个 token,但强烈建议为 Elastic AI Agent (
- 输出 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 时对模型进行惩罚。
- Kibana 的 OpenAI 流适配器会将
- 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 创建连接器
运行以下
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 .通过列出 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 }如需通过 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 界面创建连接器
- 登录 Kibana (
https://${KIBANA_IP}:5601)。 - 打开主导航菜单,然后依次前往管理 > 提醒和数据分析 > 连接器。
- 点击创建连接器,然后选择 OpenAI。
- 配置连接器字段:
- 连接器名称:输入
${LLM_CONNECTOR_NAME}的值(例如Gemma4-31B)。 - 选择 OpenAI 提供方:选择其他(OpenAI 兼容服务)。
- 网址:输入
${LLM_API_URL}(LLM 服务的/v1/chat/completions端点)的值。 - 默认模型:输入
${LLM_MODEL_NAME}的值(例如gemma4:31b)。 - API 密钥:输入
${LLM_API_KEY}的值(如果您的服务器不强制执行不记名身份验证,则输入任何非空字符串,或者输入服务器的 API 密钥)。 - 启用原生函数调用:开启切换开关。
- 连接器名称:输入
- 点击保存并测试以保存连接器,并立即打开测试标签页。
- 在测试标签页中,输入示例提示(例如
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 智能体,请执行以下操作:
- 在 Kibana 中打开主导航菜单,然后选择 AI 智能体(或点击顶部标题栏中的问问 AI 按钮,或直接前往
https://${KIBANA_IP}:5601/app/chat)。 - 确保选择 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 }'
幕后会发生些什么:
- 智能体调用
platform.core.get_index_mapping(indices=["kibana_sample_data_logs"])来检查可用字段(clientip、geo.src、response、bytes、request等)。 它会调用
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 10FROM kibana_sample_data_logs | WHERE clientip IN ("30.156.16.164", "213.235.30.99", "221.241.228.46", "209.94.234.133", "7.127.73.138", "247.240.202.244", "130.125.65.24", "164.85.94.243", "81.194.200.150", "30.145.143.210") AND response == "200" | STATS success_count = COUNT(*) BY clientip
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 }'
幕后会发生些什么:
- 代理通过
platform.core.get_index_mapping检查kibana_sample_data_logs架构,识别response.keyword和bytes字段。 它会调用
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 数据集进行操作。给定自然语言提示后,智能体会执行以下操作:
- 运行自定义 ES|QL 工具 (
investigate_suspicious_web_clients),以按客户端 IP 汇总kibana_sample_data_logs中的非200HTTP 响应。 - 区分恶意侦察和利用活动 (
30.156.16.164) 与良性下载失败。 - 调用自定义 Kibana 工作流工具 (
create_security_case),自动打开结构化 Kibana 安全案例,其中包含发现结果和修复步骤。
创建自定义 ES|QL 和支持请求创建工具
创建名为
investigate_suspicious_web_clients的自定义esql工具,该工具会查询kibana_sample_data_logs以获取生成非200HTTP 状态代码的热门客户端 IP 地址,以及这些地址的状态代码、来源国家/地区 (geo.src)、用户代理 (agent) 和请求的网址 (request):方法 1:通过 Kibana 界面:
- 在 Kibana 中,打开主导航菜单,然后依次前往 Elasticsearch > Agents,然后点击管理所有工具(或直接前往
https://${KIBANA_IP}:5601/app/agent_builder/tools),然后点击新建工具。 配置工具字段:
- 工具 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
- 工具 ID:
点击保存。
- 在 Kibana 中,打开主导航菜单,然后依次前往 Elasticsearch > Agents,然后点击管理所有工具(或直接前往
选项 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}'
由于 Agent Builder 的内置
platform.core.cases工具是只读的(用于搜索和检索现有案例),因此请创建一个 Kibana 工作流 (create-security-case) 来调用 Kibana 的原生cases.createCase步骤,并将其注册为名为create_security_case的workflow工具:方法 1:通过 Kibana 界面:
- 打开主导航菜单,然后前往管理 > 工作流(或直接前往
https://${KIBANA_IP}:5601/app/workflows),然后点击创建新工作流。 在 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返回 Agent Builder 工具页面(
https://${KIBANA_IP}:5601/app/agent_builder/tools,或前往 Elasticsearch > 代理,然后点击管理所有工具),然后点击新建工具。配置工作流工具字段,然后点击保存:
- 工具 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
- 工具 ID:
- 打开主导航菜单,然后前往管理 > 工作流(或直接前往
选项 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 界面:
- 打开主导航菜单,然后前往 Elasticsearch > 代理(或直接前往
https://${KIBANA_IP}:5601/app/agent_builder/agents),然后点击新建代理。 配置代理详细信息:
- 客服 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。
- 客服 ID:
点击保存。
- 打开主导航菜单,然后前往 Elasticsearch > 代理(或直接前往
选项 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
与 Web Security Incident Responder 智能体对话:
方法 1:通过 Kibana 界面:
- 打开主导航菜单,然后依次前往 Elasticsearch > 代理(或直接前往
https://${KIBANA_IP}:5601/app/agent_builder)。 - 从智能体选择器中选择 Web Security Incident Responder,并确保已选择 LLM 连接器 (
${LLM_CONNECTOR_NAME})。 输入以下提示:
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.
- 打开主导航菜单,然后依次前往 Elasticsearch > 代理(或直接前往
方法 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." }
验证新创建的 Kibana 安全事件:
方法 1:通过 Kibana 界面:
- 打开主导航菜单,然后依次前往安全 > 案例(或直接前往
https://${KIBANA_IP}:5601/app/security/cases)。 - 点击 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 字段丰富这些记录,并创建两条检测规则,以便根据这些事件生成安全提醒:
将
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}'创建两条针对
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}'等待大约 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" }
运行攻击发现
- 在 Kibana 中,打开主导航菜单,然后依次前往安全 > 攻击发现(或直接前往
https://${KIBANA_IP}:5601/app/security/attack_discovery)。 - 从页面顶部的连接器菜单中选择您的 LLM 连接器 (
${LLM_CONNECTOR_NAME})。 - 点击生成,分析
.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"
}