chore
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<agent_config>
|
<agent_config>
|
||||||
|
|
||||||
<!-- ── Logs nginx (prod-uber et pre-prod-uber) ──────────────────── -->
|
<!-- ── Logs nginx ──────────────────────────────────────────────────── -->
|
||||||
<localfile>
|
<localfile>
|
||||||
<log_format>apache</log_format>
|
<log_format>apache</log_format>
|
||||||
<location>/var/log/nginx/access.log</location>
|
<location>/var/log/nginx/access.log</location>
|
||||||
@@ -14,14 +14,6 @@
|
|||||||
<location>/var/log/nginx/error.log</location>
|
<location>/var/log/nginx/error.log</location>
|
||||||
</localfile>
|
</localfile>
|
||||||
|
|
||||||
<!-- ── Logs Docker backend Go (stdout container) ─────────────────── -->
|
|
||||||
<!-- Le container s'appelle "backend" dans le compose prod -->
|
|
||||||
<localfile>
|
|
||||||
<log_format>syslog</log_format>
|
|
||||||
<location>/var/lib/docker/containers/*/*-json.log</location>
|
|
||||||
<label key="container">backend</label>
|
|
||||||
</localfile>
|
|
||||||
|
|
||||||
<!-- ── Auth system ───────────────────────────────────────────────── -->
|
<!-- ── Auth system ───────────────────────────────────────────────── -->
|
||||||
<localfile>
|
<localfile>
|
||||||
<log_format>syslog</log_format>
|
<log_format>syslog</log_format>
|
||||||
@@ -35,4 +27,4 @@
|
|||||||
</localfile>
|
</localfile>
|
||||||
|
|
||||||
</agent_config>
|
</agent_config>
|
||||||
</content>
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
<!-- Decoders API Backend Go (service systemd "backend") -->
|
|
||||||
|
|
||||||
<!-- Root : capture tous les logs du service systemd backend -->
|
|
||||||
<decoder name="go-backend">
|
|
||||||
<program_name>backend</program_name>
|
|
||||||
</decoder>
|
|
||||||
|
|
||||||
<!-- Child : logs HTTP Gin — "[GIN] | 200 | 1.234ms | 1.2.3.4 | GET /api/..." -->
|
|
||||||
<decoder name="go-backend-gin">
|
|
||||||
<parent>go-backend</parent>
|
|
||||||
<prematch>[GIN]</prematch>
|
|
||||||
<regex type="pcre2">\|\s+(\d{3})\s+\|\s+(\S+)\s+\|\s+([\d.]+)\s+\|\s+(\S+)\s+(\S+)</regex>
|
|
||||||
<order>id,extra_data,srcip,protocol,url</order>
|
|
||||||
</decoder>
|
|
||||||
|
|
||||||
<!-- Child : echecs auth login -->
|
|
||||||
<decoder name="go-backend-login-fail">
|
|
||||||
<parent>go-backend</parent>
|
|
||||||
<prematch>LOGIN_ADMIN|LOGIN_CLIENT</prematch>
|
|
||||||
<regex>LOGIN_\S+ \S+ \S+ \S+: (\S+)</regex>
|
|
||||||
<order>srcuser</order>
|
|
||||||
</decoder>
|
|
||||||
|
|
||||||
<!-- Fallback root (logs non-systemd, ex: pre-prod Docker) -->
|
|
||||||
<decoder name="go-backend-raw">
|
|
||||||
<prematch>LOGIN_ADMIN|LOGIN_CLIENT</prematch>
|
|
||||||
<regex>LOGIN_\S+ \S+ \S+ \S+: (\S+)</regex>
|
|
||||||
<order>srcuser</order>
|
|
||||||
</decoder>
|
|
||||||
@@ -1,56 +1,45 @@
|
|||||||
<!-- WireGuard Decoders — logs kernel via journald
|
<!-- WireGuard Decoders
|
||||||
Format: wireguard: wg0: <action> to/from peer N (IP:port) -->
|
Parent: kernel (built-in, program_name=^kernel)
|
||||||
|
Decoders enfants directs de kernel (Wazuh: 2 niveaux max) -->
|
||||||
|
|
||||||
<!-- Root : messages kernel WireGuard (syslog program_name=kernel) -->
|
|
||||||
<decoder name="wireguard">
|
|
||||||
<program_name>kernel</program_name>
|
|
||||||
<prematch>wireguard: wg0: </prematch>
|
|
||||||
</decoder>
|
|
||||||
|
|
||||||
<!-- Initiation handshake : VPN server → peer -->
|
|
||||||
<decoder name="wireguard-handshake-init">
|
<decoder name="wireguard-handshake-init">
|
||||||
<parent>wireguard</parent>
|
<parent>kernel</parent>
|
||||||
<prematch>Sending handshake initiation</prematch>
|
<prematch>wireguard: wg0: Sending handshake initiation</prematch>
|
||||||
<regex type="pcre2">wireguard: wg0: Sending handshake initiation to peer (\d+) \(([\d.]+):\d+\)</regex>
|
<regex type="pcre2">wireguard: wg0: Sending handshake initiation to peer (\d+) \(([\d.]+):\d+\)</regex>
|
||||||
<order>id,srcip</order>
|
<order>id,srcip</order>
|
||||||
</decoder>
|
</decoder>
|
||||||
|
|
||||||
<!-- Réponse handshake envoyée : VPN server → peer -->
|
|
||||||
<decoder name="wireguard-handshake-resp-sent">
|
<decoder name="wireguard-handshake-resp-sent">
|
||||||
<parent>wireguard</parent>
|
<parent>kernel</parent>
|
||||||
<prematch>Sending handshake response</prematch>
|
<prematch>wireguard: wg0: Sending handshake response</prematch>
|
||||||
<regex type="pcre2">wireguard: wg0: Sending handshake response to peer (\d+) \(([\d.]+):\d+\)</regex>
|
<regex type="pcre2">wireguard: wg0: Sending handshake response to peer (\d+) \(([\d.]+):\d+\)</regex>
|
||||||
<order>id,srcip</order>
|
<order>id,srcip</order>
|
||||||
</decoder>
|
</decoder>
|
||||||
|
|
||||||
<!-- Réponse handshake reçue : peer → VPN server -->
|
|
||||||
<decoder name="wireguard-handshake-resp-recv">
|
<decoder name="wireguard-handshake-resp-recv">
|
||||||
<parent>wireguard</parent>
|
<parent>kernel</parent>
|
||||||
<prematch>Receiving handshake response</prematch>
|
<prematch>wireguard: wg0: Receiving handshake response</prematch>
|
||||||
<regex type="pcre2">wireguard: wg0: Receiving handshake response from peer (\d+) \(([\d.]+):\d+\)</regex>
|
<regex type="pcre2">wireguard: wg0: Receiving handshake response from peer (\d+) \(([\d.]+):\d+\)</regex>
|
||||||
<order>id,srcip</order>
|
<order>id,srcip</order>
|
||||||
</decoder>
|
</decoder>
|
||||||
|
|
||||||
<!-- Keepalive (niveau bas, pas d'alerte) -->
|
|
||||||
<decoder name="wireguard-keepalive">
|
<decoder name="wireguard-keepalive">
|
||||||
<parent>wireguard</parent>
|
<parent>kernel</parent>
|
||||||
<prematch>keepalive packet</prematch>
|
<prematch>wireguard: wg0: </prematch>
|
||||||
<regex type="pcre2">wireguard: wg0: \S+ keepalive packet \S+ peer (\d+) \(([\d.]+):\d+\)</regex>
|
<regex type="pcre2">wireguard: wg0: \S+ keepalive packet \S+ peer (\d+) \(([\d.]+):\d+\)</regex>
|
||||||
<order>id,srcip</order>
|
<order>id,srcip</order>
|
||||||
</decoder>
|
</decoder>
|
||||||
|
|
||||||
<!-- Handshake timeout — "did not complete after 5 seconds, retrying (try N)" -->
|
|
||||||
<decoder name="wireguard-timeout">
|
<decoder name="wireguard-timeout">
|
||||||
<parent>wireguard</parent>
|
<parent>kernel</parent>
|
||||||
<prematch>did not complete</prematch>
|
<prematch>wireguard: wg0: Handshake for peer</prematch>
|
||||||
<regex type="pcre2">wireguard: wg0: Handshake for peer (\d+)[^(]+\(([\d.]+):\d+\).+retrying \(try (\d+)\)</regex>
|
<regex type="pcre2">wireguard: wg0: Handshake for peer (\d+)[^(]+\(([\d.]+):\d+\).+retrying \(try (\d+)\)</regex>
|
||||||
<order>id,srcip,extra_data</order>
|
<order>id,srcip,extra_data</order>
|
||||||
</decoder>
|
</decoder>
|
||||||
|
|
||||||
<!-- Peer roaming (changement d'IP source) -->
|
|
||||||
<decoder name="wireguard-roaming">
|
<decoder name="wireguard-roaming">
|
||||||
<parent>wireguard</parent>
|
<parent>kernel</parent>
|
||||||
<prematch>is now roaming</prematch>
|
<prematch>wireguard: wg0: Peer </prematch>
|
||||||
<regex type="pcre2">wireguard: wg0: Peer (\d+).+roaming to ([\d.]+):\d+</regex>
|
<regex type="pcre2">wireguard: wg0: Peer (\d+).+roaming to ([\d.]+):\d+</regex>
|
||||||
<order>id,srcip</order>
|
<order>id,srcip</order>
|
||||||
</decoder>
|
</decoder>
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
<!-- Regles API Backend Go — IDs 100600-100720 -->
|
|
||||||
|
|
||||||
<!-- ── Logs backend systemd ───────────────────────────────────────────── -->
|
|
||||||
<group name="api_backend,">
|
|
||||||
|
|
||||||
<!-- Catch-all : toute ligne du service backend (niveau 3 = indexe dans OpenSearch) -->
|
|
||||||
<rule id="100700" level="3">
|
|
||||||
<decoded_as>go-backend</decoded_as>
|
|
||||||
<description>Go Backend API: log entry</description>
|
|
||||||
<group>api_backend,</group>
|
|
||||||
</rule>
|
|
||||||
|
|
||||||
<!-- Log HTTP Gin : toute requete -->
|
|
||||||
<rule id="100701" level="3">
|
|
||||||
<if_sid>100700</if_sid>
|
|
||||||
<match>[GIN]</match>
|
|
||||||
<description>API: Requete HTTP Gin loggee</description>
|
|
||||||
<group>api_backend,api_http,</group>
|
|
||||||
</rule>
|
|
||||||
|
|
||||||
<!-- Log HTTP Gin : erreur client 4xx — pattern "| 4NN |" -->
|
|
||||||
<rule id="100702" level="5">
|
|
||||||
<if_sid>100701</if_sid>
|
|
||||||
<regex>| 4\d\d |</regex>
|
|
||||||
<description>API: Erreur client 4xx — $(srcip)</description>
|
|
||||||
<group>api_backend,api_http,api_error,</group>
|
|
||||||
</rule>
|
|
||||||
|
|
||||||
<!-- Log HTTP Gin : erreur serveur 5xx -->
|
|
||||||
<rule id="100703" level="8">
|
|
||||||
<if_sid>100701</if_sid>
|
|
||||||
<regex>| 5\d\d |</regex>
|
|
||||||
<description>API: Erreur serveur 5xx — $(srcip)</description>
|
|
||||||
<group>api_backend,api_http,api_error,</group>
|
|
||||||
</rule>
|
|
||||||
|
|
||||||
<!-- Erreur Go (panic, fatal, error) -->
|
|
||||||
<rule id="100704" level="9">
|
|
||||||
<if_sid>100700</if_sid>
|
|
||||||
<match>panic|PANIC|fatal|FATAL</match>
|
|
||||||
<description>API: Erreur critique Go backend (panic/fatal)</description>
|
|
||||||
<group>api_backend,api_error,</group>
|
|
||||||
</rule>
|
|
||||||
|
|
||||||
</group>
|
|
||||||
|
|
||||||
<!-- ── Auth failures via logs Go backend ────────────────────────────── -->
|
|
||||||
<group name="web,authentication_failures,api,">
|
|
||||||
|
|
||||||
<!-- Echec login admin (log Go direct) -->
|
|
||||||
<rule id="100600" level="5">
|
|
||||||
<decoded_as>go-backend-login-fail</decoded_as>
|
|
||||||
<match>LOGIN_ADMIN</match>
|
|
||||||
<description>API: Echec auth admin</description>
|
|
||||||
<group>api_auth_failure,</group>
|
|
||||||
</rule>
|
|
||||||
|
|
||||||
<!-- Echec login client (log Go direct) -->
|
|
||||||
<rule id="100601" level="5">
|
|
||||||
<decoded_as>go-backend-login-fail</decoded_as>
|
|
||||||
<match>LOGIN_CLIENT</match>
|
|
||||||
<description>API: Echec auth client</description>
|
|
||||||
<group>api_auth_failure,</group>
|
|
||||||
</rule>
|
|
||||||
|
|
||||||
<!-- Brute-force login admin : 5 echecs en 60s -->
|
|
||||||
<rule id="100610" level="10" frequency="5" timeframe="60">
|
|
||||||
<if_matched_sid>100600</if_matched_sid>
|
|
||||||
<same_source_ip />
|
|
||||||
<description>API: Brute-force login admin — 5 echecs/60s depuis $(srcip)</description>
|
|
||||||
<group>api_brute_force,authentication_failures,</group>
|
|
||||||
</rule>
|
|
||||||
|
|
||||||
<!-- Brute-force login client : 5 echecs en 60s -->
|
|
||||||
<rule id="100611" level="10" frequency="5" timeframe="60">
|
|
||||||
<if_matched_sid>100601</if_matched_sid>
|
|
||||||
<same_source_ip />
|
|
||||||
<description>API: Brute-force login client — 5 echecs/60s depuis $(srcip)</description>
|
|
||||||
<group>api_brute_force,authentication_failures,</group>
|
|
||||||
</rule>
|
|
||||||
|
|
||||||
<!-- Scan auth : 20 echecs en 120s toutes routes -->
|
|
||||||
<rule id="100612" level="12" frequency="20" timeframe="120">
|
|
||||||
<if_matched_group>api_auth_failure</if_matched_group>
|
|
||||||
<same_source_ip />
|
|
||||||
<description>API: Scan auth — 20 echecs/120s depuis $(srcip)</description>
|
|
||||||
<group>api_brute_force,authentication_failures,</group>
|
|
||||||
</rule>
|
|
||||||
|
|
||||||
</group>
|
|
||||||
@@ -1,69 +1,81 @@
|
|||||||
<!-- Règles WireGuard VPN — IDs 100800-100820 -->
|
<!-- Règles WireGuard VPN — IDs 100800-100816
|
||||||
|
Les logs kernel passent par rule 5100 (program_name=^kernel).
|
||||||
|
On hérite via if_sid 5100 et on filtre par match/regex. -->
|
||||||
|
|
||||||
<group name="wireguard,vpn,">
|
<group name="wireguard,vpn,">
|
||||||
|
|
||||||
<!-- Keepalive : niveau 0 = pas d'index dans OpenSearch -->
|
<!-- Keepalive : level 0 = pas d'index OpenSearch -->
|
||||||
<rule id="100800" level="0">
|
<rule id="100800" level="0">
|
||||||
<decoded_as>wireguard-keepalive</decoded_as>
|
<if_sid>5100</if_sid>
|
||||||
|
<match>wireguard: wg0: </match>
|
||||||
|
<regex>keepalive packet</regex>
|
||||||
<description>WireGuard: keepalive peer $(srcip)</description>
|
<description>WireGuard: keepalive peer $(srcip)</description>
|
||||||
<group>wireguard,vpn_keepalive,</group>
|
<group>wireguard,vpn_keepalive,</group>
|
||||||
</rule>
|
</rule>
|
||||||
|
|
||||||
<!-- Handshake initiation (VPN server → peer) -->
|
<!-- Handshake initiation -->
|
||||||
<rule id="100801" level="3">
|
<rule id="100801" level="3">
|
||||||
<decoded_as>wireguard-handshake-init</decoded_as>
|
<if_sid>5100</if_sid>
|
||||||
|
<match>wireguard: wg0: Sending handshake initiation</match>
|
||||||
<description>WireGuard: handshake initiation vers peer $(srcip)</description>
|
<description>WireGuard: handshake initiation vers peer $(srcip)</description>
|
||||||
<group>wireguard,vpn_handshake,</group>
|
<group>wireguard,vpn_handshake,</group>
|
||||||
</rule>
|
</rule>
|
||||||
|
|
||||||
<!-- Handshake réponse envoyée (VPN server → peer) -->
|
<!-- Handshake réponse envoyée -->
|
||||||
<rule id="100802" level="3">
|
<rule id="100802" level="3">
|
||||||
<decoded_as>wireguard-handshake-resp-sent</decoded_as>
|
<if_sid>5100</if_sid>
|
||||||
|
<match>wireguard: wg0: Sending handshake response</match>
|
||||||
<description>WireGuard: handshake reponse envoyee a peer $(srcip)</description>
|
<description>WireGuard: handshake reponse envoyee a peer $(srcip)</description>
|
||||||
<group>wireguard,vpn_handshake,</group>
|
<group>wireguard,vpn_handshake,</group>
|
||||||
</rule>
|
</rule>
|
||||||
|
|
||||||
<!-- Handshake réponse reçue (peer → VPN server) -->
|
<!-- Handshake réponse reçue (session établie) -->
|
||||||
<rule id="100803" level="3">
|
<rule id="100803" level="3">
|
||||||
<decoded_as>wireguard-handshake-resp-recv</decoded_as>
|
<if_sid>5100</if_sid>
|
||||||
<description>WireGuard: handshake complete avec peer $(srcip)</description>
|
<match>wireguard: wg0: Receiving handshake response</match>
|
||||||
|
<description>WireGuard: handshake complet avec peer $(srcip)</description>
|
||||||
<group>wireguard,vpn_handshake,</group>
|
<group>wireguard,vpn_handshake,</group>
|
||||||
</rule>
|
</rule>
|
||||||
|
|
||||||
<!-- Timeout handshake (premier retry) -->
|
<!-- Handshake timeout -->
|
||||||
<rule id="100810" level="7">
|
<rule id="100810" level="7">
|
||||||
<decoded_as>wireguard-timeout</decoded_as>
|
<if_sid>5100</if_sid>
|
||||||
<description>WireGuard: handshake timeout peer $(srcip) (essai $(extra_data))</description>
|
<match>wireguard: wg0: Handshake for peer</match>
|
||||||
|
<regex>did not complete</regex>
|
||||||
|
<description>WireGuard: handshake timeout peer $(srcip)</description>
|
||||||
<group>wireguard,vpn_warning,</group>
|
<group>wireguard,vpn_warning,</group>
|
||||||
</rule>
|
</rule>
|
||||||
|
|
||||||
<!-- Timeout répété — peer probablement déconnecté (try >= 5) -->
|
<!-- Timeout répété >= 5 -->
|
||||||
<rule id="100811" level="10">
|
<rule id="100811" level="10">
|
||||||
<if_sid>100810</if_sid>
|
<if_sid>100810</if_sid>
|
||||||
<match>try 5|try 6|try 7|try 8|try 9</match>
|
<match>try 5|try 6|try 7|try 8|try 9</match>
|
||||||
<description>WireGuard: peer $(srcip) deconnecte — handshake echoue 5 fois consecutives</description>
|
<description>WireGuard: peer $(srcip) deconnecte</description>
|
||||||
<group>wireguard,vpn_disconnected,</group>
|
<group>wireguard,vpn_disconnected,</group>
|
||||||
</rule>
|
</rule>
|
||||||
|
|
||||||
<!-- Timeout grave — try 10+ -->
|
<!-- Timeout grave >= 10 -->
|
||||||
<rule id="100812" level="12">
|
<rule id="100812" level="12">
|
||||||
<if_sid>100810</if_sid>
|
<if_sid>100810</if_sid>
|
||||||
<regex>retrying \(try 1\d\)</regex>
|
<regex>retrying .try 1\d.</regex>
|
||||||
<description>WireGuard: peer $(srcip) hors ligne depuis plus de 60 secondes</description>
|
<description>WireGuard: peer $(srcip) hors ligne depuis plus de 60s</description>
|
||||||
<group>wireguard,vpn_disconnected,</group>
|
<group>wireguard,vpn_disconnected,</group>
|
||||||
</rule>
|
</rule>
|
||||||
|
|
||||||
<!-- Peer roaming (changement d'IP publique) -->
|
<!-- Peer roaming -->
|
||||||
<rule id="100815" level="5">
|
<rule id="100815" level="5">
|
||||||
<decoded_as>wireguard-roaming</decoded_as>
|
<if_sid>5100</if_sid>
|
||||||
<description>WireGuard: peer $(id) a change d'IP (roaming) vers $(srcip)</description>
|
<match>wireguard: wg0: Peer </match>
|
||||||
|
<regex>is now roaming</regex>
|
||||||
|
<description>WireGuard: peer $(id) roaming vers $(srcip)</description>
|
||||||
<group>wireguard,vpn_roaming,</group>
|
<group>wireguard,vpn_roaming,</group>
|
||||||
</rule>
|
</rule>
|
||||||
|
|
||||||
<!-- Peer reconnecté (handshake complet) -->
|
<!-- Reconnexion après déconnexion -->
|
||||||
<rule id="100816" level="6">
|
<rule id="100816" level="6">
|
||||||
<decoded_as>wireguard-handshake-resp-recv</decoded_as>
|
<if_sid>5100</if_sid>
|
||||||
<description>WireGuard: peer $(srcip) handshake complet (reconnexion possible)</description>
|
<match>wireguard: wg0: Receiving handshake response</match>
|
||||||
|
<description>WireGuard: peer $(srcip) reconnecte</description>
|
||||||
<group>wireguard,vpn_reconnected,</group>
|
<group>wireguard,vpn_reconnected,</group>
|
||||||
</rule>
|
</rule>
|
||||||
|
|
||||||
|
|||||||
@@ -1,417 +0,0 @@
|
|||||||
#!/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()
|
|
||||||
Reference in New Issue
Block a user