在 GDC 實體隔離方案部署 Elastic Stack 的生成式 AI 功能

總覽

本指南概述如何在 Google Distributed Cloud (GDC) 實體隔離共用叢集上,端對端部署 Elastic Stack (Elasticsearch 和 Kibana),並示範如何與大型語言模型 (LLM) 服務整合,評估 Elastic 的生成式 AI 功能。

部署作業會使用 GDC 實體隔離方案 Marketplace 中提供的官方 Elastic Cloud on Kubernetes (ECK) 運算子。運算子會安裝到共用 Kubernetes 叢集上的專案命名空間,並自動佈建及管理 Elasticsearch 和 Kibana 資源的生命週期。部署完成後,Kibana 會連線至 LLM 推論端點 (例如 GDC 上的代管 Gemini,或與 OpenAI 相容的自行管理服務,例如 Ollama 或 vLLM)。這項整合功能可強化 Elastic 的生成式 AI 功能,包括 Elastic AI 代理、使用 Agent Builder 建構的自訂代理,以及攻擊偵測。

本指南適用於 Elastic 9.4.0 版、Elastic Kubernetes Operator 3.4.0 版和 GDC 1.16.2 版。如果您使用不同版本,部分操作說明可能會略有不同。

架構

這個架構包含 Elastic Stack 部署項目,由 ECK 運算子在 GDC 共用使用者叢集的專案命名空間內管理。Kibana 會透過內部 GDC 網路與 LLM 推論端點通訊。

Elastic Stack 架構圖。

這個架構的主要元件包括:

  • GDC 共用叢集:機構範圍的共用 Kubernetes 叢集,連結至多項專案,託管 GDC Marketplace 軟體套件和應用程式工作負載。
  • GDC 專案:共用叢集上的專案命名空間,用於隔離 ECK 運算子、Elasticsearch 叢集、Kibana 執行個體,以及相關聯的 Kubernetes Secret 和 NetworkPolicy。
  • Elastic Cloud on Kubernetes (ECK) 運算子:從 GDC Marketplace 安裝至專案命名空間,負責管理 Elasticsearch 和 Kibana 自訂資源,並協調生成式 AI 功能和外部 LLM 連接器所需的 Elastic 授權。
  • Elasticsearch:儲存及索引作業和安全資料,並執行 Kibana AI 功能和代理程式產生的搜尋和 ES|QL 查詢。
  • Kibana:託管 Elastic 的生成式 AI 功能,包括 Elastic AI Agent (用於自然語言記錄調查和 ES|QL 生成)、Agent Builder (用於在 Elasticsearch 資料上建構自訂對話式代理和工具),以及 Attack Discovery (用於安全性警示關聯和攻擊鏈分析)。Kibana 也會管理 LLM 動作連接器,在使用者、Elasticsearch 和 LLM 服務之間,自動調度管理多步驟工具呼叫。
  • LLM 服務:代管 LLM 服務 (例如 Gemini on GDC) 或自行管理的推論伺服器 (例如 Ollama 或 vLLM),可透過內部 GDC 網路為 Kibana 提供對話完成和工具呼叫功能。

事前準備

開始部署前,請確認 GDC 環境符合下列必要條件和假設:

  • 共用 Kubernetes 叢集和 GDC 專案:您有權存取現有的 GDC 共用叢集,以及附加至該叢集的 GDC 專案。本指南中部署的所有 Kubernetes 資源,都以專案做為命名空間容器。確認叢集有足夠的運算和永久儲存空間資源,可託管 ECK 運算子、Elasticsearch 和 Kibana 工作負載。
  • LLM 服務:您可透過網路存取 LLM 服務,包括代管 Gemini 服務 (如果 GDC 環境支援),或自行管理的 OpenAI 相容 LLM 服務 (例如透過 GPU 專案中的 Ollama 或 vLLM 提供的 gemma4:31b)。部署 LLM 基礎架構本身不在本指南的討論範圍內,但特定模型和 API 需求會在「LLM 服務需求」中詳細說明。
  • 擷取 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):具備讀取權限,可在 GDC 控制台和 CLI 中查看現有專案及其資源。
    • Marketplace 編輯者 (marketplace-editor):具備在專案中安裝、升級及解除安裝 GDC Marketplace 軟體套件的權限。
    • 專案 NetworkPolicy 管理員 (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 殼層別名:

    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) 運算子安裝到專案命名空間。

