סקירה כללית
במדריך הזה מוסבר איך לפרוס את Elastic Stack (Elasticsearch ו-Kibana) מקצה לקצה באשכול משותף של Google Distributed Cloud (GDC) עם air gap, ואיך לשלב אותו עם שירות של מודל שפה גדול (LLM) כדי להעריך את התכונות מבוססות-AI גנרטיבי של Elastic.
הפריסה משתמשת במפעיל הרשמי של Elastic Cloud on Kubernetes (ECK) שזמין ב-Marketplace של GDC עם air gap. האופרטור מותקן במרחב שמות של פרויקט באשכול Kubernetes משותף, ומבצע אוטומציה של הקצאת משאבים וניהול מחזור החיים של משאבי Elasticsearch ו-Kibana. אחרי הפריסה, Kibana מתחבר לנקודת קצה (endpoint) של הסקת מסקנות של LLM (למשל, Gemini מנוהל ב-GDC או שירות תואם ל-OpenAI בניהול עצמי כמו Ollama או vLLM). השילוב הזה מפעיל את יכולות מבוססות-AI גנרטיבי של Elastic, כולל Elastic AI Agent, סוכנים בהתאמה אישית שנוצרו באמצעות Agent Builder ו-Attack Discovery.
המדריך הזה נכתב עבור Elastic v9.4.0, Elastic Kubernetes Operator v3.4.0 ו-GDC v1.16.2. יכול להיות שיהיו הבדלים קלים בהוראות אם אתם משתמשים בגרסאות שונות.
ארכיטקטורה
הארכיטקטורה מורכבת מפריסת Elastic Stack שמנוהלת על ידי מפעיל ECK בתוך מרחב שמות של פרויקט באשכול משתמשים משותף של GDC. Kibana מתקשרת דרך הרשת הפנימית של GDC עם נקודת קצה להסקת LLM.

