Men-deploy fitur AI generatif Elastic Stack di GDC dengan air gap

Ringkasan

Panduan ini menguraikan deployment menyeluruh Elastic Stack (Elasticsearch dan Kibana) di cluster bersama Google Distributed Cloud (GDC) dengan air gap dan menunjukkan cara mengintegrasikannya dengan layanan Model Bahasa Besar (LLM) untuk mengevaluasi fitur AI generatif Elastic.

Deployment ini menggunakan operator Elastic Cloud on Kubernetes (ECK) resmi yang tersedia di Marketplace GDC dengan air gap. Operator diinstal ke dalam namespace project di cluster Kubernetes bersama dan mengotomatiskan penyediaan serta pengelolaan siklus proses resource Elasticsearch dan Kibana. Setelah di-deploy, Kibana akan terhubung ke endpoint inferensi LLM (seperti Gemini terkelola di GDC atau layanan yang kompatibel dengan OpenAI yang dikelola sendiri seperti Ollama atau vLLM). Integrasi ini mendukung kemampuan AI generatif Elastic, termasuk Elastic AI Agent, agen kustom yang dibuat dengan Agent Builder, dan Attack Discovery.

Panduan ini ditulis untuk Elastic v9.4.0, Elastic Kubernetes Operator v3.4.0, dan GDC v1.16.2. Beberapa petunjuk mungkin sedikit berbeda jika Anda menggunakan versi yang berbeda.

Arsitektur

Arsitektur ini terdiri dari deployment Elastic Stack yang dikelola oleh operator ECK di dalam namespace project pada cluster pengguna bersama GDC. Kibana berkomunikasi melalui jaringan GDC internal dengan endpoint inferensi LLM.

Diagram arsitektur Elastic Stack.

Komponen utama arsitektur ini meliputi:

  • Cluster bersama GDC: Cluster Kubernetes bersama dengan cakupan organisasi yang terpasang ke beberapa project, yang menghosting paket software dan workload aplikasi GDC Marketplace.
  • Project GDC: Namespace project di cluster bersama yang mengisolasi operator ECK, cluster Elasticsearch, instance Kibana, serta Secret dan NetworkPolicy Kubernetes terkait.
  • Operator Elastic Cloud on Kubernetes (ECK): Diinstal dari GDC Marketplace ke namespace project, operator mengelola resource kustom Elasticsearch dan Kibana serta merekonsiliasi lisensi Elastic yang diperlukan untuk fitur AI generatif dan konektor LLM eksternal.
  • Elasticsearch: Menyimpan dan mengindeks data operasional dan keamanan, serta mengeksekusi kueri penelusuran dan ES|QL yang dihasilkan oleh fitur AI dan agen Kibana.
  • Kibana: Menghosting fitur AI generatif Elastic, termasuk Elastic AI Agent (untuk investigasi log bahasa alami dan pembuatan ES|QL), Agent Builder (untuk membangun agen percakapan dan alat kustom di atas data Elasticsearch), dan Attack Discovery (untuk korelasi notifikasi keamanan dan analisis rantai serangan). Kibana juga mengelola konektor tindakan LLM yang mengorkestrasi panggilan alat multi-langkah antara pengguna, Elasticsearch, dan layanan LLM.
  • Layanan LLM: Layanan LLM terkelola (seperti Gemini di GDC) atau server inferensi yang dikelola sendiri (seperti Ollama atau vLLM) yang menyediakan kemampuan penyelesaian chat dan pemanggilan alat ke Kibana melalui jaringan GDC internal.

Sebelum memulai