透過 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」產品,依序點選「Install」(安裝)、叢集 ${CLUSTER_NAME} 和「Install」(安裝)。)*

  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 運算子,並預先設定為從本機實體隔離 Harbor 登錄檔提取 Elasticsearch 和 Kibana 容器映像檔。

建立 elastic-stack.yaml,在專案命名空間 (${PROJECT_ID}) 中部署 1 節點 Elasticsearch 叢集、Kibana UI 執行個體、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 防火牆規則允許傳入 LoadBalancer 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 UI

擷取 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、Agent Builder 和 Attack Discovery) 和外部 LLM 連接器需要企業授權 (或有效的 30 天企業試用授權)。

當 Elasticsearch 叢集由 ECK 運算子管理時,ECK 的 license-controller 會持續比對叢集授權與運算子命名空間 (${PROJECT_ID}) 中的 Kubernetes Secret。如果您尚未取得正式版 Enterprise 授權,elastic-stack.yaml (license.k8s.elastic.co/type: enterprise_trial) 中包含的 eck-trial-license Secret 會指示 ECK 自動佈建及維護 30 天的 Enterprise 試用授權。(請注意,如果透過 POST /_license/start_trial 手動開始試用,但沒有 ECK 授權 Secret,ECK 會在下一個協調週期將叢集還原為 basic)。

如要透過 CLI 驗證有效授權,請按照下列步驟操作:

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