הרכיבים העיקריים בארכיטקטורה הזו:
- אשכול משותף של GDC: אשכול Kubernetes משותף בהיקף הארגון שמצורף לכמה פרויקטים ומארח חבילות תוכנה ועומסי עבודה של אפליקציות מ-GDC Marketplace.
- פרויקט GDC: מרחב השמות של הפרויקט באשכול המשותף שמבודד את מפעיל ה-ECK, אשכול Elasticsearch, מכונת Kibana, ו-Kubernetes Secrets ו-NetworkPolicies המשויכים.
- Elastic Cloud on Kubernetes (ECK) operator: מותקן מ-GDC Marketplace במרחב השמות של הפרויקט. האופרטור מנהל את המשאבים המותאמים אישית
Elasticsearchו-Kibana, ומתאים את רישיון Elastic שנדרש לתכונות מבוססות-AI גנרטיבי ולמחברים חיצוניים של מודלי שפה גדולים (LLM). - Elasticsearch: מאחסן ומבצע אינדוקס של נתונים תפעוליים ונתוני אבטחה, ומריץ שאילתות חיפוש ושאילתות ES|QL שנוצרו על ידי תכונות ה-AI והסוכנים של Kibana.
- Kibana: מארח את תכונות ה-AI הגנרטיבי של Elastic, כולל Elastic AI Agent (לחקירת יומנים בשפה טבעית וליצירת ES|QL), Agent Builder (ליצירת סוכנים וכלים מותאמים אישית בממשק שיחה על סמך נתוני Elasticsearch) ו-Attack Discovery (למתאם של התראות אבטחה ולניתוח שרשרת התקפות). Kibana גם מנהל את מחברי הפעולות של ה-LLM, שמתאמים את הקריאות לפונקציות בכמה שלבים בין המשתמש, Elasticsearch ושירות ה-LLM.
- שירות LLM: שירות LLM מנוהל (כמו Gemini ב-GDC) או שרת הסקה בניהול עצמי (כמו Ollama או vLLM) שמספק ל-Kibana יכולות של השלמת צ'אט וקריאה לכלים דרך הרשת הפנימית של GDC.
לפני שמתחילים
לפני שמתחילים בפריסה, חשוב לוודא שסביבת GDC עומדת בדרישות המוקדמות הבאות ובהנחות הבאות:
- אשכול Kubernetes משותף ופרויקט GDC: יש לכם גישה לאשכול משותף קיים ב-GDC ולפרויקט ב-GDC שמקושר לאשכול הזה. הפרויקט משמש כמכל של מרחב השמות לכל המשאבים ב-Kubernetes שנפרסו במדריך הזה. מוודאים שיש באשכול מספיק משאבי מחשוב ואחסון מתמיד כדי לארח את עומסי העבודה של מפעיל ECK, Elasticsearch ו-Kibana.
- שירות LLM: יש לכם גישה ברשת לשירות LLM, לשירות Gemini המנוהל (אם הוא זמין בסביבת GDC שלכם) או לשירות LLM בניהול עצמי שתואם ל-OpenAI (לדוגמה,
gemma4:31bשמוגש דרך Ollama או vLLM בפרויקט GPU). הפריסה של תשתית ה-LLM עצמה לא נכללת במדריך הזה, אבל הדרישות הספציפיות של המודל ושל ה-API מפורטות בקטע דרישות לשירות ה-LLM. מאחזרים את הגרסה של Elastic שמגיעה עם סביבת GDC:
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}מבקשים או מקשרים את תפקידי ה-IAM הנדרשים ברמת הפרויקט למשתמש בפרויקט היעד ב-GDC (
${PROJECT_ID}). בהתאם למסמכי התיעוד של מחזור החיים של GDC Marketplace ולדרישות הרשת של GDC, נדרשים לפחות התפקידים הבאים בפרויקט:- אדמין של מרחב שמות (
namespace-admin): הרשאת אדמין מלאה ליצירה ולניהול של עומסי עבודה, סודות, שירותים ומשאבים בהתאמה אישית של Kubernetes במרחב השמות של הפרויקט באשכול המשותף. - צפייה בפרויקט (
project-viewer): גישת קריאה כדי לצפות בפרויקט הקיים ובמשאבים שלו במסוף GDC וב-CLI. - עריכה ב-Marketplace (
marketplace-editor): הרשאה להתקין, לשדרג ולהסיר חבילות תוכנה של GDC Marketplace בפרויקט. - אדמין של NetworkPolicy בפרויקט (
project-networkpolicy-admin): הרשאה להגדיר כלליProjectNetworkPolicyגלובליים שמאפשרים לתנועה מפרויקטים אחרים ומחוץ לפרויקט להגיע לשירותי LoadBalancer של Kibana ו-Elasticsearch.
בקשו מאדמין ה-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 של האשכול המשותף ומגדירים כינוי מעטפת של
kuבשבילkubectl:KUBECONFIG=kubeconfig-${CLUSTER_NAME}.yaml gdcloud clusters \ get-credentials ${CLUSTER_NAME} \ --zone ${ZONE} alias ku="kubectl --kubeconfig=kubeconfig-${CLUSTER_NAME}.yaml"
התקנת חבילת שטחי פרסום ייעודית לזירות מסחר של Elastic
בקטע הזה תתקינו את מפעיל Elastic Cloud on Kubernetes (ECK) במרחב השמות של הפרויקט מ-Marketplace של GDC עם air gap, לפי המדריך למחזור החיים של GDC Marketplace.
התקנה באמצעות ה-CLI של gdcloud (או מסוף GDC)
מציגים את חבילות התוכנה הזמינות ב-Marketplace כדי לאשר את שם השירות ואת הגרסה הזמינה:
gdcloud marketplace services listהפלט אמור להיראות כך:
SERVICE LATEST VERSION confluent <CONFLUENT_VERSION> dataproc <DATAPROC_VERSION> elastic <ECK_OPERATOR_VERSION> mariadb <MARIADB_VERSION> mattermost <MATTERMOST_VERSION> mongodb <MONGODB_VERSION> neo4j <NEO4J_VERSION> redis <REDIS_VERSION> starboard <STARBOARD_VERSION> starburst <STARBURST_VERSION> tes <TES_VERSION> vault <VAULT_VERSION>מתקינים את חבילת שטחי פרסום ייעודית לזירות מסחר באשכול המשותף:
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).*מוודאים שה-StatefulSet וה-pod של
elastic-operatorפועלים במרחב השמות של הפרויקט (${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 Marketplace פורסת את מפעיל ה-ECK שמוגדר מראש לשליפת קובצי אימג' של קונטיינרים של Elasticsearch ו-Kibana מהמרשם המקומי של Harbor עם air gap.
יוצרים את elastic-stack.yaml כדי לפרוס אשכול Elasticsearch עם צומת אחד, מכונה של ממשק המשתמש של Kibana, שירותי LoadBalancer HTTP ו-allow-all NetworkPolicy במרחב השמות של הפרויקט (${PROJECT_ID}). המניפסט כולל גם רישיון Secret שתואם למפעיל ECK, שמוגדר כאן כגרסת ניסיון של 30 יום ל-Enterprise (eck-trial-license) למקרה שעדיין אין לכם רישיון Enterprise משלכם:
cat <<EOF > elastic-stack.yaml
apiVersion: v1
kind: Secret
metadata:
name: eck-trial-license
namespace: ${PROJECT_ID}
labels:
license.k8s.elastic.co/type: enterprise_trial
annotations:
elastic.co/eula: accepted
---
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
name: elastic-cluster
namespace: ${PROJECT_ID}
spec:
version: ${ELASTIC_VERSION}
http:
service:
spec:
type: LoadBalancer
nodeSets:
- name: default
count: 1
config:
# Disable mmap to avoid requiring privileged host sysctl modifications
node.store.allow_mmap: false
podTemplate:
spec:
initContainers:
- name: sysctl
securityContext:
privileged: false
allowPrivilegeEscalation: false
runAsNonRoot: true
capabilities:
drop:
- ALL
command: ["sh", "-c", "true"]
containers:
- name: elasticsearch
resources:
requests:
memory: 2Gi
cpu: 500m
limits:
memory: 4Gi
cpu: 2
volumeClaimTemplates:
- metadata:
name: elasticsearch-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
name: kibana-ui
namespace: ${PROJECT_ID}
spec:
version: ${ELASTIC_VERSION}
count: 1
elasticsearchRef:
name: elastic-cluster
http:
service:
spec:
type: LoadBalancer
podTemplate:
spec:
containers:
- name: kibana
resources:
requests:
memory: 1Gi
cpu: 200m
limits:
memory: 2Gi
cpu: 1
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
namespace: ${PROJECT_ID}
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
cidr: 0.0.0.0/0
EOF
החלה ואימות של הפריסה
החלת המניפסט:
ku apply -f elastic-stack.yaml
מחילים ProjectNetworkPolicy גלובלי כדי שכללי חומת האש של GDC יאפשרו תעבורה נכנסת לכתובות ה-IP של איזון העומסים, וגם תעבורה בין פרויקטים (למשל, תקשורת עם שירות ה-LLM):
kubectl --kubeconfig=kubeconfig-global.yaml apply -f - <<EOF
apiVersion: networking.global.gdc.goog/v1
kind: ProjectNetworkPolicy
metadata:
namespace: ${PROJECT_ID}
name: allow-inbound-traffic-from-external
spec:
policyType: Ingress
subject:
subjectType: UserWorkload
ingress:
- from:
- ipBlock:
cidr: 0.0.0.0/0
---
apiVersion: networking.global.gdc.goog/v1
kind: ProjectNetworkPolicy
metadata:
namespace: ${PROJECT_ID}
name: allow-inbound-traffic-from-all-projects
spec:
policyType: Ingress
subject:
subjectType: UserWorkload
ingress:
- from:
- projects: {}
EOF
עוקבים אחרי הפודים, השירותים והמשאבים המותאמים אישית עד שהסטטוס של Elasticsearch ו-Kibana מגיע ל-Running ו-Ready, ומקבלים כתובות IP של LoadBalancer ב-${CLUSTER_NAME}:
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
מייצאים את כתובות ה-IP החיצוניות של LoadBalancer שהוקצו ל-Elasticsearch ול-Kibana, כדי להשתמש בהן בשלבים הבאים:
export ELASTICSEARCH_IP=$(ku get svc elastic-cluster-es-http \
-n ${PROJECT_ID} \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')
export KIBANA_IP=$(ku get svc kibana-ui-kb-http \
-n ${PROJECT_ID} \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo "Kibana External URL: https://${KIBANA_IP}:5601"
echo "Elasticsearch External URL: https://${ELASTICSEARCH_IP}:9200"
אחזור פרטי הכניסה וגישה לממשק המשתמש של Kibana
אחזור הסיסמה של משתמש-העל ב-elastic
ECK יוצר באופן אוטומטי סוד של Kubernetes (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}"
אימות החיבור והתחברות לחשבון
מוודאים שאפשר לגשת ל-Elasticsearch דרך HTTPS מתחנת העבודה:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" "https://${ELASTICSEARCH_IP}:9200"פותחים את
https://${KIBANA_IP}:5601בדפדפן, מאשרים את האזהרה לגבי האישור בחתימה עצמית ונכנסים באמצעות:- שם משתמש:
elastic - סיסמה:
<output from ELASTIC_PASSWORD above>
- שם משתמש:
אימות הרישיון של Elastic
כדי להשתמש בתכונות מבוססות-AI גנרטיבי של Elastic (כולל סוכן AI של Elastic, Agent Builder ו-Attack Discovery) ובמחברים חיצוניים של LLM, צריך רישיון Enterprise (או רישיון Enterprise פעיל ל-30 ימי ניסיון).
כשמפעיל ECK מנהל אשכול Elasticsearch, ECK license-controller מבצע באופן רציף תהליך של התאמת רישיון האשכול ל-Kubernetes Secrets במרחב השמות של המפעיל (${PROJECT_ID}). אם עדיין אין לכם רישיון Enterprise לשימוש בסביבת ייצור, ה-Secret eck-trial-license שכלול ב-elastic-stack.yaml (license.k8s.elastic.co/type:
enterprise_trial) מורה ל-ECK להקצות ולתחזק באופן אוטומטי רישיון ניסיון Enterprise ל-30 יום. (שימו לב שאם תתחילו תקופת ניסיון באופן ידני דרך POST
/_license/start_trial בלי סוד רישיון ECK, ECK יחזיר את האשכול למצב basic במחזור ההתאמה הבא).
כדי לאמת את הרישיון הפעיל באמצעות CLI:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \
"https://${ELASTICSEARCH_IP}:9200/_license" | jq .
*(לחלופין, ב-Kibana, פותחים את תפריט הניווט ועוברים אל Management > Dev Tools, ואז מריצים את GET /_license בכרטיסייה Console.)*
הפלט אמור להיראות כך, עם "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
}
}
טעינה של מערך נתונים מובנה לדוגמה
כדי להעריך את תכונות ה-AI של Elastic בלי לכתוב צינורות עיבוד נתונים מותאמים אישית להטמעת נתונים או לדרוש גישה לאינטרנט החיצוני, אפשר לטעון את מערך הנתונים המובנה של יומני אינטרנט לדוגמה של Kibana (kibana_sample_data_logs). מכיוון שארכיון הנתונים לדוגמה מקובץ בתוך קובץ האימג' של הקונטיינר של Kibana, הוא מותקן תוך שניות בסביבה עם air gap. קבוצת הנתונים מכילה 14,074 מסמכי יומן גישה לאינטרנט עם כתובות ה-IP של הלקוח (clientip), קודי סטטוס של HTTP (response), מדינות המקור והיעד (geo.src, geo.dest), בייטים שהועברו (bytes), כתובות URL ומחרוזות של סוכן משתמש.
אפשר לעיין במאמרים הוספת נתוני דוגמה ב-Kibana ו-elastic-datasets-builtin.md כדי לקבל מידע על מערכי נתונים מובנים נוספים (flights ו-ecommerce).
טעינת מערך הנתונים
מריצים את הפקודה הבאה כדי לטעון את kibana_sample_data_logs דרך נקודת הקצה הפנימית של הנתונים לדוגמה של 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"
אימות מערך הנתונים לדוגמה
מוודאים שמערך הנתונים לדוגמה logs מסומן כ-installed ב-Kibana, ומוודאים שזרם הנתונים התומך מכיל מסמכי 14,074 ב-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"
הפלט אמור להיראות כך:
{
"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 עם air gap, אי אפשר להגיע למודלים מסוג LLM של Elastic Managed שמתארחים בענן ולספקי מודלים מסוג LLM של SaaS ציבורי חיצוניים. בהתאם לסביבת GDC:
- Managed Gemini ב-GDC: בסביבות GDC שבהן Gemini מוצע כשירות מנוהל של מודל שפה גדול (LLM), אפשר לקשר את Kibana ישירות לשירות המנוהל של Gemini, אם הוא זמין.
- שירות LLM בניהול עצמי: לחלופין, אתם יכולים לחבר את Elastic לשירות LLM בניהול עצמי שמתארח בסביבת GDC (לדוגמה, בצומתי GPU של GDC).
המדריך הזה לא כולל הוראות לפריסת שירות LLM בניהול עצמי. כשמקצים או בוחרים שירות LLM בניהול עצמי ל-Elastic, צריך לוודא שהוא עומד בדרישות שנגזרות מהמדריכים הרשמיים של Elastic:
- הגדרת הגישה למודלי LLM
- מודלים מותאמים אישית של LLM בניהול עצמי (ראו גם התחברות ל-vLLM בסביבות עם air gap)
- יכולת צפייה (observability) ב-LLM וב-AI אקטיבי
סיכום הדרישות הטכניות
הנקודות הבאות מסכמות את הדרישות העיקריות של ה-API, יכולות המודל, חלון ההקשר וזמן הריצה שנדרשות כדי ששירות LLM בניהול עצמי יפעל בצורה מהימנה עם סוכן AI, Agent Builder ו-Attack Discovery של Elastic. שימו לב שהשמות של פרמטרים ספציפיים להגדרה שמוזכרים בקטעים הבאים (כמו num_ctx, num_predict, repeat_penalty, repeat_last_n, OLLAMA_FLASH_ATTENTION ו-OLLAMA_NUM_PARALLEL) מופיעים באמצעות Ollama כדוגמה. אם אתם משתמשים במנוע הסקה אחר, כמו vLLM, אתם צריכים להחיל את ההגדרות המקבילות.
- OpenAI-compatible chat completions API (
/v1/chat/completions) with native function calling:- אפשר לשלב כל שרת הסקה בניהול עצמי (כמו Ollama או vLLM) שחושף נקודת קצה
/v1/chat/completionsשתואמת ל-OpenAI (כולל סטרימינג של אירועים שנשלחים מהשרת באמצעות"stream": true) באמצעות מחבר OpenAI של Kibana, כשהספק מוגדר לאחר (שירות שתואם ל-OpenAI). - מפעילים קריאה להפעלת פונקציות מקוריות (
"enableNativeFunctionCalling": true) במחבר של Kibana כשמשתמשים במודלים שתומכים בקריאה להפעלת כלים מבוססי-ענן (כמוgemma4:31b). כברירת מחדל ("enableNativeFunctionCalling": false), Kibana משתמש בקריאה להפעלת פונקציות מדומות שמבוססות על הנחיות, שהיא פחות אמינה במהלך קריאות להפעלת כלים בתצוגת עץ ויכולה להיכשל עם שגיאות ניתוח (כמוError: Missing name for tool use). הפעלת קריאה להפעלת פונקציות מקוריות מעבירה את העיצוב של הקריאה להפעלת כלים ישירות לפרוטוקול OpenAItoolsשל שרת ההסקה.
- אפשר לשלב כל שרת הסקה בניהול עצמי (כמו Ollama או vLLM) שחושף נקודת קצה
- מודל שעבר כוונון לפי הוראות עם יכולות מובנות של שימוש בכלים וקריאה לפונקציות:
- סוכני AI גמישים (
elastic-ai-agent) וסוכנים מותאמים אישית ב-Agent Builder מסתמכים על הפעלת כלים מרובת-שלבים (platform.core.list_indices,platform.core.get_index_mapping,platform.core.search,platform.core.execute_esqlו-platform.core.cases). הם משתמשים בכלים האלה כדי לגלות מיפויים של אינדקסים, ליצור ולהפעיל שאילתות ES|QL ב-Elasticsearch ולסכם את הממצאים. - המודל שנבחר צריך להיות מכוונן לפי הוראות ומסוגל ליצור קריאות תקינות לכלים מבוססי-ענן, פלטים מובְנים של JSON ושאילתות ES|QL. כדי לראות את הקריטריונים להערכת המודל, עיינו במאמרים של Elastic בנושא מטריצת הביצועים של LLM ל-Observability ומטריצת הביצועים של LLM ל-Elastic Security.
- סוכני AI גמישים (
- חלון הקשר של לפחות
128,000טוקנים (לדוגמה,num_ctx: 131072ב-Ollama):- במסמכי התיעוד הכלליים של Elastic לניהול עצמי של מודל LLM בהתאמה אישית מצוין מינימום של 64,000 טוקנים, אבל מומלץ מאוד להשתמש בחלון הקשר של 128,000 טוקנים (
131,072טוקנים) בשביל Elastic AI Agent (elastic-ai-agent). - במהלך חקירות רב-שלביות, הסוכן משלב הוראות מערכת והגדרות כלים עם סכימות אינדקס, תיעוד של ES|QL ותוצאות חיפוש גולמיות (לדוגמה, אחזור של עד 100 מסמכי דוגמה כדי לבדוק ערכי שדות לפני צבירה). בחקירה אחת יכולים להיות יותר מ-70,000 טוקנים בהיסטוריית השיחות, ולכן חלון הקשר של 64,000 טוקנים יקטע את ההנחיה באמצע השיחה.
- חשוב לשים לב שב-Ollama, הוראת
PARAMETER num_ctxשמוטמעת ב-Modelfileשל המודל מבטלת את משתנה הסביבהOLLAMA_CONTEXT_LENGTHשל הקונטיינר. מוודאים שגםOLLAMA_CONTEXT_LENGTH=131072וגםPARAMETER num_ctx 131072מוגדרים ב-Modelfileשל המודל (ב-GPU של NVIDIA A100 או H100 בנפח 80GB, gemma4:31bבהקשר של128Kמתאים ל-VRAM).
- במסמכי התיעוד הכלליים של Elastic לניהול עצמי של מודל LLM בהתאמה אישית מצוין מינימום של 64,000 טוקנים, אבל מומלץ מאוד להשתמש בחלון הקשר של 128,000 טוקנים (
- מגבלת טוקנים לפלט וקנס קל על חזרה (לדוגמה,
num_predict: 8192ו-repeat_penalty: 1.05ב-Ollama):- המתאם של Kibana לסטרימינג של OpenAI מתייחס אל
"finish_reason": "length"כשגיאה חמורהToken limit reachedושולח"temperature": 0(פענוח חמדני) בכל בקשה. - במודלים עם יכולת הסקת מסקנות (כמו
gemma4:31b, שיוצר אסימוני חשיבה פנימיים לפני שהוא מפעיל קריאות לפונקציות), צריך להגדיר מגבלת אסימונים של לפחות8,192אסימונים וקנס קל על חזרה על מילים. לדוגמה, ב-Ollama, מגדירים אתnum_predict: 8192,repeat_penalty: 1.05ו-repeat_last_n: 64. הימנעו מקנסות אגרסיביים מדי על חזרות (כמו1.15+על חלונות גדולים), כי הם מקשים על המודל להעתיק שאילתת ES|QL מבלוק החשיבה הפנימי שלו ל-JSON הסופי של הקריאה לכלי.
- המתאם של Kibana לסטרימינג של OpenAI מתייחס אל
- האצת GPU, Flash Attention ומשבצות מקבילות של מטמון KV (פחות מ-60 שניות לכל תור):
- המחבר של OpenAI ב-Kibana אוכף זמן קצוב לתפוגה של HTTP כברירת מחדל של 60 שניות לכל סבב סטרימינג.
- מכיוון שפנייה אחת של סוכן יכולה להפעיל קריאות משנה מקוננות של LLM (לדוגמה, כדי לתרגם בקשות חיפוש בשפה טבעית לשאילתות ES|QL בזמן שהשיחה הראשית עם הסוכן פעילה), הפעלה של שרת הסקת מסקנות עם משבצת אחת בלבד של מטמון KV עלולה לגרום לקריאות משנה להוציא מהמטמון את ההקשר של השיחה הראשית ולהאט את הפניות הבאות.
- ב-Ollama, מגדירים את
OLLAMA_FLASH_ATTENTION=1ו-OLLAMA_NUM_PARALLEL=2כדי ש-Flash Attention יאיץ את הערכת ההקשר הארוך, והשיחה הראשית והקריאות המשניות המקוננות ישמרו על משבצות נפרדות של מטמון KV בזיכרון ה-GPU.
הגדרת מחבר LLM ב-Kibana
אחרי ששירות LLM שתואם ל-OpenAI פועל ואפשר להגיע אליו מ-Kibana pod, מגדירים מחבר OpenAI ב-Kibana באמצעות ה-CLI או ממשק המשתמש של Kibana.
אפשרות 1: יצירת המחבר באמצעות ה-CLI
מריצים את הפקודה הבאה של
POST /api/actions/connectorכדי ליצור מחבר OpenAI (.gen-ai) שמפנה לשירות ה-LLM (${LLM_API_URL}) עם קריאה להפעלת פונקציות מקורית ("enableNativeFunctionCalling": true). המערכת של Kibana יוצרת ומקצה באופן אוטומטי מזהה מחבר ייחודי: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 .כדי לוודא שהמחבר
${LLM_CONNECTOR_NAME}קיים ואין בו סודות חסרים, מציגים את המחברים של Kibana ומסננים לפי שם: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 }כדי לבדוק את הקישוריות מקצה לקצה בין Kibana לבין שירות ה-LLM מה-CLI, מחפשים את מזהה המחבר שנוצר אוטומטית לפי השם ומפעילים את נקודת הקצה (endpoint) שלו ב-
_execute:export CONNECTOR_ID=$(curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ "https://${KIBANA_IP}:5601/api/actions/connectors" \ -H "kbn-xsrf: true" \ | jq -r ".[] | select(.name == \"${LLM_CONNECTOR_NAME}\") | .id") curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "https://${KIBANA_IP}:5601/api/actions/connector/${CONNECTOR_ID}/_execute" \ -H "kbn-xsrf: true" \ -H "Content-Type: application/json" \ -d "{ \"params\": { \"subAction\": \"invokeAI\", \"subActionParams\": { \"model\": \"${LLM_MODEL_NAME}\", \"messages\": [ { \"role\": \"user\", \"content\": \"Reply with the single word Pong.\" } ] } } }" | jq .הפלט אמור להיראות כך, עם
"status": "ok"והתשובה של המודל:{ "status": "ok", "data": { "message": "Pong", "usage": { "prompt_tokens": 23, "prompt_tokens_details": { "cached_tokens": 0 }, "completion_tokens": 38, "total_tokens": 61 } }, "connector_id": "<GENERATED_CONNECTOR_ID>" }
אפשרות 2: יצירת המחבר דרך ממשק המשתמש של Kibana
- נכנסים ל-Kibana (
https://${KIBANA_IP}:5601). - פותחים את תפריט הניווט הראשי ועוברים אל Management > Alerts and Insights > Connectors.
- לוחצים על יצירת מחבר ובוחרים באפשרות OpenAI.
- מגדירים את השדות של המחבר:
- Connector name: מזינים את הערך של
${LLM_CONNECTOR_NAME}(לדוגמה,Gemma4-31B). - Select an OpenAI provider: בוחרים באפשרות Other (OpenAI Compatible Service).
- כתובת URL: מזינים את הערך של
${LLM_API_URL}(נקודת הקצה/v1/chat/completionsשל שירות ה-LLM). - מודל ברירת המחדל: מזינים את הערך של
${LLM_MODEL_NAME}(לדוגמה,gemma4:31b). - מפתח API: מזינים את הערך של
${LLM_API_KEY}(כל מחרוזת לא ריקה אם השרת לא אוכף אימות bearer, או מפתח ה-API של השרת). - הפעלת קריאה להפעלת פונקציות מקוריות: מפעילים את המתג.
- Connector name: מזינים את הערך של
- לוחצים על שמירה ובדיקה כדי לשמור את המחבר ולפתוח מיד את הכרטיסייה בדיקה.
- בכרטיסייה Test (בדיקה), מזינים הנחיה לדוגמה (למשל,
Reply with the single word Pong.) ולוחצים על Run (הפעלה) כדי לוודא ש-Kibana מקבל תגובה תקינה משירות ה-LLM.
בדיקת השילובים של מודלי שפה גדולים (LLM)
אחרי שמתקינים את קבוצת הנתונים kibana_sample_data_logs ומאמתים את המחבר ל-LLM, אפשר לבדוק את יכולות ה-AI של Elastic ב-Kibana.
Elastic AI Agent
Elastic AI Agent (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.
כדי לפתוח ולהגדיר את Elastic AI Agent בממשק המשתמש של Kibana:
- פותחים את תפריט הניווט הראשי ב-Kibana ובוחרים באפשרות סוכן AI (או לוחצים על הלחצן Ask AI בסרגל הכותרת העליון, או עוברים ישירות אל
https://${KIBANA_IP}:5601/app/chat). - מוודאים שElastic AI Agent נבחר כסוכן הפעיל, ושהמחבר של מודל השפה הגדול (
${LLM_CONNECTOR_NAME}) נבחר כמחבר המודל.
דוגמה 1: בדיקת בקשות HTTP שנכשלו וסורקים פוטנציאליים
מבקשים מ-Elastic AI Agent לזהות את כתובות ה-IP של הלקוחות המובילים שמייצרות תגובות HTTP שאינן 200 ב-kibana_sample_data_logs, את מדינות המקור שלהן ואם לאחת מכתובות ה-IP האלה הייתה גם תנועת HTTP 200 מוצלחת:
אפשרות 1: דרך ממשק המשתמש של Kibana: מזינים את השאלה הבאה בפרומפט של הצ'אט עם סוכן ה-AI:
In kibana_sample_data_logs, which client IP addresses had the most failed HTTP requests (non-200 status codes), what countries did they come from, and did any of those IPs also have successful HTTP 200 requests?אפשרות 2: דרך ה-CLI:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "https://${KIBANA_IP}:5601/api/agent_builder/converse" \ -H "kbn-xsrf: true" \ -H "Content-Type: application/json" \ -d "{ \"agent_id\": \"elastic-ai-agent\", \"connector_id\": \"${CONNECTOR_ID}\", \"input\": \"In kibana_sample_data_logs, which client IP addresses had the most failed HTTP requests (non-200 status codes), what countries did they come from, and did any of those IPs also have successful HTTP 200 requests?\" }" | jq '{ status: .status, tools_called: [.steps[] | select(.type == "tool_call") | {tool_id: .tool_id, params: .params}], response: .response.message }'
מה קורה מאחורי הקלעים:
- הסוכן מתקשר אל
platform.core.get_index_mapping(indices=["kibana_sample_data_logs"])כדי לבדוק את השדות הזמינים (clientip,geo.src,response,bytes,requestוכו'). הוא קורא ל-
platform.core.searchכדי לבדוק מסמכים לדוגמה ולוודא שקודי הסטטוס של HTTP מאוחסנים ב-response, ואז מפעיל צבירות של ES|QL כדי לדרג בקשות שהן לא200לפיclientipו-geo.srcולבדוק אילו מכתובות ה-IP המובילות תיעדו גם תנועה שלresponse == "200":FROM kibana_sample_data_logs | WHERE response != "200" | STATS failed_count = COUNT(*) BY clientip, geo.src | SORT failed_count DESC | LIMIT 10FROM kibana_sample_data_logs | WHERE clientip IN ("30.156.16.164", "213.235.30.99", "221.241.228.46", "209.94.234.133", "7.127.73.138", "247.240.202.244", "130.125.65.24", "164.85.94.243", "81.194.200.150", "30.145.143.210") AND response == "200" | STATS success_count = COUNT(*) BY clientip
התשובה של Elastic AI Agent אמורה להיראות כך:
כתובת ה-IP של הלקוח עם הכי הרבה בקשות HTTP שנכשלו (קודי סטטוס שאינם 200) בפרק זמן של
kibana_sample_data_logsהיא 30.156.16.164, שמקורה בארצות הברית ונרשמו בה 100 בקשות שנכשלו.כתובות IP מובילות אחרות עם בקשות שנכשלו, שכולן מקורן גם בארצות הברית, כוללות:
- 213.235.30.99: 6 כשלים
- 221.241.228.46: 6 כשלים
- 209.94.234.133: 6 כשלים
- 7.127.73.138: 5 כשלים
- 247.240.202.244: 5 כשלים
- 130.125.65.24: 5 כשלים
- 164.85.94.243: 4 כשלים
- 81.194.200.150: 4 כשלים
- 30.145.143.210: 4 כשלים
לגבי בקשות שהצליחו, לכל כתובות ה-IP שרשומות למעלה היו לפחות כמה תגובות HTTP 200 מוצלחות, חוץ מ-30.156.16.164, שלא היו לה בקשות מוצלחות.
דוגמה 2: ניתוח נפח התנועה והבייטים של המטען הייעודי (payload) לפי קוד סטטוס של HTTP
מבקשים מסוכן ה-AI של Elastic פירוט של קודי הסטטוס של תגובות ה-HTTP בכל הבקשות ב-kibana_sample_data_logs, ואת מספר הבייטים הכולל שהועברו לכל קוד סטטוס:
אפשרות 1: דרך ממשק המשתמש של Kibana: מתחילים שיחה חדשה ב-סוכן AI ומזינים את השאלה הבאה:
In kibana_sample_data_logs, what is the breakdown of HTTP response status codes across all requests, and how much total data (in bytes) was transferred for each status code?אפשרות 2: באמצעות ה-CLI:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "https://${KIBANA_IP}:5601/api/agent_builder/converse" \ -H "kbn-xsrf: true" \ -H "Content-Type: application/json" \ -d "{ \"agent_id\": \"elastic-ai-agent\", \"connector_id\": \"${CONNECTOR_ID}\", \"input\": \"In kibana_sample_data_logs, what is the breakdown of HTTP response status codes across all requests, and how much total data (in bytes) was transferred for each status code?\" }" | jq '{ status: .status, tools_called: [.steps[] | select(.type == "tool_call") | {tool_id: .tool_id, params: .params}], response: .response.message }'
מה קורה מאחורי הקלעים:
- הסוכן בודק את סכימת
kibana_sample_data_logsבאמצעותplatform.core.get_index_mappingומזהה את השדותresponse.keywordו-bytes. הוא מפעיל את
platform.core.search, שיוצר ומפעיל את הצבירה הבאה של ES|QL:FROM kibana_sample_data_logs | STATS request_count = COUNT(*), total_bytes = SUM(bytes) BY response.keyword | LIMIT 100
התשובה של Elastic AI Agent אמורה להיראות כך:
פירוט של קודי מצב התגובה של HTTP והנתונים הכוללים שהועברו ב-
kibana_sample_data_logs:
| קוד סטטוס | מספר הבקשות | סה"כ נתונים שהועברו (בייטים) |
|---|---|---|
| 200 | 12,832 | 75,681,246 |
| 404 | 801 | 4,044,443 |
| 503 | 441 | 0 |
סוכנים בהתאמה אישית באמצעות Agent Builder
Agent Builder מאפשר ליצור סוכני AI בהתאמה אישית לתהליכי עבודה תפעוליים ספציפיים, על ידי שילוב של הוראות מערכת מותאמות אישית עם כלים מובנים (כמו platform.core.cases, platform.core.execute_esql ו-platform.core.search) וכלים שמוגדרים על ידי המשתמש (esql, index_search, workflow ו-mcp).
בדוגמה הבאה אנחנו יוצרים סוכן Web Security Incident Responder שפועל ישירות על מערך הנתונים kibana_sample_data_logs. בהינתן פרומפט בשפה טבעית, הסוכן:
- מריץ כלי ES|QL מותאם אישית (
investigate_suspicious_web_clients) כדי לצבור תגובות שאינן200HTTP ב-kibana_sample_data_logsלפי כתובת IP של הלקוח. - מבחין בין פעילות זדונית של איסוף מידע וניצול לרעה (
30.156.16.164) לבין כשלים תקינים בהורדה. - מפעיל כלי מותאם אישית של Kibana Workflow (
create_security_case) כדי לפתוח באופן אוטומטי Kibana Security Case מובנה עם הממצאים ושלבי התיקון.
יצירת כלים מותאמים אישית של ES|QL וליצירת פניות
יוצרים כלי
esqlבהתאמה אישית בשםinvestigate_suspicious_web_clientsששולח שאילתות ל-kibana_sample_data_logsכדי לקבל את כתובות ה-IP של הלקוחות המובילים שיוצרים קודי סטטוס של HTTP שאינם200, יחד עם קודי הסטטוס שלהם, מדינת המקור (geo.src), סוכני המשתמש (agent) וכתובות ה-URL המבוקשות (request):אפשרות 1: דרך ממשק המשתמש של Kibana:
- ב-Kibana, פותחים את תפריט הניווט הראשי ועוברים אל Elasticsearch > Agents (סוכנים), ואז לוחצים על Manage all tools (ניהול כל הכלים) (או עוברים ישירות אל
https://${KIBANA_IP}:5601/app/agent_builder/tools) ולוחצים על New tool (כלי חדש). מגדירים את שדות הכלי:
- מזהה הכלי:
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
- מזהה הכלי:
לוחצים על Save.
- ב-Kibana, פותחים את תפריט הניווט הראשי ועוברים אל Elasticsearch > Agents (סוכנים), ואז לוחצים על Manage all tools (ניהול כל הכלים) (או עוברים ישירות אל
אפשרות 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}'
מכיוון שהכלי המובנה
platform.core.casesב-Agent Builder הוא לקריאה בלבד (משמש לחיפוש ולאחזור של כרטיסי תמיכה קיימים), צריך ליצור Kibana Workflow (create-security-case) שמפעיל את השלב המקוריcases.createCaseשל Kibana, ולרשום אותו ככליworkflowבשםcreate_security_case:אפשרות 1: דרך ממשק המשתמש של Kibana:
- פותחים את תפריט הניווט הראשי ועוברים אל ניהול > תהליכי עבודה (או עוברים ישירות אל
https://${KIBANA_IP}:5601/app/workflows), ואז לוחצים על יצירת תהליך עבודה חדש. בעורך ה-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חוזרים לדף Tools ב-Agent Builder (
https://${KIBANA_IP}:5601/app/agent_builder/tools, או עוברים אל Elasticsearch > Agents ולוחצים על Manage all tools) ולוחצים על New tool.מגדירים את השדות של כלי תהליך העבודה ולוחצים על שמירה:
- מזהה הכלי:
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:
- פותחים את תפריט הניווט הראשי ועוברים אל Elasticsearch > Agents (או עוברים ישירות אל
https://${KIBANA_IP}:5601/app/agent_builder/agents) ולוחצים על New agent. מגדירים את פרטי הסוכן:
- מזהה הנציג:
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.
- מזהה הנציג:
לוחצים על Save.
- פותחים את תפריט הניווט הראשי ועוברים אל Elasticsearch > Agents (או עוברים ישירות אל
אפשרות 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
מנהלים שיחה עם הסוכן Web Security Incident Responder:
אפשרות 1: דרך ממשק המשתמש של Kibana:
- פותחים את תפריט הניווט הראשי ועוברים אל Elasticsearch > Agents (או עוברים ישירות אל
https://${KIBANA_IP}:5601/app/agent_builder). - בבורר הנציגים, בוחרים באפשרות Web Security Incident Responder ומוודאים שהמחבר ל-LLM (
${LLM_CONNECTOR_NAME}) נבחר. מזינים את ההנחיה הבאה:
Investigate the top client IPs generating HTTP errors in our web logs, identify any attacker performing reconnaissance or exploitation, and open a high-severity Kibana Security Case with your findings.
- פותחים את תפריט הניווט הראשי ועוברים אל Elasticsearch > Agents (או עוברים ישירות אל
אפשרות 2: באמצעות ה-CLI:
curl -sk -u "elastic:${ELASTIC_PASSWORD}" \ -X POST "https://${KIBANA_IP}:5601/api/agent_builder/converse" \ -H "kbn-xsrf: true" \ -H "Content-Type: application/json" \ -d "{ \"agent_id\": \"web-security-incident-responder\", \"connector_id\": \"${CONNECTOR_ID}\", \"input\": \"Investigate the top client IPs generating HTTP errors in our web logs, identify any attacker performing reconnaissance or exploitation, and open a high-severity Kibana Security Case with your findings.\" }" | jq '{ status: .status, tools_called: [.steps[] | select(.type == "tool_call") | .tool_id], response: .response.message }'הפלט אמור להיראות כך:
{ "status": "completed", "tools_called": [ "investigate_suspicious_web_clients", "create_security_case" ], "response": "I have completed the investigation into the web logs. \n\nThe analysis identified client IP **`30.156.16.164`** (located in the US) as a malicious actor. This IP generated 100 failed requests (HTTP 404) exhibiting clear patterns of reconnaissance and exploitation:\n\n* **Reconnaissance:** Extensive probing for WordPress installations and plugins, as well as Nmap scan signatures (e.g., `/nmaplowercheck...`).\n* **Path Traversal:** Attempts to access sensitive system files via `/etc/passwd`.\n* **Remote Command Execution (RCE):** Attempts to execute commands through paths such as `/level/15/exec`.\n* **Application Probing:** Targeting common management interfaces including Tomcat, Axis2, and JMX consoles.\n\nAs a result, I have opened a **high-severity** Kibana Security Case to track this incident:\n\n* **Case ID:** `1f0e00a5-cc89-4f62-b305-f286fae3d70e`\n* **Title:** Malicious Reconnaissance and Exploitation Attempt from 30.156.16.164\n* **Recommended Action:** Immediately block the IP at the firewall/WAF and verify that no unauthorized access was achieved." }
מאמתים את Kibana Security Case שנוצר לאחרונה:
אפשרות 1: דרך ממשק המשתמש של Kibana:
- פותחים את תפריט הניווט הראשי ועוברים אל אבטחה > פניות (או עוברים ישירות אל
https://${KIBANA_IP}:5601/app/security/cases). - לוחצים על Malicious Reconnaissance and Exploitation Attempt from 30.156.16.164 (ניסיון זדוני לסיור ולניצול לרעה מכתובת 30.156.16.164) כדי לראות את סיכום התקרית בפורמט Markdown, ניתוח MITRE ATT&CK, דוגמאות לכתובות URL זדוניות ורשימת משימות לתיקון.
- פותחים את תפריט הניווט הראשי ועוברים אל אבטחה > פניות (או עוברים ישירות אל
אפשרות 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 הוא פיצ'ר מבוסס-AI ב-Elastic Security שמנתח התראות אבטחה פתוחות, ממפה אותן למסגרת MITRE ATT&CK ומקשר בין התראות, כתובות IP וחשבונות משתמשים קשורים לשרשראות התקפה חזותיות. התכונה Attack Discovery (גילוי מתקפות) דורשת מודל גדול כדי לסנתז באופן מהימן פלט JSON מובנה של כמה התראות, ולכן צריך להשתמש במודל מסוג 30B ומעלה (למשל gemma4:31b).
יצירת התראות אבטחה מיומני הדוגמה
כברירת מחדל, קבוצת הנתונים kibana_sample_data_logs מכילה יומני גישה גולמיים לאינטרנט ולא מאכלסת את אינדקס התראות האבטחה של Elastic Security (.alerts-security.alerts-default). בנוסף, מחולל הנתונים לדוגמה של Kibana מקצה ערכי @timestamp עתידיים (עד 30 יום קדימה) ל-100 בקשות הסריקה והניצול מ-30.156.16.164, מה שמציב אותן מחוץ לחלון now-30d עד now שנשאל על ידי כללי הזיהוי של Elastic Security.
כדי להדגים את Attack Discovery באמצעות יומני הדוגמה, מעבירים את @timestamp של הרשומות של 30.156.16.164 30 ימים אחורה, מעשירים אותן בשדות source.ip ו-user.name של Elastic Common Schema (ECS), ויוצרים שני כללי זיהוי שיוצרים התראות אבטחה מהאירועים האלה:
מפה
clientipאלsource.ip, הגדרתuser.name(www-data) והזזת@timestampאחורה ב-30 ימים ברשומות30.156.16.164ב-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}'יוצרים שני כללי זיהוי של 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}'ממתינים כ-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" }
הפעלת Attack Discovery
- ב-Kibana, פותחים את תפריט הניווט הראשי ועוברים אל Security > Attack Discovery (או מנווטים ישירות אל
https://${KIBANA_IP}:5601/app/security/attack_discovery). - בתפריט המחברים שבחלק העליון של הדף, בוחרים את המחבר של ה-LLM (
${LLM_CONNECTOR_NAME}). - לוחצים על Generate כדי לנתח את ההתראות הפתוחות ב-
.alerts-security.alerts-default.
אפשר גם להפעיל ולבדוק את Attack Discovery מ-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"
}