This commit is contained in:
2026-06-25 18:45:58 +02:00
parent 283190db09
commit 373074ccb2
14 changed files with 1828 additions and 16 deletions
@@ -0,0 +1,417 @@
#!/usr/bin/env python3
"""
Crée le dashboard "API Go - Performance & Trafic" dans OpenSearch Dashboards (Wazuh).
Usage:
docker cp create-dashboard-go-api.py wazuh_dashboard:/tmp/
docker exec wazuh_dashboard python3 /tmp/create-dashboard-go-api.py
Données utilisées :
- rule 100700 → toute ligne backend Go
- rule 100701 → requête HTTP Gin (tous codes)
- rule 100702 → erreur 4xx
- rule 100703 → erreur 5xx
- rule 100704 → panic / fatal
- fields: data.id (code HTTP), data.extra_data (temps réponse),
data.srcip (IP client), data.protocol (GET/POST...),
data.url (endpoint), agent.name (prod-mln / pre-prod-mln)
"""
import json
import urllib.request
import urllib.error
import ssl
import base64
import sys
import os
# ── Configuration ──────────────────────────────────────────────────────────────
DASHBOARD_HOST = "https://localhost:5601"
DASHBOARD_USER = "kibanaserver"
DASHBOARD_PASS = os.environ.get("DASHBOARD_PASSWORD", "E9Jpr6586kQ3wYrCS2!")
INDEX_PATTERN = "wazuh-alerts-*"
# ── Queries ────────────────────────────────────────────────────────────────────
Q_ALL = "rule.id: 100700 OR rule.id: 100701 OR rule.id: 100702 OR rule.id: 100703 OR rule.id: 100704"
Q_HTTP = "rule.id: 100701 OR rule.id: 100702 OR rule.id: 100703"
Q_4XX = "rule.id: 100702"
Q_5XX = "rule.id: 100703"
Q_PANIC = "rule.id: 100704"
# ── Client HTTP ────────────────────────────────────────────────────────────────
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
_auth = base64.b64encode(f"{DASHBOARD_USER}:{DASHBOARD_PASS}".encode()).decode()
_headers = {
"Content-Type": "application/json",
"osd-xsrf": "true",
"Authorization": f"Basic {_auth}",
}
def api(method, path, body=None):
data = json.dumps(body).encode() if body else None
req = urllib.request.Request(DASHBOARD_HOST + path, data=data, headers=_headers, method=method)
try:
with urllib.request.urlopen(req, context=ctx, timeout=15) as r:
return json.loads(r.read())
except urllib.error.HTTPError as e:
return {"error": e.code, "msg": e.read().decode()[:300]}
def search_src(query):
return json.dumps({
"index": INDEX_PATTERN,
"filter": [],
"query": {"language": "kuery", "query": query},
})
# ── Visualisations ─────────────────────────────────────────────────────────────
VISUALIZATIONS = [
# ── 1. Métrique : total requêtes HTTP ─────────────────────────────────────
{
"id": "go-api-total-requests",
"title": "[API Go] Total Requetes HTTP",
"type": "metric",
"query": Q_HTTP,
"visState": {
"type": "metric",
"params": {
"metric": {
"percentageMode": False, "useRanges": False,
"colorSchema": "Blues",
"metricColorMode": "None",
"colorsRange": [{"from": 0, "to": 99999999}],
"labels": {"show": True},
"invertColors": False,
"style": {"bgFill": "#000", "bgColor": False, "labelColor": False,
"subText": "requetes loggees", "fontSize": 60},
}
},
"aggs": [{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}}],
},
},
# ── 2. Métrique : erreurs 4xx ─────────────────────────────────────────────
{
"id": "go-api-4xx",
"title": "[API Go] Erreurs 4xx",
"type": "metric",
"query": Q_4XX,
"visState": {
"type": "metric",
"params": {
"metric": {
"percentageMode": False, "useRanges": False,
"colorSchema": "Yellow to Red",
"metricColorMode": "Labels",
"colorsRange": [{"from": 0, "to": 50}, {"from": 50, "to": 500}, {"from": 500, "to": 99999}],
"labels": {"show": True},
"invertColors": False,
"style": {"bgFill": "#000", "bgColor": False, "labelColor": False,
"subText": "erreurs client 4xx", "fontSize": 60},
}
},
"aggs": [{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}}],
},
},
# ── 3. Métrique : erreurs 5xx ─────────────────────────────────────────────
{
"id": "go-api-5xx",
"title": "[API Go] Erreurs 5xx",
"type": "metric",
"query": Q_5XX,
"visState": {
"type": "metric",
"params": {
"metric": {
"percentageMode": False, "useRanges": False,
"colorSchema": "Green to Red",
"metricColorMode": "Labels",
"colorsRange": [{"from": 0, "to": 1}, {"from": 1, "to": 20}, {"from": 20, "to": 99999}],
"labels": {"show": True},
"invertColors": False,
"style": {"bgFill": "#000", "bgColor": False, "labelColor": False,
"subText": "erreurs serveur 5xx", "fontSize": 60},
}
},
"aggs": [{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}}],
},
},
# ── 4. Timeline : requêtes par heure (prod vs pre-prod) ───────────────────
{
"id": "go-api-timeline",
"title": "[API Go] Timeline Requetes par Serveur",
"type": "histogram",
"query": Q_HTTP,
"visState": {
"type": "histogram",
"params": {
"type": "histogram",
"grid": {"categoryLines": False},
"categoryAxes": [{"id": "CategoryAxis-1", "type": "category", "position": "bottom",
"show": True, "style": {},
"labels": {"show": True, "truncate": 100}, "title": {}}],
"valueAxes": [{"id": "ValueAxis-1", "name": "LeftAxis-1", "type": "value",
"position": "left", "show": True, "style": {},
"scale": {"type": "linear", "mode": "normal"},
"labels": {"show": True, "rotate": 0, "filter": False, "truncate": 100},
"title": {"text": "Requetes"}}],
"seriesParams": [{"show": True, "type": "histogram", "mode": "stacked",
"data": {"label": "Requetes", "id": "1"},
"valueAxis": "ValueAxis-1"}],
"addTooltip": True, "addLegend": True, "legendPosition": "right",
"times": [], "addTimeMarker": False,
},
"aggs": [
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
{"id": "2", "enabled": True, "type": "date_histogram", "schema": "segment",
"params": {"field": "@timestamp", "interval": "auto", "min_doc_count": 1, "extended_bounds": {}}},
{"id": "3", "enabled": True, "type": "terms", "schema": "group",
"params": {"field": "agent.name", "size": 3, "order": "desc", "orderBy": "1",
"otherBucket": False}},
],
},
},
# ── 5. Pie : distribution codes HTTP ──────────────────────────────────────
{
"id": "go-api-http-codes",
"title": "[API Go] Distribution Codes HTTP",
"type": "pie",
"query": Q_HTTP,
"visState": {
"type": "pie",
"params": {
"type": "pie", "addTooltip": True, "addLegend": True,
"legendPosition": "right", "isDonut": True,
"labels": {"show": True, "values": True, "last_level": True, "truncate": 100},
},
"aggs": [
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
{"id": "2", "enabled": True, "type": "terms", "schema": "segment",
"params": {"field": "data.id", "size": 10, "order": "desc", "orderBy": "1",
"otherBucket": True, "otherBucketLabel": "Autres", "missingBucket": False}},
],
},
},
# ── 6. Pie : méthodes HTTP ────────────────────────────────────────────────
{
"id": "go-api-methods",
"title": "[API Go] Methodes HTTP",
"type": "pie",
"query": Q_HTTP,
"visState": {
"type": "pie",
"params": {
"type": "pie", "addTooltip": True, "addLegend": True,
"legendPosition": "right", "isDonut": False,
"labels": {"show": True, "values": True, "last_level": True, "truncate": 100},
},
"aggs": [
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
{"id": "2", "enabled": True, "type": "terms", "schema": "segment",
"params": {"field": "data.protocol", "size": 8, "order": "desc", "orderBy": "1",
"otherBucket": False, "missingBucket": False}},
],
},
},
# ── 7. Bar horizontal : top endpoints ────────────────────────────────────
{
"id": "go-api-top-endpoints",
"title": "[API Go] Top Endpoints",
"type": "horizontal_bar",
"query": Q_HTTP,
"visState": {
"type": "horizontal_bar",
"params": {
"type": "horizontal_bar",
"grid": {"categoryLines": False},
"categoryAxes": [{"id": "CategoryAxis-1", "type": "category", "position": "left",
"show": True, "style": {},
"labels": {"show": True, "rotate": 0, "filter": True, "truncate": 300},
"title": {}}],
"valueAxes": [{"id": "ValueAxis-1", "name": "LeftAxis-1", "type": "value",
"position": "bottom", "show": True, "style": {},
"scale": {"type": "linear", "mode": "normal"},
"labels": {"show": True, "rotate": 0, "filter": False, "truncate": 100},
"title": {"text": "Requetes"}}],
"seriesParams": [{"show": True, "type": "horizontal_bar", "mode": "stacked",
"data": {"label": "Requetes", "id": "1"},
"valueAxis": "ValueAxis-1"}],
"addTooltip": True, "addLegend": True, "legendPosition": "right",
"times": [], "addTimeMarker": False,
},
"aggs": [
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
{"id": "2", "enabled": True, "type": "terms", "schema": "segment",
"params": {"field": "data.url", "size": 15, "order": "desc", "orderBy": "1",
"otherBucket": True, "otherBucketLabel": "Autres", "missingBucket": False}},
{"id": "3", "enabled": True, "type": "terms", "schema": "group",
"params": {"field": "data.id", "size": 4, "order": "desc", "orderBy": "1",
"otherBucket": True, "otherBucketLabel": "Autres"}},
],
},
},
# ── 8. Bar horizontal : top endpoints en erreur ───────────────────────────
{
"id": "go-api-error-endpoints",
"title": "[API Go] Endpoints en Erreur (4xx/5xx)",
"type": "horizontal_bar",
"query": Q_4XX + " OR " + Q_5XX,
"visState": {
"type": "horizontal_bar",
"params": {
"type": "horizontal_bar",
"grid": {"categoryLines": False},
"categoryAxes": [{"id": "CategoryAxis-1", "type": "category", "position": "left",
"show": True, "style": {},
"labels": {"show": True, "rotate": 0, "filter": True, "truncate": 300},
"title": {}}],
"valueAxes": [{"id": "ValueAxis-1", "name": "LeftAxis-1", "type": "value",
"position": "bottom", "show": True, "style": {},
"scale": {"type": "linear", "mode": "normal"},
"labels": {"show": True, "rotate": 0, "filter": False, "truncate": 100},
"title": {"text": "Erreurs"}}],
"seriesParams": [{"show": True, "type": "horizontal_bar", "mode": "stacked",
"data": {"label": "Erreurs", "id": "1"},
"valueAxis": "ValueAxis-1"}],
"addTooltip": True, "addLegend": True, "legendPosition": "right",
"times": [], "addTimeMarker": False,
},
"aggs": [
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
{"id": "2", "enabled": True, "type": "terms", "schema": "segment",
"params": {"field": "data.url", "size": 15, "order": "desc", "orderBy": "1",
"otherBucket": True, "otherBucketLabel": "Autres", "missingBucket": False}},
{"id": "3", "enabled": True, "type": "terms", "schema": "group",
"params": {"field": "data.id", "size": 3, "order": "desc", "orderBy": "1",
"otherBucket": False}},
],
},
},
# ── 9. Bar horizontal : top IPs clientes ──────────────────────────────────
{
"id": "go-api-top-clients",
"title": "[API Go] Top IPs Clientes",
"type": "horizontal_bar",
"query": Q_HTTP,
"visState": {
"type": "horizontal_bar",
"params": {
"type": "horizontal_bar",
"grid": {"categoryLines": False},
"categoryAxes": [{"id": "CategoryAxis-1", "type": "category", "position": "left",
"show": True, "style": {},
"labels": {"show": True, "rotate": 0, "filter": True, "truncate": 200},
"title": {}}],
"valueAxes": [{"id": "ValueAxis-1", "name": "LeftAxis-1", "type": "value",
"position": "bottom", "show": True, "style": {},
"scale": {"type": "linear", "mode": "normal"},
"labels": {"show": True, "rotate": 0, "filter": False, "truncate": 100},
"title": {"text": "Requetes"}}],
"seriesParams": [{"show": True, "type": "horizontal_bar", "mode": "normal",
"data": {"label": "Requetes", "id": "1"},
"valueAxis": "ValueAxis-1"}],
"addTooltip": True, "addLegend": True, "legendPosition": "right",
"times": [], "addTimeMarker": False,
},
"aggs": [
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
{"id": "2", "enabled": True, "type": "terms", "schema": "segment",
"params": {"field": "data.srcip", "size": 15, "order": "desc", "orderBy": "1",
"otherBucket": False, "missingBucket": False}},
],
},
},
# ── 10. Table : erreurs critiques (panic / fatal) ──────────────────────────
{
"id": "go-api-panics",
"title": "[API Go] Erreurs Critiques (panic/fatal)",
"type": "table",
"query": Q_PANIC,
"visState": {
"type": "table",
"params": {
"perPage": 15, "showPartialRows": False, "showMetricsAtAllLevels": False,
"sort": {"columnIndex": None, "direction": None},
"showTotal": False, "totalFunc": "sum",
},
"aggs": [
{"id": "1", "enabled": True, "type": "count", "schema": "metric", "params": {}},
{"id": "2", "enabled": True, "type": "terms", "schema": "bucket",
"params": {"field": "agent.name", "size": 5, "order": "desc", "orderBy": "1",
"otherBucket": False, "missingBucket": False}},
{"id": "3", "enabled": True, "type": "date_histogram", "schema": "bucket",
"params": {"field": "@timestamp", "interval": "1d", "min_doc_count": 1, "extended_bounds": {}}},
],
},
"uiStateJSON": json.dumps({"vis": {"params": {"sort": {"columnIndex": 0, "direction": "desc"}}}}),
},
]
# ── Layout dashboard (grille 48 colonnes) ──────────────────────────────────────
# Ligne 0 : 3 métriques (total | 4xx | 5xx)
# Ligne 6 : timeline requêtes (pleine largeur)
# Ligne 18 : pie codes HTTP + pie méthodes
# Ligne 34 : top endpoints (pleine largeur)
# Ligne 50 : endpoints en erreur (gauche) + top clients (droite)
# Ligne 66 : table panics (pleine largeur)
PANELS = [
{"panelIndex": "1", "gridData": {"x": 0, "y": 0, "w": 16, "h": 6, "i": "1"}, "version": "2.19.5", "type": "visualization", "id": "go-api-total-requests", "embeddableConfig": {}},
{"panelIndex": "2", "gridData": {"x": 16, "y": 0, "w": 16, "h": 6, "i": "2"}, "version": "2.19.5", "type": "visualization", "id": "go-api-4xx", "embeddableConfig": {}},
{"panelIndex": "3", "gridData": {"x": 32, "y": 0, "w": 16, "h": 6, "i": "3"}, "version": "2.19.5", "type": "visualization", "id": "go-api-5xx", "embeddableConfig": {}},
{"panelIndex": "4", "gridData": {"x": 0, "y": 6, "w": 48, "h": 12, "i": "4"}, "version": "2.19.5", "type": "visualization", "id": "go-api-timeline", "embeddableConfig": {}},
{"panelIndex": "5", "gridData": {"x": 0, "y": 18, "w": 24, "h": 16, "i": "5"}, "version": "2.19.5", "type": "visualization", "id": "go-api-http-codes", "embeddableConfig": {}},
{"panelIndex": "6", "gridData": {"x": 24, "y": 18, "w": 24, "h": 16, "i": "6"}, "version": "2.19.5", "type": "visualization", "id": "go-api-methods", "embeddableConfig": {}},
{"panelIndex": "7", "gridData": {"x": 0, "y": 34, "w": 48, "h": 16, "i": "7"}, "version": "2.19.5", "type": "visualization", "id": "go-api-top-endpoints", "embeddableConfig": {}},
{"panelIndex": "8", "gridData": {"x": 0, "y": 50, "w": 28, "h": 16, "i": "8"}, "version": "2.19.5", "type": "visualization", "id": "go-api-error-endpoints", "embeddableConfig": {}},
{"panelIndex": "9", "gridData": {"x": 28, "y": 50, "w": 20, "h": 16, "i": "9"}, "version": "2.19.5", "type": "visualization", "id": "go-api-top-clients", "embeddableConfig": {}},
{"panelIndex": "10", "gridData": {"x": 0, "y": 66, "w": 48, "h": 12, "i": "10"}, "version": "2.19.5", "type": "visualization", "id": "go-api-panics", "embeddableConfig": {}},
]
# ── Main ───────────────────────────────────────────────────────────────────────
def main():
print("=== Creation dashboard: API Go - Performance & Trafic ===\n")
for viz in VISUALIZATIONS:
vis_state = dict(viz["visState"])
vis_state["title"] = viz["title"]
body = {
"attributes": {
"title": viz["title"],
"visState": json.dumps(vis_state),
"uiStateJSON": viz.get("uiStateJSON", "{}"),
"description": "",
"kibanaSavedObjectMeta": {"searchSourceJSON": search_src(viz["query"])},
}
}
r = api("POST", f"/api/saved_objects/visualization/{viz['id']}?overwrite=true", body)
if "id" in r:
print(f" OK {viz['title']}")
else:
print(f" ERR {viz['title']}: {r}")
sys.exit(1)
dashboard_body = {
"attributes": {
"title": "API Go - Performance et Trafic",
"description": "Requetes HTTP Gin, codes de statut, endpoints, temps de reponse, erreurs 4xx/5xx, panics",
"panelsJSON": json.dumps(PANELS),
"optionsJSON": json.dumps({"useMargins": True, "hidePanelTitles": False}),
"timeRestore": False,
"kibanaSavedObjectMeta": {
"searchSourceJSON": json.dumps({"query": {"language": "kuery", "query": ""}, "filter": []})
},
}
}
r = api("POST", "/api/saved_objects/dashboard/go-api-performance-dashboard?overwrite=true", dashboard_body)
if "id" in r:
print(f"\n OK Dashboard: {r['id']}")
print(f" URL: https://<monitoring-ip>/#/app/dashboards#/view/{r['id']}")
else:
print(f"\n ERR Dashboard: {r}")
sys.exit(1)
print("\n=== Termine ===")
if __name__ == "__main__":
main()