*(或者,您也可以在 Kibana 中開啟導覽選單,依序前往「Management」(管理) >「Dev Tools」(開發人員工具),然後在「Console」(控制台) 分頁中執行 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 內建的網頁記錄範例資料集 (kibana_sample_data_logs)。由於範例資料封存檔已隨附於 Kibana 容器映像檔中,因此在實體隔離環境中,只需幾秒鐘即可完成安裝。資料集會填入 14,074 份網站存取記錄文件,其中包含用戶端 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 代管 LLM 和外部公開 SaaS LLM 供應商。視 GDC 環境而定:

  • GDC 代管 Gemini:在提供 Gemini 做為代管 LLM 服務的 GDC 環境中,您可以直接將 Kibana 連結至代管 Gemini 服務 (如有)。
  • 自行管理的 LLM 服務:您也可以將 Elastic 連結至託管於 GDC 環境 (例如 GDC GPU 節點) 的自行管理 LLM 服務。

部署自行管理的 LLM 服務不在本指南的討論範圍內。為 Elastic 佈建或選取自行管理的 LLM 服務時,請確保該服務符合 Elastic 官方指南中的要求:

技術相關規定摘要

以下重點摘要說明,自管 LLM 服務必須具備哪些關鍵 API、模型功能、脈絡視窗和執行階段需求,才能與 Elastic 的 AI 代理、Agent Builder 和 Attack Discovery 穩定搭配運作。請注意,下列各節引用的特定設定參數名稱 (例如 num_ctx、num_predict、repeat_penalty、repeat_last_n、OLLAMA_FLASH_ATTENTION 和 OLLAMA_NUM_PARALLEL) 是以 Ollama 為例。如果您使用其他推論引擎 (例如 vLLM),請套用對應的設定。

  • 與 OpenAI 相容的 Chat Completions API (/v1/chat/completions),具備原生函式呼叫功能:
    • 任何自行管理的推論伺服器 (例如 Ollama 或 vLLM),只要公開與 OpenAI 相容的 /v1/chat/completions 端點 (包括透過 "stream": true 串流伺服器傳送事件),就能使用 Kibana 的 OpenAI 連接器整合,並將供應商設為「Other (OpenAI Compatible Service)」(其他 (與 OpenAI 相容的服務))。
    • 使用支援雲端原生工具呼叫的模型 (例如 gemma4:31b) 時,請在 Kibana 連接器上啟用原生函式呼叫 ("enableNativeFunctionCalling": true)。根據預設 ("enableNativeFunctionCalling": false),Kibana 會使用以提示為基礎的模擬函式呼叫,在巢狀工具呼叫期間較不可靠,且可能會因剖析錯誤 (例如 Error: Missing name for tool use) 而失敗。啟用原生函式呼叫後,工具呼叫格式設定會直接委派給推論伺服器的 OpenAI tools 通訊協定。
  • 經過指令微調的模型,具備原生工具和函式呼叫功能:
    • Elastic AI 代理 (elastic-ai-agent) 和自訂建構代理仰賴多步驟工具呼叫 (platform.core.list_indices、platform.core.get_index_mapping、platform.core.search、platform.core.execute_esql 和 platform.core.cases)。他們會使用這些工具探索索引對應、生成並執行 ES|QL 查詢,以及彙整發現。
    • 所選模型必須經過指令微調,且能生成有效的雲端原生工具叫用、結構化 JSON 輸出和 ES|QL 查詢。如要瞭解模型評估條件,請參閱 Elastic 的可觀測性 LLM 效能矩陣和 Elastic Security LLM 效能矩陣。
  • 脈絡窗口至少有 128,000 個詞元 (例如 Ollama 中的 num_ctx: 131072):
    • 雖然 Elastic 的一般自行管理自訂 LLM 說明文件指出最低限制為 64,000 個詞元,但強烈建議 Elastic AI 代理 (elastic-ai-agent) 使用 128,000 個詞元的脈絡窗口 (131,072 個詞元)。
    • 在多步驟調查期間,代理會將系統指令和工具定義與索引結構定義、ES|QL 說明文件和原始搜尋結果結合 (例如,在匯總前擷取最多 100 份範例文件,以檢查欄位值)。單次調查的對話記錄可能會超過 70,000 個詞元,導致 64,000 個詞元的脈絡窗口在對話中途截斷提示。
    • 請注意,在 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)。
  • 輸出詞元限制和輕微重複懲罰 (例如 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 UI 在 Kibana 中設定 OpenAI 連接器。

方法 1:透過 CLI 建立連接器

  1. 執行下列 POST /api/actions/connector 指令,建立指向 LLM 服務 (${LLM_API_URL}) 的 OpenAI (.gen-ai) 連接器,並啟用原生函式呼叫 ("enableNativeFunctionCalling": true)。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 和 LLM 服務之間的即時端對端連線,請依名稱查詢自動生成的連接器 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 UI 建立連接器

  1. 登入 Kibana (https://${KIBANA_IP}:5601)。
  2. 開啟主要導覽選單,依序前往「Management」(管理) >「Alerts and Insights」(快訊和深入分析) >「Connectors」(連接器)。
  3. 按一下「建立連接器」,然後選取「OpenAI」。
  4. 設定連接器欄位:
    • Connector name:輸入 ${LLM_CONNECTOR_NAME} 的值 (例如 Gemma4-31B)。
    • 選取 OpenAI 供應商:選取「Other (OpenAI Compatible Service)」(其他 (OpenAI 相容服務))。
    • 網址:輸入 ${LLM_API_URL} 的值 (LLM 服務的 /v1/chat/completions 端點)。
    • 預設模型:輸入 ${LLM_MODEL_NAME} 的值 (例如 gemma4:31b)。
    • API 金鑰:輸入 ${LLM_API_KEY} 的值 (如果伺服器未強制執行不記名驗證,則輸入任何非空白字串,或伺服器的 API 金鑰)。
    • 啟用原生函式呼叫:開啟切換鈕。
  5. 按一下「Save & test」(儲存並測試),儲存連接器並立即開啟「Test」(測試) 分頁。
  6. 在「Test」(測試) 分頁中輸入範例提示 (例如 Reply with the single word Pong.),然後按一下「Run」(執行),確認 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 UI 中開啟及設定 Elastic AI 代理,請按照下列步驟操作:

  1. 開啟 Kibana 的主要導覽選單,然後選取「AI Agent」(AI 代理) (或按一下頂端標題列中的「Ask AI」(詢問 AI) 按鈕,或直接前往 https://${KIBANA_IP}:5601/app/chat)。
  2. 確認已選取「Elastic AI Agent」做為有效代理,並選取 LLM 連接器 (${LLM_CONNECTOR_NAME}) 做為模型連接器。

範例 1:調查失敗的 HTTP 要求和潛在掃描器

請 Elastic AI 代理找出在 kibana_sample_data_logs 中產生非 200 HTTP 回應的主要用戶端 IP 位址、來源國家/地區,以及這些 IP 是否也有成功的 HTTP 200 流量:

  • 選項 1:透過 Kibana UI:在「AI Agent」對話提示中輸入下列問題:

    In kibana_sample_data_logs, which client IP addresses had the most failed HTTP requests (non-200 status codes), what countries did they come from, and did any of those IPs also have successful HTTP 200 requests?

  • 選項 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 次失敗

在成功要求方面,除了 30.156.16.164 沒有成功要求外,上述所有 IP 至少都有一些成功的 HTTP 200 回應。

範例 2:依 HTTP 狀態碼分析流量和酬載位元組

請 Elastic AI 代理提供 kibana_sample_data_logs 中所有要求的 HTTP 回應狀態碼明細,以及每個狀態碼轉移的總位元組數:

  • 選項 1:透過 Kibana UI:在 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 代理的回覆應類似下方內容:

以下是 kibana_sample_data_logs 中 HTTP 回應狀態碼和資料移轉總量的細目:

狀態碼 要求數量 已移轉的資料總量 (位元組)
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 代理。

以下範例會建構網路安全事件應變人員代理,直接在 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 UI

      1. 在 Kibana 中,開啟主要導覽選單,依序前往「Elasticsearch」>「Agents」,然後點選「Manage all tools」(管理所有工具) (或直接前往 https://${KIBANA_IP}:5601/app/agent_builder/tools),再點選「New tool」(新增工具)。
      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 UI

      1. 開啟主要導覽選單,依序前往「Management」(管理) >「Workflows」(工作流程) (或直接前往 https://${KIBANA_IP}:5601/app/workflows),然後按一下「Create a new workflow」(建立新的工作流程)。
      2. 在 YAML 編輯器中,貼上下列工作流程定義,然後按一下「Save」(儲存):

        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 的「Tools」(工具) 頁面 (https://${KIBANA_IP}:5601/app/agent_builder/tools,或依序前往「Elasticsearch」>「Agents」(代理),然後點選「Manage all tools」(管理所有工具)),接著點選「New tool」(新增工具)。

      4. 設定工作流程工具欄位,然後按一下「儲存」:

        • 工具 ID:create_security_case
        • Type:Workflow
        • Workflow:選取「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 UI

    1. 開啟主要導覽選單,依序前往「Elasticsearch」>「Agents」(代理程式) (或直接前往 https://${KIBANA_IP}:5601/app/agent_builder/agents),然後按一下「New agent」(新增代理程式)。
    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 案件

  1. 與網路安全事件應變人員代理對話:

    • 方法 1:透過 Kibana UI

      1. 開啟主要導覽選單,然後依序前往「Elasticsearch」>「Agents」(代理程式) (或直接前往 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 UI

      1. 開啟主要導覽選單,然後依序前往「Security」(安全性) >「Cases」(案件) (或直接前往 https://${KIBANA_IP}:5601/app/security/cases)。
      2. 按一下「Malicious Reconnaissance and Exploitation Attempt from 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 位址和使用者帳戶關聯至視覺化攻擊鏈。由於 Attack Discovery 需要大型模型,才能可靠地合成多重警示結構化 JSON 輸出內容,因此請使用 30B 級或更大的模型 (例如 gemma4:31b)。

根據範例記錄產生安全性警示

根據預設,kibana_sample_data_logs 資料集包含原始網路存取記錄,且不會填入 Elastic Security 安全性警示索引 (.alerts-security.alerts-default)。此外,Kibana 的樣本資料產生器會將未來的 @timestamp 值 (最多提前 30 天) 指派給來自 30.156.16.164 的 100 個掃描和漏洞攻擊要求,使其超出 Elastic Security 偵測規則查詢的 now-30d 至 now 視窗。

如要使用範例記錄示範 Attack Discovery,請將 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. 建立兩項 Elastic Security 偵測規則,目標為 kibana_sample_data_logs*。第一項規則會偵測 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 中,開啟主要導覽選單,然後依序前往「Security」(安全性) >「Attack Discovery」(攻擊偵測) (或直接前往 https://${KIBANA_IP}:5601/app/security/attack_discovery)。
  2. 從頁面頂端的連接器選單中,選取 LLM 連接器 (${LLM_CONNECTOR_NAME})。
  3. 按一下「Generate」(生成),分析 .alerts-security.alerts-default 中待處理的警告。

您也可以透過 CLI 觸發及檢查攻擊偵測:

export EXECUTION_UUID=$(curl -sk -u "elastic:${ELASTIC_PASSWORD}" \
  -X POST "https://${KIBANA_IP}:5601/api/attack_discovery/_generate" \
  -H "kbn-xsrf: true" \
  -H "elastic-api-version: 2023-10-31" \
  -H "Content-Type: application/json" \
  -d "{
    \"alertsIndexPattern\": \".alerts-security.alerts-default\",
    \"anonymizationFields\": [
      {\"id\": \"_id\", \"field\": \"_id\", \"allowed\": true},
      {\"id\": \"@timestamp\", \"field\": \"@timestamp\", \"allowed\": true},
      {\"id\": \"kibana.alert.original_time\", \"field\": \"kibana.alert.original_time\", \"allowed\": true},
      {\"id\": \"message\", \"field\": \"message\", \"allowed\": true},
      {\"id\": \"kibana.alert.rule.name\", \"field\": \"kibana.alert.rule.name\", \"allowed\": true},
      {\"id\": \"kibana.alert.rule.description\", \"field\": \"kibana.alert.rule.description\", \"allowed\": true},
      {\"id\": \"kibana.alert.severity\", \"field\": \"kibana.alert.severity\", \"allowed\": true},
      {\"id\": \"kibana.alert.risk_score\", \"field\": \"kibana.alert.risk_score\", \"allowed\": true},
      {\"id\": \"kibana.alert.rule.threat.tactic.id\", \"field\": \"kibana.alert.rule.threat.tactic.id\", \"allowed\": true},
      {\"id\": \"kibana.alert.rule.threat.tactic.name\", \"field\": \"kibana.alert.rule.threat.tactic.name\", \"allowed\": true},
      {\"id\": \"kibana.alert.rule.threat.technique.id\", \"field\": \"kibana.alert.rule.threat.technique.id\", \"allowed\": true},
      {\"id\": \"kibana.alert.rule.threat.technique.name\", \"field\": \"kibana.alert.rule.threat.technique.name\", \"allowed\": true},
      {\"id\": \"source.ip\", \"field\": \"source.ip\", \"allowed\": true},
      {\"id\": \"user.name\", \"field\": \"user.name\", \"allowed\": true}
    ],
    \"apiConfig\": {
      \"actionTypeId\": \".gen-ai\",
      \"connectorId\": \"${CONNECTOR_ID}\"
    },
    \"replacements\": {},
    \"size\": 20,
    \"subAction\": \"invokeAI\"
  }" | jq -r '.execution_uuid')

echo "Attack Discovery execution UUID: ${EXECUTION_UUID}"

生成作業完成後 (約 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"
}