Pastikan lingkungan GDC Anda memenuhi prasyarat dan asumsi berikut sebelum memulai deployment:

  • Cluster Kubernetes bersama dan project GDC: Anda memiliki akses ke cluster bersama GDC yang ada dan project GDC yang terpasang ke cluster tersebut. Project ini berfungsi sebagai container namespace untuk semua resource Kubernetes yang di-deploy dalam panduan ini. Pastikan cluster memiliki resource komputasi dan penyimpanan persisten yang memadai untuk menghosting workload operator ECK, Elasticsearch, dan Kibana.
  • Layanan LLM: Anda memiliki akses jaringan ke layanan LLM, baik layanan Gemini terkelola (jika tersedia di lingkungan GDC Anda) atau layanan LLM yang kompatibel dengan OpenAI yang dikelola sendiri (misalnya, gemma4:31b yang disajikan melalui Ollama atau vLLM dalam project GPU). Men-deploy infrastruktur LLM itu sendiri berada di luar cakupan panduan ini, tetapi persyaratan model dan API tertentu dijelaskan dalam Persyaratan untuk layanan LLM.
  • Ambil versi Elastic yang disertakan dengan lingkungan GDC Anda:

    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')
    
  • Konfigurasi variabel lingkungan yang akan dirujuk di seluruh panduan ini. Ubah nilai ini sesuai kebutuhan agar sesuai dengan lingkungan spesifik Anda:

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

    gdcloud config set project ${PROJECT_ID}
    gdcloud config set core/zone ${ZONE}
    
  • Minta atau ikat peran IAM level project yang diperlukan untuk pengguna Anda di project GDC target (${PROJECT_ID}). Berdasarkan dokumentasi siklus proses GDC Marketplace dan persyaratan jaringan GDC, setidaknya peran project berikut diperlukan:

    • Namespace Admin (namespace-admin): Akses administratif penuh untuk membuat dan mengelola workload, Secret, Service, dan resource kustom Kubernetes dalam namespace project di cluster bersama.
    • Project Viewer (project-viewer): Akses baca untuk melihat project yang ada dan resource-nya di konsol dan CLI GDC.
    • Editor Marketplace (marketplace-editor): Izin untuk menginstal, mengupgrade, dan meng-uninstal paket software GDC Marketplace dalam project.
    • Project NetworkPolicy Admin (project-networkpolicy-admin): Izin untuk mengonfigurasi aturan ProjectNetworkPolicy global yang mengizinkan traffic lintas project dan eksternal untuk menjangkau layanan LoadBalancer Kibana dan Elasticsearch.

    Minta Organization IAM Admin atau Project IAM Admin Anda untuk memberikan peran ini (atau jalankan perintah berikut jika Anda memiliki hak istimewa administrator 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
    
  • Ambil kubeconfig untuk cluster bersama dan konfigurasikan alias shell ku untuk kubectl:

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

Instal paket Elastic Marketplace

Di bagian ini, Anda akan menginstal operator Elastic Cloud on Kubernetes (ECK) ke namespace project dari Marketplace GDC dengan air gap dengan mengikuti panduan siklus proses Marketplace GDC.

Instal melalui gdcloud CLI (atau konsol GDC)

  1. Cantumkan paket software Marketplace yang tersedia untuk mengonfirmasi nama layanan dan versi yang tersedia:

    gdcloud marketplace services list
    

    Output-nya akan terlihat seperti berikut:

    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. Instal paket Elastic Marketplace ke cluster bersama target Anda:

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

    *(Atau, di konsol GDC, pilih project Anda ${PROJECT_ID}, buka Marketplace, pilih listingan Elastic Cloud on Kubernetes, klik Install, pilih cluster ${CLUSTER_NAME}, lalu klik Install.)*

  3. Pastikan StatefulSet dan pod elastic-operator berjalan di namespace project Anda (${PROJECT_ID}):

    ku get statefulset,pods -n ${PROJECT_ID}
    

    Output-nya akan terlihat seperti berikut:

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

Men-deploy Elastic Stack

Buat manifes Elastic Stack

Paket GDC Marketplace men-deploy operator ECK yang telah dikonfigurasi sebelumnya untuk mengambil image container Elasticsearch dan Kibana dari registry Harbor lokal yang di-air-gap.

Buat elastic-stack.yaml untuk men-deploy cluster Elasticsearch 1 node, instance UI Kibana, layanan HTTP LoadBalancer, dan allow-all NetworkPolicy di namespace project Anda (${PROJECT_ID}). Manifes juga mencakup lisensi Secret yang disesuaikan oleh operator ECK, yang dikonfigurasi di sini sebagai uji coba Enterprise 30 hari (eck-trial-license) jika Anda belum memiliki lisensi Enterprise sendiri:

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

Menerapkan dan memverifikasi deployment

Terapkan manifes:

ku apply -f elastic-stack.yaml

Terapkan ProjectNetworkPolicy global agar aturan firewall GDC mengizinkan traffic masuk ke IP LoadBalancer serta traffic lintas project (seperti komunikasi dengan layanan 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

Pantau pod, layanan, dan resource kustom hingga Elasticsearch dan Kibana mencapai status Running dan Ready serta mendapatkan IP LoadBalancer di ${CLUSTER_NAME}:

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

Setelah resource siap, output-nya akan terlihat seperti berikut:

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

Ekspor IP LoadBalancer eksternal yang ditetapkan ke Elasticsearch dan Kibana untuk digunakan pada langkah berikutnya:

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"

Mengambil kredensial dan mengakses UI Kibana

Mengambil sandi superuser elastic

ECK secara otomatis membuat Secret Kubernetes (elastic-cluster-es-elastic-user) yang berisi sandi superuser elastic default. Ambil dan dekode dengan:

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

Memverifikasi konektivitas dan login

  1. Pastikan Elasticsearch dapat dijangkau melalui HTTPS dari workstation Anda:

    curl -sk -u "elastic:${ELASTIC_PASSWORD}" "https://${ELASTICSEARCH_IP}:9200"
    
  2. Buka https://${KIBANA_IP}:5601 di browser Anda, setujui peringatan sertifikat yang ditandatangani sendiri, lalu login dengan:

    • Username: elastic
    • Password: <output from ELASTIC_PASSWORD above>

Verifikasi lisensi Elastic

Fitur AI generatif Elastic (termasuk Elastic AI Agent, Agent Builder, dan Attack Discovery) serta konektor LLM eksternal memerlukan lisensi Enterprise (atau lisensi uji coba Enterprise 30 hari yang aktif).

Saat cluster Elasticsearch dikelola oleh operator ECK, license-controller ECK terus merekonsiliasi lisensi cluster dengan Secret Kubernetes di namespace operator (${PROJECT_ID}). Jika Anda belum memiliki lisensi Enterprise produksi, Secret eck-trial-license yang disertakan dalam elastic-stack.yaml (license.k8s.elastic.co/type: enterprise_trial) akan menginstruksikan ECK untuk menyediakan dan mengelola lisensi uji coba Enterprise 30 hari secara otomatis. (Perhatikan bahwa memulai uji coba secara manual melalui POST /_license/start_trial tanpa Secret lisensi ECK akan menyebabkan ECK mengembalikan cluster ke basic pada siklus rekonsiliasi berikutnya.)

Untuk memverifikasi lisensi aktif melalui CLI:

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

*(Atau, di Kibana, buka menu navigasi, lalu buka Management > Dev Tools, lalu jalankan GET /_license di tab Console.)*

Output-nya akan terlihat seperti berikut, yang menampilkan "status": "active" dan "type": "enterprise" (atau "type": "trial" jika menggunakan lisensi uji coba):

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

Memuat set data sampel bawaan

Untuk mengevaluasi fitur AI Elastic tanpa menulis pipeline penyerapan data kustom atau memerlukan akses internet eksternal, Anda dapat memuat set data log web sampel bawaan Kibana (kibana_sample_data_logs). Karena arsip data sampel disertakan dalam image container Kibana, arsip tersebut diinstal dalam hitungan detik di lingkungan yang terisolasi. Set data ini mengisi 14.074 dokumen log akses web yang berisi alamat IP klien (clientip), kode status HTTP (response), negara sumber dan tujuan (geo.src, geo.dest), byte yang ditransfer (bytes), URL, dan string agen pengguna.

Lihat Menambahkan data sampel di Kibana dan elastic-datasets-builtin.md untuk set data bawaan tambahan (flights dan ecommerce).

Memuat set data

Jalankan perintah berikut untuk memuat kibana_sample_data_logs melalui endpoint data contoh internal Kibana:

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"

Memverifikasi set data sampel

Pastikan set data sampel logs ditandai sebagai installed di Kibana dan pastikan aliran data pendukung berisi dokumen 14,074 di Elasticsearch:

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"

Output-nya akan terlihat seperti berikut:

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

Persyaratan untuk layanan LLM

Dalam lingkungan GDC dengan air gap, LLM Terkelola Elastic yang dihosting di cloud dan penyedia LLM SaaS publik eksternal tidak dapat dijangkau. Bergantung pada lingkungan GDC Anda:

  • Gemini Terkelola di GDC: Di lingkungan GDC yang menawarkan Gemini sebagai layanan LLM terkelola, Anda dapat menghubungkan Kibana langsung ke layanan Gemini terkelola jika tersedia.
  • Layanan LLM yang dikelola sendiri: Atau, Anda dapat menghubungkan Elastic ke layanan LLM yang dikelola sendiri yang dihosting dalam lingkungan GDC Anda (misalnya, di node GPU GDC).

Men-deploy layanan LLM yang dikelola sendiri berada di luar cakupan panduan ini. Saat menyediakan atau memilih layanan LLM yang dikelola sendiri untuk Elastic, pastikan layanan tersebut memenuhi persyaratan yang berasal dari panduan resmi Elastic:

Ringkasan persyaratan teknis

Poin-poin berikut merangkum API utama, kemampuan model, jendela konteks, dan persyaratan runtime yang diperlukan agar layanan LLM yang dikelola sendiri dapat berfungsi dengan andal dengan Agen AI, Pembuat Agen, dan Penemuan Serangan Elastic. Perhatikan bahwa nama parameter konfigurasi tertentu yang dikutip di bagian berikut (seperti num_ctx, num_predict, repeat_penalty, repeat_last_n, OLLAMA_FLASH_ATTENTION, dan OLLAMA_NUM_PARALLEL) diberikan menggunakan Ollama sebagai contoh. Terapkan setelan yang setara jika Anda menggunakan mesin inferensi lain seperti vLLM.

  • API penyelesaian chat yang kompatibel dengan OpenAI (/v1/chat/completions) dengan panggilan fungsi native:
    • Server inferensi yang dikelola sendiri (seperti Ollama atau vLLM) yang mengekspos endpoint /v1/chat/completions yang kompatibel dengan OpenAI (termasuk streaming Peristiwa yang Dikirim Server melalui "stream": true) dapat diintegrasikan menggunakan konektor OpenAI Kibana dengan penyedia yang ditetapkan ke Other (OpenAI Compatible Service).
    • Aktifkan panggilan fungsi native ("enableNativeFunctionCalling": true) di konektor Kibana saat menggunakan model yang mendukung panggilan alat cloud native (seperti gemma4:31b). Secara default ("enableNativeFunctionCalling": false), Kibana menggunakan panggilan fungsi simulasi berbasis perintah, yang kurang andal selama panggilan alat bertingkat dan dapat gagal dengan error parsing (seperti Error: Missing name for tool use). Mengaktifkan panggilan fungsi native mendelegasikan pemformatan panggilan alat langsung ke protokol OpenAI tools server inferensi.
  • Model yang disesuaikan dengan petunjuk dengan kemampuan panggilan fungsi dan alat bawaan:
    • Elastic AI Agent (elastic-ai-agent) dan agen Agent Builder kustom mengandalkan panggilan alat multi-langkah (platform.core.list_indices, platform.core.get_index_mapping, platform.core.search, platform.core.execute_esql, dan platform.core.cases). Mereka menggunakan alat ini untuk menemukan pemetaan indeks, membuat dan menjalankan kueri ES|QL terhadap Elasticsearch, dan menyintesis temuan.
    • Model yang dipilih harus disesuaikan dengan perintah dan mampu menghasilkan pemanggilan alat cloud native yang valid, output JSON terstruktur, dan kueri ES|QL. Lihat matriks performa LLM untuk Observability dan matriks performa LLM untuk Elastic Security dari Elastic untuk mengetahui kriteria evaluasi model.
  • Jendela konteks minimal 128,000 token (misalnya, num_ctx: 131072 di Ollama):
    • Meskipun dokumentasi LLM kustom yang dikelola sendiri Elastic secara umum mengutip minimum 64.000 token, jendela konteks 128.000 token (131,072 token) sangat direkomendasikan untuk Elastic AI Agent (elastic-ai-agent).
    • Selama investigasi multi-langkah, agen menggabungkan petunjuk sistem dan definisi alat dengan skema indeks, dokumentasi ES|QL, dan hasil penelusuran mentah (misalnya, mengambil hingga 100 dokumen sampel untuk memeriksa nilai kolom sebelum menggabungkan). Satu investigasi dapat melebihi 70.000 token dalam histori percakapan, yang menyebabkan jendela konteks 64.000 token memangkas prompt di tengah percakapan.
    • Perhatikan bahwa di Ollama, perintah PARAMETER num_ctx yang dimasukkan ke dalam Modelfile model akan menggantikan variabel lingkungan OLLAMA_CONTEXT_LENGTH container. Pastikan OLLAMA_CONTEXT_LENGTH=131072 dan PARAMETER num_ctx 131072 di Modelfile model dikonfigurasi (pada GPU NVIDIA A100 atau H100 80 GB, gemma4:31b pada konteks 128K cocok dengan nyaman di VRAM).
  • Batas token output dan penalti pengulangan ringan (misalnya, num_predict: 8192 dan repeat_penalty: 1.05 di Ollama):
    • Adaptor streaming OpenAI Kibana memperlakukan "finish_reason": "length" sebagai error Token limit reached fatal dan mengirimkan "temperature": 0 (greedy decoding) dalam setiap permintaan.
    • Untuk model yang memiliki kemampuan penalaran (seperti gemma4:31b, yang menghasilkan token penalaran internal sebelum mengeluarkan panggilan alat), konfigurasikan batas token output minimal 8,192 token dan penalti pengulangan yang ringan. Misalnya, di Ollama, tetapkan num_predict: 8192, repeat_penalty: 1.05, dan repeat_last_n: 64. Hindari penalti pengulangan yang terlalu agresif (seperti 1.15+ pada jendela besar), karena penalti tersebut akan menghukum model saat menyalin kueri ES|QL dari blok penalaran internalnya ke JSON panggilan alat akhir.
  • Akselerasi GPU, Flash Attention, dan slot cache KV paralel (<60 dtk per giliran):
    • Konektor OpenAI Kibana menerapkan waktu tunggu HTTP default 60 detik per giliran streaming.
    • Karena satu giliran agen dapat memicu sub-panggilan LLM bertingkat (misalnya, untuk menerjemahkan permintaan penelusuran bahasa alami menjadi kueri ES|QL saat percakapan agen utama aktif), menjalankan server inferensi dengan hanya satu slot cache KV dapat menyebabkan sub-panggilan mengeluarkan konteks yang di-cache dari percakapan utama dan memperlambat giliran berikutnya.
    • Di Ollama, tetapkan OLLAMA_FLASH_ATTENTION=1 dan OLLAMA_NUM_PARALLEL=2 agar Flash Attention mempercepat evaluasi konteks panjang, serta percakapan utama dan sub-panggilan bertingkat mempertahankan slot cache KV terpisah di memori GPU.

Mengonfigurasi konektor LLM di Kibana

Setelah layanan LLM yang kompatibel dengan OpenAI berjalan dan dapat dijangkau dari pod Kibana, konfigurasi konektor OpenAI di Kibana menggunakan CLI atau UI Kibana.

Opsi 1: Membuat konektor melalui CLI

  1. Jalankan perintah POST /api/actions/connector berikut untuk membuat konektor OpenAI (.gen-ai) yang mengarah ke layanan LLM Anda (${LLM_API_URL}) dengan pemanggilan fungsi bawaan diaktifkan ("enableNativeFunctionCalling": true). Kibana akan otomatis membuat dan menetapkan ID konektor unik:

    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. Pastikan konektor ${LLM_CONNECTOR_NAME} ada dan tidak memiliki secret yang hilang dengan mencantumkan konektor Kibana dan memfilter menurut nama:

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

    Output-nya akan terlihat seperti berikut:

    {
      "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. Untuk menguji konektivitas end-to-end langsung antara Kibana dan layanan LLM dari CLI, cari ID konektor yang dibuat otomatis berdasarkan nama dan panggil endpoint _execute-nya:

    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 .
    

    Output-nya akan terlihat seperti berikut, yang menampilkan "status": "ok" dan respons model:

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

Opsi 2: Membuat konektor melalui UI Kibana

  1. Login ke Kibana (https://${KIBANA_IP}:5601).
  2. Buka menu navigasi utama, lalu buka Management > Alerts and Insights > Connectors.
  3. Klik Create connector dan pilih OpenAI.
  4. Konfigurasikan kolom konektor:
    • Connector name: Masukkan nilai ${LLM_CONNECTOR_NAME} (misalnya, Gemma4-31B).
    • Select an OpenAI provider: Pilih Other (OpenAI Compatible Service).
    • URL: Masukkan nilai ${LLM_API_URL} (endpoint /v1/chat/completions layanan LLM Anda).
    • Model default: Masukkan nilai ${LLM_MODEL_NAME} (misalnya, gemma4:31b).
    • Kunci API: Masukkan nilai ${LLM_API_KEY} (string apa pun yang tidak kosong jika server Anda tidak menerapkan autentikasi pemilik, atau kunci API server Anda).
    • Aktifkan pemanggilan fungsi native: Aktifkan tombolnya.
  5. Klik Save & test untuk menyimpan konektor dan langsung membuka tab Test.
  6. Di tab Test, masukkan contoh prompt (misalnya, Reply with the single word Pong.) dan klik Run untuk memverifikasi bahwa Kibana menerima respons yang valid dari layanan LLM.

Menguji integrasi LLM

Setelah set data kibana_sample_data_logs diinstal dan konektor LLM Anda diverifikasi, Anda dapat menguji kemampuan AI Elastic di Kibana.

Agen AI yang Elastis

Elastic AI Agent (elastic-ai-agent) adalah agen percakapan default yang terintegrasi ke dalam Kibana untuk menjelajahi dan menganalisis data di seluruh Elasticsearch menggunakan bahasa alami. Didukung oleh konektor LLM yang Anda konfigurasi dan alat Agent Builder bawaan, agen akan otomatis menemukan indeks dan pemetaan Elasticsearch (platform.core.list_indices, platform.core.get_index_mapping), membuat dan mengeksekusi kueri ES|QL (platform.core.search, platform.core.execute_esql), dan menyintesis temuan tanpa mengharuskan Anda mengetahui skema yang mendasarinya terlebih dahulu. Anda juga dapat membangun agen kustom sendiri dengan petunjuk dan alat yang disesuaikan; lihat Agen kustom dengan Agent Builder untuk mengetahui detail selengkapnya.

Untuk membuka dan mengonfigurasi Elastic AI Agent di UI Kibana:

  1. Buka menu navigasi utama di Kibana dan pilih agen AI (atau klik tombol Ask AI di panel header atas, atau buka langsung ke https://${KIBANA_IP}:5601/app/chat).
  2. Pastikan Elastic AI Agent dipilih sebagai agen aktif dan konektor LLM Anda (${LLM_CONNECTOR_NAME}) dipilih sebagai konektor model.

Contoh 1: Menyelidiki permintaan HTTP yang gagal dan potensi pemindai

Minta Agen AI Elastic untuk mengidentifikasi alamat IP klien teratas yang menghasilkan respons HTTP non-200 di kibana_sample_data_logs, negara asalnya, dan apakah ada IP tersebut yang juga memiliki traffic HTTP 200 yang berhasil:

  • Opsi 1: Melalui UI Kibana: Masukkan pertanyaan berikut di perintah chat 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?

  • Opsi 2: Melalui 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
      }'
    

Yang terjadi di balik layar:

  1. Agen memanggil platform.core.get_index_mapping(indices=["kibana_sample_data_logs"]) untuk memeriksa kolom yang tersedia (clientip, geo.src, response, bytes, request, dll.).
  2. Kueri ini memanggil platform.core.search untuk memeriksa contoh dokumen dan mengonfirmasi bahwa kode status HTTP disimpan di response, lalu menjalankan agregasi ES|QL untuk memberi peringkat permintaan non-200 berdasarkan clientip dan geo.src serta memeriksa IP teratas mana yang juga mencatat traffic 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
    

Respons Agen AI Elastis akan terlihat seperti berikut:

Alamat IP klien dengan permintaan HTTP gagal terbanyak (kode status non-200) di kibana_sample_data_logs adalah 30.156.16.164, yang berasal dari Amerika Serikat dan mencatat 100 permintaan gagal.

Alamat IP teratas lainnya dengan permintaan yang gagal, yang semuanya juga berasal dari Amerika Serikat, meliputi:

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

Terkait permintaan yang berhasil, semua IP yang tercantum di atas memiliki setidaknya beberapa respons HTTP 200 yang berhasil, kecuali untuk 30.156.16.164, yang tidak memiliki permintaan yang berhasil.

Contoh 2: Menganalisis volume traffic dan byte payload berdasarkan kode status HTTP

Minta Elastic AI Agent untuk memberikan perincian kode status respons HTTP di semua permintaan di kibana_sample_data_logs dan total byte yang ditransfer untuk setiap kode status:

  • Opsi 1: Melalui UI Kibana: Mulai percakapan baru di AI Agent dan masukkan pertanyaan berikut:

    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?

  • Opsi 2: Melalui 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
      }'
    

Yang terjadi di balik layar:

  1. Agen memeriksa skema kibana_sample_data_logs melalui platform.core.get_index_mapping, mengidentifikasi kolom response.keyword dan bytes.
  2. Tindakan ini akan memanggil platform.core.search, yang menghasilkan dan menjalankan agregasi ES|QL berikut:

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

Respons Elastic AI Agent akan terlihat seperti berikut:

Perincian kode status respons HTTP dan total data yang ditransfer di kibana_sample_data_logs adalah sebagai berikut:

Kode Status Jumlah Permintaan Total Data yang Ditransfer (Byte)
200 12.832 75.681.246
404 801 4.044.443
503 441 0

Agen kustom dengan Agent Builder

Agent Builder memungkinkan Anda membuat agen AI kustom yang disesuaikan dengan alur kerja operasional tertentu dengan menggabungkan petunjuk sistem kustom dengan alat bawaan (seperti platform.core.cases, platform.core.execute_esql, dan platform.core.search) dan alat yang ditentukan pengguna (esql, index_search, workflow, dan mcp).

Contoh berikut membangun agen Web Security Incident Responder yang beroperasi langsung pada set data kibana_sample_data_logs. Dengan perintah bahasa alami, agen akan:

  1. Menjalankan alat ES|QL kustom (investigate_suspicious_web_clients) untuk menggabungkan respons HTTP non-200 di kibana_sample_data_logs berdasarkan IP klien.
  2. Membedakan aktivitas pengintaian dan eksploitasi berbahaya (30.156.16.164) dari kegagalan download yang tidak berbahaya.
  3. Memanggil alat Kibana Workflow kustom (create_security_case) untuk otomatis membuka Kasus Keamanan Kibana terstruktur dengan temuan dan langkah-langkah perbaikannya.

Membuat alat kustom untuk ES|QL dan pembuatan kasus

  1. Buat alat esql kustom bernama investigate_suspicious_web_clients yang mengkueri kibana_sample_data_logs untuk alamat IP klien teratas yang menghasilkan kode status HTTP non-200, beserta kode statusnya, negara asal (geo.src), agen pengguna (agent), dan URL yang diminta (request):

    • Opsi 1: Melalui UI Kibana:

      1. Di Kibana, buka menu navigasi utama dan buka Elasticsearch > Agents, lalu klik Manage all tools (atau buka langsung ke https://${KIBANA_IP}:5601/app/agent_builder/tools) dan klik New tool.
      2. Konfigurasi kolom alat:

        • ID Alat: investigate_suspicious_web_clients
        • Type: ES|QL
        • Deskripsi: 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.
        • Label: security, logs
        • Kueri 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. Klik Simpan.

    • Opsi 2: Melalui 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. Karena alat platform.core.cases bawaan Agent Builder bersifat hanya baca (digunakan untuk menelusuri dan mengambil kasus yang ada), buat Alur Kerja Kibana (create-security-case) yang memanggil langkah cases.createCase asli Kibana, dan daftarkan sebagai alat workflow bernama create_security_case:

    • Opsi 1: Melalui UI Kibana:

      1. Buka menu navigasi utama, lalu buka Management > Workflows (atau buka langsung https://${KIBANA_IP}:5601/app/workflows), lalu klik Create a new workflow.
      2. Di editor YAML, tempel definisi alur kerja berikut dan klik 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. Kembali ke halaman Tools Agent Builder (https://${KIBANA_IP}:5601/app/agent_builder/tools, atau buka Elasticsearch > Agents, lalu klik Manage all tools) dan klik New tool.

      4. Konfigurasikan kolom alat alur kerja, lalu klik Save:

        • ID Alat: create_security_case
        • Type: Workflow
        • Alur kerja: Pilih Buat Kasus Keamanan (create-security-case)
        • Deskripsi: Creates a new Kibana Case in Elastic Security with a title, markdown description of the incident findings, and severity (low, medium, high, critical).
        • Label: security, cases
    • Opsi 2: Melalui 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}'
      

Membuat agen Incident Responder kustom

Buat agen web-security-incident-responder dan tetapkan alat investigate_suspicious_web_clients, create_security_case, dan platform.core.cases:

  • Opsi 1: Melalui UI Kibana:

    1. Buka menu navigasi utama, lalu buka Elasticsearch > Agents (atau buka langsung ke https://${KIBANA_IP}:5601/app/agent_builder/agents), lalu klik New agent.
    2. Konfigurasikan detail agen:

      • ID Agen: web-security-incident-responder
      • Nama tampilan: Web Security Incident Responder
      • Deskripsi tampilan: Investigates suspicious HTTP error activity in web access logs and opens Kibana Security Cases with the findings.
      • Label: security, incident-response
      • Petunjuk Khusus:

        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.
        
      • Alat: Pilih investigate_suspicious_web_clients, create_security_case, dan platform.core.cases.

    3. Klik Simpan.

  • Opsi 2: Melalui 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}'
    

Menjalankan agen dan memverifikasi Kasus Kibana

  1. Jalankan percakapan dengan agen Web Security Incident Responder:

    • Opsi 1: Melalui UI Kibana:

      1. Buka menu navigasi utama, lalu buka Elasticsearch > Agents (atau buka langsung ke https://${KIBANA_IP}:5601/app/agent_builder).
      2. Pilih Web Security Incident Responder dari pemilih agen dan pastikan konektor LLM Anda (${LLM_CONNECTOR_NAME}) dipilih.
      3. Masukkan perintah berikut:

        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.

    • Opsi 2: Melalui 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
        }'
      

      Output-nya akan terlihat seperti berikut:

      {
        "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. Verifikasi Kasus Keamanan Kibana yang baru dibuat:

    • Opsi 1: Melalui UI Kibana:

      1. Buka menu navigasi utama, lalu buka Security > Cases (atau buka langsung ke https://${KIBANA_IP}:5601/app/security/cases).
      2. Klik Malicious Reconnaissance and Exploitation Attempt from 30.156.16.164 untuk melihat ringkasan insiden Markdown yang diformat, analisis MITRE ATT&CK, contoh URL berbahaya, dan checklist remediasi.
    • Opsi 2: Melalui 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}'
      

      Output-nya akan terlihat seperti berikut:

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

Penemuan Serangan

Attack Discovery adalah fitur AI di Elastic Security yang menganalisis notifikasi keamanan terbuka, memetakannya ke framework MITRE ATT&CK, dan menghubungkan notifikasi, alamat IP, dan akun pengguna terkait ke dalam rantai serangan visual. Karena Attack Discovery memerlukan model besar untuk menyintesis output JSON terstruktur multi-pemberitahuan dengan andal, gunakan model kelas 30B atau yang lebih besar (seperti gemma4:31b).

Membuat pemberitahuan keamanan dari log sampel

Secara default, set data kibana_sample_data_logs berisi log akses web mentah dan tidak mengisi indeks pemberitahuan Elastic Security (.alerts-security.alerts-default). Selain itu, generator data sampel Kibana menetapkan nilai @timestamp mendatang (hingga 30 hari ke depan) ke 100 permintaan pemindaian dan eksploitasi dari 30.156.16.164, yang menempatkannya di luar jendela now-30d hingga now yang dikueri oleh aturan deteksi Elastic Security.

Untuk mendemonstrasikan Attack Discovery dengan log contoh, geser @timestamp dari data 30.156.16.164 mundur 30 hari, perkaya dengan kolom source.ip dan user.name Elastic Common Schema (ECS), dan buat dua aturan deteksi yang menghasilkan notifikasi keamanan dari peristiwa tersebut:

  1. Petakan clientip ke source.ip, tetapkan user.name (www-data), dan geser @timestamp kembali 30 hari pada data 30.156.16.164 di kibana_sample_data_logs*:

    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. Buat dua aturan deteksi Elastic Security yang menargetkan kibana_sample_data_logs*. Aturan pertama mendeteksi probing login Nmap dan WordPress (Reconnaissance), dan aturan kedua mendeteksi upaya traversal jalur /etc/passwd dan eksekusi perintah jarak jauh (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. Tunggu sekitar 60 detik hingga eksekusi aturan awal selesai, lalu verifikasi bahwa 4 pemberitahuan telah diindeks ke dalam .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}'
    

    Output-nya akan terlihat seperti berikut:

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

Menjalankan Penemuan Serangan

  1. Di Kibana, buka menu navigasi utama, lalu buka Security > Attack Discovery (atau buka langsung ke https://${KIBANA_IP}:5601/app/security/attack_discovery).
  2. Pilih konektor LLM Anda (${LLM_CONNECTOR_NAME}) dari menu konektor di bagian atas halaman.
  3. Klik Buat untuk menganalisis pemberitahuan terbuka di .alerts-security.alerts-default.

Anda juga dapat memicu dan memeriksa Attack Discovery dari 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}"

Setelah pembuatan selesai (sekitar 60–90 detik), ambil rantai serangan yang ditemukan:

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

Penemuan serangan yang dihasilkan menghubungkan keempat pemberitahuan menjadi satu insiden dengan IP sumber, akun pengguna, dan taktik MITRE ATT&CK yang ditautkan:

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