1. 安全运营里的真实痛点:告警太多、攻击链看不清
安全运营中心(SOC)每天面对的现实是:EDR、防火墙、邮件网关、身份系统各吐各的告警,一个勒索软件事件可能在 6 个索引里留下 40 条独立告警,但值班分析师看到的只是「PowerShell 编码命令执行」「LSASS 访问」「卷影副本删除」三条互不相干的记录。Elastic Security 的 Attack Discovery 功能解决的正是这个问题——它把多条安全告警喂给 LLM,让模型自动识别跨告警的攻击链,输出带时间线和 MITRE 战术映射的叙述性报告。
但这里有个很多人踩过的坑:Attack Discovery 不是装好就自动跑的后台服务。你在 Kibana 里配好了 AI Connector、测试返回 pong、许可证也正常,索引.alerts-security.attack.discovery.alerts-default却始终是 0 条文档。原因很简单——调度(Schedule)是独立对象,不显式创建就永远不会触发 LLM 分析。
这篇内容面向做安全运营、想把攻击链分析流程自动化的工程师。我会用 Claude Code 配合 Elastic AI Agent Builder MCP,把「创建调度 → 立即触发 → 查询攻击链 → 溯源原始告警」整条链路走通,同时给出 TaoToken 统一 Key/API 通道的配置片段,让模型调用走同一个入口,省去在多个 provider 之间来回切换的麻烦。适合谁:手上已有 Elastic 9.x 集群、想让 Claude Code 直接对话式操作 Elastic Security、又不想每次翻 API 文档的人。
整个流程的核心只有三步:创建调度、手动触发、用 ES|QL 查结果并关联原始告警。下面按可跟做的顺序展开。
2. TaoToken 前置准备:统一 Key 与 API 通道
在动手配 MCP 之前,先把模型调用这一层理顺。Claude Code 本身要调模型,Elastic Attack Discovery 背后的 Connector 也要调模型,如果两边各配一套 Key、各记一个 Base URL,排障时很容易搞混是谁的凭证失效了。我的做法是统一走 TaoToken 的 API 通道:一个 Key、一个 Base URL,Claude Code 和 Elastic Connector 都指向它。
官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 端点是 https://taotoken.net/api (这个不加 UTM,直接作为 Base URL 用)。注册后在控制台的 API Keys 页面生成一个 Key,形如sk-开头的一串字符,复制下来先存到环境变量里,别直接写进配置文件提交到仓库。
export TAOTOKEN_API_KEY="sk-你的key" export TAOTOKEN_BASE_URL="https://taotoken.net/api"这里要区分两个概念,很多人第一次配会混。Claude Code 用的 Base URL是给 Anthropic 兼容协议用的,通常要在后面拼上版本路径;Elastic Connector 用的 Base URL是 OpenAI 兼容协议,走/v1这一层。两者指向同一个域名,只是路径不同。你可以在 TaoToken 的模型对话页面先验证 Key 是否可用,再往下配。
关于模型选择:Claude Code 侧我一般用 claude-sonnet 系列做代码和工具调用,Elastic Attack Discovery 侧的 Connector 可以用同一通道下的其他模型(比如 qwen 系列或 gpt 系列),因为 Attack Discovery 对模型的要求是「能读懂多条告警并做归纳」,不一定非要最强的编码模型。统一通道的好处是账单和限流在一个地方看,出问题只查一处。
注意:Key 只存在服务端环境变量或密钥管理里,不要写进 MCP 的 JSON 配置明文后提交 Git。下面示例里我用
${TAOTOKEN_API_KEY}占位,实际运行时由 shell 展开。
配好之后建议先做一次最小连通性测试,确认 Key 和 Base URL 都对,再进入 MCP 配置环节。测试命令用 curl 打一个模型列表或最小对话请求即可,返回 200 且结构正常就说明通道没问题。这一步花两分钟,能省掉后面「到底是 MCP 配错了还是 Key 失效了」的反复排查。
3. 可复制配置:MCP 服务端与 Claude Code 客户端
这一节是全文最需要照着抄的部分。Elastic AI Agent Builder MCP 的本质是把 Elastic 的 API 能力包装成 MCP 工具,让 Claude Code 能通过对话调用。配置分两处:MCP 服务端的启动参数,以及 Claude Code 客户端的settings.json或 MCP 注册配置。
先看 MCP 服务端。Elastic 官方提供了 MCP server 的启动方式,核心是三个环境变量:Elasticsearch 地址、认证信息、以及是否跳过自签名证书校验(本地或测试集群常见)。如果你用的是 9.x 自建集群且证书是自签的,ELASTICSEARCH_INSECURE=true必须开,否则连接会直接失败。
# MCP 服务端启动参数(示例,按你的集群地址替换) export ELASTICSEARCH_URL="https://localhost:9200" export ELASTICSEARCH_USERNAME="elastic" export ELASTICSEARCH_PASSWORD="你的密码" export ELASTICSEARCH_INSECURE="true" # 自签名证书时开启 export KIBANA_URL="http://localhost:5601"然后是 Claude Code 客户端的 MCP 注册配置。Claude Code 读取的配置文件通常在用户目录下的.claude/settings.json或项目级.mcp.json,具体路径以你本地版本为准。下面这段是可直接复制的 JSON 片段,注意command和args要指向你实际安装的 MCP server 入口,env里把上面那些变量带进去,同时把 TaoToken 的 Key 和 Base URL 也注入,保证 MCP 内部若需要调模型时走统一通道。
{ "mcpServers": { "elastic-agent-builder": { "command": "npx", "args": ["-y", "@elastic/mcp-server-elasticsearch"], "env": { "ELASTICSEARCH_URL": "https://localhost:9200", "ELASTICSEARCH_USERNAME": "elastic", "ELASTICSEARCH_PASSWORD": "${ELASTIC_PASSWORD}", "ELASTICSEARCH_INSECURE": "true", "KIBANA_URL": "http://localhost:5601", "OPENAI_BASE_URL": "https://taotoken.net/api/v1", "OPENAI_API_KEY": "${TAOTOKEN_API_KEY}" } } } }如果你用的是 Cline 或 CC Switch 这类客户端来管理 MCP,配置结构大同小异,关键是三件套齐全:Base URL、Key、Model ID。Base URL 填https://taotoken.net/api/v1,Key 填你的sk-凭证,Model ID 填你在 TaoToken 控制台看到的模型标识(比如claude-sonnet-4-6或qwen3.5-plus)。三件套缺一个,调用就会报 401 或 model not found。
Elastic 侧的 AI Connector 也要配一份,因为 Attack Discovery 的调度参数里要引用connectorId。在 Kibana 里创建 Connector 时选 OpenAI 类型,Base URL 同样填 TaoToken 的/v1地址,Key 填同一个,模型填你选定的 Model ID。创建完记下返回的 connector id,后面创建调度要用。
# 验证 Connector 是否可用(把 ID 换成你的) curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "http://localhost:5601/api/actions/connector/${CONNECTOR_ID}/_execute" \ -H "kbn-xsrf: true" -H "Content-Type: application/json" \ -d '{"params":{"subAction":"invokeAI","subActionParams":{"messages":[{"role":"user","content":"ping"}]}}}'返回里带"status":"ok"和模型回复内容,说明 Connector 通了。这一步不通,后面创建调度也是白搭,因为 Attack Discovery 触发时会用这个 Connector 去调模型。
4. 验证请求与成功结果:从创建调度到攻击链查询
配置通了之后,进入真正的攻击链分析流程。我按实际操作顺序走一遍,每步都给命令和预期返回结构。
第一步,确认 Attack Discovery 索引状态。先看索引存不存在、有多少文档。如果 count 为 0,说明从未运行过,这是后面要创建调度的信号。
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ "https://localhost:9200/.alerts-security.attack.discovery.alerts-default/_count" # 预期:{"count":0,"_shards":{...}}第二步,创建 Attack Discovery 调度。这是整个流程的关键,不创建就永远不会有结果。参数里alerts_index_pattern指向原始告警索引,api_config引用上一步的 Connector,size控制喂给 LLM 的告警条数(建议 100~200,太大容易超 Token 限制),start/end是分析时间窗。
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "http://localhost:5601/api/attack_discovery/schedules" \ -H "kbn-xsrf: true" -H "Content-Type: application/json" \ -d '{ "name": "每日安全攻击分析", "enabled": true, "params": { "alerts_index_pattern": ".alerts-security.alerts-default", "api_config": { "actionTypeId": ".gen-ai", "connectorId": "'"${CONNECTOR_ID}"'", "model": "qwen3.5-plus", "provider": "OpenAI" }, "size": 100, "start": "now-30d", "end": "now" }, "schedule": {"interval": "24h"}, "actions": [] }' # 预期返回含 "id":"<schedule-id>",记下来第三步,立即触发一次。不想等 24 小时调度周期,用_run_soon手动跑。注意这个接口需要x-elastic-internal-origin头,否则会被拒。
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "http://localhost:5601/internal/alerting/rule/${SCHEDULE_ID}/_run_soon" \ -H "kbn-xsrf: true" -H "x-elastic-internal-origin: Kibana" # 预期:"Rule is already running" 或触发成功提示第四步,查询攻击链结果。分析完成后,用 ES|QL 查 open 状态的攻击发现。返回结构里重点看title、summary_markdown、details_markdown、mitre_attack_tactics四个字段。
FROM .alerts-security.attack.discovery.alerts-default METADATA _id | WHERE `@timestamp` > NOW() - 24 hours AND `kibana.alert.workflow_status` == "open" | KEEP _id, `kibana.alert.attack_discovery.title`, `kibana.alert.attack_discovery.summary_markdown`, `kibana.alert.attack_discovery.details_markdown`, `kibana.alert.attack_discovery.mitre_attack_tactics` | SORT `@timestamp` DESC | LIMIT 20一次成功的返回会长这样:title是「Ransomware Attack Chain」这类归纳名,details_markdown里是带时间戳的攻击步骤叙述,比如「WINWORD.EXE 派生 powershell.exe 执行编码命令 → 建立 C2 连接 → rundll32 转储 LSASS 凭证 → vssadmin 删除卷影副本 → 批量加密文件 → 投放勒索信」。mitre_attack_tactics会映射到 Execution、Credential Access、Impact 等战术。
第五步,溯源原始告警。拿到攻击发现后,用_id关联回原始告警索引,还原完整时间线。这一步是把 LLM 的归纳落回可验证的原始证据。
FROM .alerts-security.alerts-default METADATA _id | WHERE _id IN ("D11_kJ0Bb_T067uw3Wd2", "Dl1_kJ0Bb_T067uw3Wd2") | KEEP _id, `kibana.alert.original_time`, `kibana.alert.rule.name`, `host.name`, `user.name`, `process.name`, `process.command_line`, `process.parent.name` | SORT `kibana.alert.original_time` ASC预期返回是按时间升序的告警序列,process.parent.name能拼出进程父子树,process.command_line里能看到编码命令和 C2 地址。到这一步,一条从「告警太多」到「攻击链清晰」的闭环就走完了。
5. 常见报错排查:401、local proxy failed、reading choices
实操中卡住的地方基本集中在几个固定报错上,我按出现频率排一下,每个都给定位思路。
401 Unauthorized。两种可能:TaoToken 的 Key 失效或没注入,或者 Elastic 的认证信息错了。先分清是哪一层——如果 Claude Code 对话时直接报 401,查OPENAI_API_KEY环境变量是否被 shell 正确展开(${TAOTOKEN_API_KEY}在 JSON 里不会自动展开,要确认客户端支持变量替换,否则直接写值但别提交)。如果是创建调度时报 401,查elastic用户密码和 Connector 的 Key。用前面那条_execute测试命令单独验证 Connector,能快速定位。
local proxy failed。这个报错通常出现在 MCP 客户端连不上 MCP server 时,本质是本地进程没起来或端口不通。检查command和args指向的包是否已安装(npx -y首次运行会下载,网络慢时会超时),以及ELASTICSEARCH_URL是否可达。自建集群如果开了自签名证书但没设ELASTICSEARCH_INSECURE=true,连接会在 TLS 握手阶段失败,表现也类似代理失败。用curl -sk先手动打一下 ES 的根路径,确认网络层通。
reading choices 相关报错。这类错误一般来自模型返回结构不符合预期,常见于 Base URL 拼错(比如漏了/v1)导致请求打到了非 OpenAI 兼容端点,返回的是 HTML 或错误 JSON,解析choices字段时就崩了。核对 Base URL 是否为https://taotoken.net/api/v1,Model ID 是否在 TaoToken 控制台存在。另外size设太大导致上下文超限时,也可能在解析阶段报错,把size降到 100 再试。
OAuth 相关报错。如果你用的客户端默认走 OAuth 流程而你的通道是 API Key 模式,会提示授权失败。在客户端配置里显式指定 API Key 认证方式,别让它自动探测。CC Switch 这类工具里通常有「认证方式」下拉,选 API Key 并填三件套。
调度创建成功但索引仍为 0。检查调度是否enabled: true,以及_run_soon是否真的触发了。有时返回「Rule is already running」是因为上一次还在跑,等几分钟再查。另外确认alerts_index_pattern指向的索引里确实有数据,空告警喂给 LLM 也不会有输出。
| 报错 | 最可能原因 | 快速验证 |
|---|---|---|
| 401 | Key 未注入/失效 | 单独跑_execute测试 |
| local proxy failed | MCP server 未启动/证书 | curl -sk打 ES 根路径 |
| reading choices | Base URL 缺/v1 | 核对端点与 Model ID |
| OAuth 失败 | 认证方式选错 | 客户端改 API Key 模式 |
6. 把流程固化成日常:调度参数与后续动作
跑通一次之后,真正有价值的是把它变成日常运营的一部分。调度参数里schedule.interval建议按告警量设,告警多的环境用1h或6h,少的用24h足够;size控制在 100~200,既能让 LLM 看到足够上下文,又不至于超 Token。actions字段可以配置分析完成后的通知,比如写入 Elastic Cases 或推送到协作工具,这样分析师不用盯着索引等结果。
我自己的习惯是保留一份调度管理 API 的速查,方便随时查状态、改参数、临时触发。常用的就几个:GET /api/attack_discovery/schedules/_find列所有调度,POST .../_run_soon手动触发,PUT改参数,DELETE清理废弃调度。配合 Claude Code 的对话能力,这些操作可以直接用自然语言描述让模型生成命令,省去翻文档。
需要提醒的是,Attack Discovery 的输出是 LLM 归纳结果,details_markdown里的时间线和进程关系要回到原始告警用 ES|QL 验证,别直接当结论用。把「LLM 归纳 + 原始证据溯源」当成固定动作,分析质量才稳得住。
如果你想把模型调用和 MCP 配置再简化,可以从 API Keys 页面统一管理凭证(https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ),接入细节看文档(https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= )。长期做编码和 Agent 类任务的话,Coding Plan 能把额度集中管理(https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= )。先把调度建起来、触发一次、查到第一条攻击链,剩下的就是调参数和接通知了。