first sync
This commit is contained in:
352
README.md
352
README.md
@@ -1,3 +1,351 @@
|
||||
# SIEM---Wazuh
|
||||
# Plugin GLPI SIEM-Wazuh
|
||||
|
||||
Plugin GLPI pour intéger Wazuh dans GLPI
|
||||
[](https://github.com/siem-wazuh/glpi-plugin)
|
||||
[](https://glpi-project.org/)
|
||||
[](LICENSE)
|
||||
|
||||
## Description
|
||||
|
||||
Le plugin SIEM-Wazuh permet l'intégration complète entre Wazuh (solution SIEM open source) et GLPI (système de gestion des services IT). Il permet de :
|
||||
|
||||
- **Surveiller** automatiquement les serveurs Wazuh configurés
|
||||
- **Récupérer** les alertes de sécurité en temps réel
|
||||
- **Associer** automatiquement les alertes aux assets de l'inventaire GLPI
|
||||
- **Créer** automatiquement des tickets pour les incidents critiques
|
||||
- **Gérer** centralement la configuration depuis l'interface GLPI
|
||||
|
||||
## Fonctionnalités
|
||||
|
||||
### 🛡️ Surveillance Proactive
|
||||
- Synchronisation automatique des alertes Wazuh
|
||||
- Configuration de multiples serveurs Wazuh
|
||||
- Filtrage par niveau de règle et sévérité
|
||||
- Intervalle de synchronisation configurable
|
||||
|
||||
### 🔗 Intégration Assets
|
||||
- Association automatique alertes ↔ ordinateurs
|
||||
- Association automatique alertes ↔ équipements réseau
|
||||
- Correspondance par nom d'hôte et adresse IP
|
||||
- Onglets dédiés sur chaque asset
|
||||
|
||||
### 🎫 Gestion des Tickets
|
||||
- Création automatique de tickets GLPI
|
||||
- Configuration des types et catégories
|
||||
- Priorité automatique basée sur la sévérité
|
||||
- Assignation automatique configurables
|
||||
|
||||
### 🌍 Interface Multilingue
|
||||
Support complet pour :
|
||||
- 🇫🇷 Français
|
||||
- 🇬🇧 Anglais
|
||||
- 🇩🇪 Allemand
|
||||
- 🇪🇸 Espagnol
|
||||
- 🇮🇹 Italien
|
||||
- 🇵🇹 Portugais
|
||||
- 🇵🇱 Polonais
|
||||
|
||||
### ⚙️ Configuration Avancée
|
||||
- Interface de configuration intuitive
|
||||
- Gestion granulaire des droits utilisateur
|
||||
- Logs de débogage détaillés
|
||||
- Statistiques et tableaux de bord
|
||||
|
||||
## Prérequis
|
||||
|
||||
### Système
|
||||
- **GLPI** : Version 10.0.0 minimum
|
||||
- **PHP** : Version 7.4 minimum
|
||||
- **Extensions PHP** : curl, json, mbstring, openssl
|
||||
- **Base de données** : MySQL 5.7+ ou MariaDB 10.2+
|
||||
|
||||
### Wazuh
|
||||
- **Wazuh Manager** : Version 4.0+ recommandée
|
||||
- **API Wazuh** : Accès avec authentification
|
||||
- **Wazuh Indexer** : Optionnel (pour requêtes avancées)
|
||||
|
||||
## Installation
|
||||
|
||||
### 1. Téléchargement
|
||||
|
||||
```bash
|
||||
cd /path/to/glpi/plugins/
|
||||
wget https://github.com/siem-wazuh/glpi-plugin/releases/download/1.0.0/siem-wazuh-1.0.0.tar.gz
|
||||
tar -xzf siem-wazuh-1.0.0.tar.gz
|
||||
```
|
||||
|
||||
### 2. Installation via Interface GLPI
|
||||
|
||||
1. Se connecter en tant qu'administrateur GLPI
|
||||
2. Aller dans **Configuration > Plugins**
|
||||
3. Localiser le plugin "SIEM - Wazuh"
|
||||
4. Cliquer sur **Installer**
|
||||
5. Cliquer sur **Activer**
|
||||
|
||||
### 3. Configuration Initiale
|
||||
|
||||
#### Configuration des Droits
|
||||
1. **Administration > Profils**
|
||||
2. Sélectionner le profil à configurer
|
||||
3. Onglet **SIEM Wazuh Rights**
|
||||
4. Attribuer les droits appropriés :
|
||||
- **Configuration** : Accès à la configuration
|
||||
- **Servers** : Gestion des serveurs Wazuh
|
||||
- **Alerts** : Consultation des alertes
|
||||
|
||||
#### Ajout d'un Serveur Wazuh
|
||||
1. **Administration > Serveurs Wazuh**
|
||||
2. Cliquer sur **+** (Ajouter)
|
||||
3. Remplir les informations :
|
||||
```
|
||||
Nom connexion: Serveur Wazuh Production
|
||||
URL Serveur Wazuh: https://wazuh-manager.domain.com
|
||||
Port API: 55000
|
||||
Login API: wazuh
|
||||
Mot de passe API: [mot_de_passe_sécurisé]
|
||||
Intervalle de synchronisation: 300 (secondes)
|
||||
```
|
||||
4. **Tester la connexion**
|
||||
5. **Enregistrer**
|
||||
|
||||
#### Configuration Générale
|
||||
1. **Outils > Configuration SIEM Wazuh**
|
||||
2. Configurer les paramètres selon vos besoins :
|
||||
- **Synchronisation automatique** : Activé
|
||||
- **Création automatique de tickets** : Activé
|
||||
- **Niveau de règle minimum** : 5
|
||||
- **Rétention des alertes** : 90 jours
|
||||
|
||||
## Configuration
|
||||
|
||||
### Paramètres de Synchronisation
|
||||
|
||||
| Paramètre | Description | Valeur par défaut |
|
||||
|-----------|-------------|-------------------|
|
||||
| `sync_enabled` | Active la synchronisation | `1` (Oui) |
|
||||
| `max_alerts_per_sync` | Maximum d'alertes par sync | `100` |
|
||||
| `min_rule_level` | Niveau minimum des règles | `5` |
|
||||
| `sync_interval` | Intervalle en secondes | `300` |
|
||||
|
||||
### Mapping des Assets
|
||||
|
||||
Le plugin associe automatiquement les alertes aux assets GLPI :
|
||||
|
||||
1. **Par nom d'hôte** : `agent.name` → `computers.name`
|
||||
2. **Par adresse IP** : `agent.ip` → `networkports.ip`
|
||||
3. **Sensible à la casse** : Configurable
|
||||
4. **Patterns à ignorer** : `localhost`, `127.0.0.1`, etc.
|
||||
|
||||
### Création de Tickets
|
||||
|
||||
Les tickets sont créés automatiquement selon ces règles :
|
||||
|
||||
| Niveau Règle | Sévérité | Priorité Ticket |
|
||||
|--------------|----------|-----------------|
|
||||
| 1-4 | Low | Très faible |
|
||||
| 5-6 | Low | Faible |
|
||||
| 7-9 | Medium | Moyenne |
|
||||
| 10-11 | High | Élevée |
|
||||
| 12+ | Critical | Très élevée |
|
||||
|
||||
## Utilisation
|
||||
|
||||
### Consultation des Alertes
|
||||
|
||||
#### Sur les Assets
|
||||
- Aller sur un ordinateur ou équipement réseau
|
||||
- Cliquer sur l'onglet **"Alertes Wazuh"**
|
||||
- Consulter le résumé et la liste des alertes
|
||||
|
||||
#### Vue Globale
|
||||
- **Outils > Configuration SIEM Wazuh**
|
||||
- Consulter les **statistiques générales**
|
||||
- Filtrer par serveur, sévérité, statut
|
||||
|
||||
### Actions Disponibles
|
||||
|
||||
#### Sur les Alertes
|
||||
- ✅ **Marquer comme traitée**
|
||||
- ❌ **Ignorer l'alerte**
|
||||
- 🎫 **Créer un ticket manuellement**
|
||||
- 📊 **Exporter en CSV**
|
||||
|
||||
#### Sur les Serveurs
|
||||
- 🔧 **Tester la connexion**
|
||||
- 🔄 **Synchroniser manuellement**
|
||||
- ⏯️ **Activer/Désactiver**
|
||||
- 📈 **Consulter les statistiques**
|
||||
|
||||
## Tâches Automatisées
|
||||
|
||||
### Cron Jobs
|
||||
|
||||
Le plugin configure automatiquement deux tâches :
|
||||
|
||||
```bash
|
||||
# Synchronisation des alertes (toutes les 5 minutes)
|
||||
*/5 * * * * php /path/to/glpi/front/cron.php --force=PluginSiemWazuhAlert-sync_alerts
|
||||
|
||||
# Nettoyage des anciennes alertes (quotidien)
|
||||
0 2 * * * php /path/to/glpi/front/cron.php --force=PluginSiemWazuhAlert-cleanup_old_alerts
|
||||
```
|
||||
|
||||
### Activation des Tâches
|
||||
|
||||
1. **Administration > Tâches automatiques**
|
||||
2. Rechercher "Wazuh"
|
||||
3. Activer les tâches
|
||||
4. Configurer la fréquence si nécessaire
|
||||
|
||||
## Dépannage
|
||||
|
||||
### Problèmes Courants
|
||||
|
||||
#### Connexion Échouée
|
||||
```
|
||||
Erreur: Connection failed: cURL error 7: Failed to connect
|
||||
```
|
||||
**Solutions :**
|
||||
- Vérifier l'URL et le port du serveur
|
||||
- Contrôler les règles de firewall
|
||||
- Tester depuis le serveur GLPI : `curl -k https://wazuh-server:55000`
|
||||
|
||||
#### Authentification Échouée
|
||||
```
|
||||
Erreur: Authentication failed: 401 Unauthorized
|
||||
```
|
||||
**Solutions :**
|
||||
- Vérifier les identifiants API
|
||||
- Contrôler les permissions utilisateur Wazuh
|
||||
- Vérifier que l'utilisateur existe : `GET /security/users`
|
||||
|
||||
#### Pas d'Alertes Synchronisées
|
||||
**Vérifications :**
|
||||
1. Niveau de règle minimum trop élevé
|
||||
2. Serveur inactif
|
||||
3. Pas d'alertes récentes dans Wazuh
|
||||
4. Erreurs dans les logs : **Debug & Logs**
|
||||
|
||||
### Logs de Débogage
|
||||
|
||||
Activer le mode debug :
|
||||
1. **Outils > Configuration SIEM Wazuh**
|
||||
2. Onglet **Debug & Logs**
|
||||
3. **Mode debug** : Activé
|
||||
4. **Niveau de log** : Debug
|
||||
5. Consulter la section **Logs récents**
|
||||
|
||||
### Support et Logs Système
|
||||
|
||||
```bash
|
||||
# Logs Apache/Nginx
|
||||
tail -f /var/log/apache2/error.log | grep -i wazuh
|
||||
|
||||
# Logs PHP
|
||||
tail -f /var/log/php/error.log | grep -i wazuh
|
||||
|
||||
# Logs GLPI
|
||||
tail -f /var/log/glpi/glpi.log | grep -i wazuh
|
||||
```
|
||||
|
||||
## API et Intégration
|
||||
|
||||
### Endpoints Disponibles
|
||||
|
||||
Le plugin expose quelques endpoints pour intégration :
|
||||
|
||||
```
|
||||
GET /plugins/siem-wazuh/ajax/sync_alerts.php?action=status
|
||||
POST /plugins/siem-wazuh/ajax/sync_alerts.php (sync)
|
||||
POST /plugins/siem-wazuh/ajax/test_connection.php
|
||||
```
|
||||
|
||||
### Webhooks Wazuh
|
||||
|
||||
Pour recevoir les alertes en temps réel, configurez un webhook :
|
||||
|
||||
```xml
|
||||
<!-- /var/ossec/etc/ossec.conf -->
|
||||
<integration>
|
||||
<name>webhook</name>
|
||||
<hook_url>https://glpi.domain.com/plugins/siem-wazuh/webhook.php</hook_url>
|
||||
<level>7</level>
|
||||
<rule_id>100001,100002</rule_id>
|
||||
<alert_format>json</alert_format>
|
||||
</integration>
|
||||
```
|
||||
|
||||
## Développement
|
||||
|
||||
### Structure du Plugin
|
||||
|
||||
```
|
||||
plugins/siem-wazuh/
|
||||
├── README.md # Documentation
|
||||
├── CHANGELOG.md # Historique des versions
|
||||
├── LICENSE # Licence GPL v2+
|
||||
├── SIEM-Wazuh.xml # Descripteur plugin
|
||||
├── setup.php # Configuration principale
|
||||
├── hook.php # Installation/désinstallation
|
||||
├── inc/ # Classes PHP
|
||||
│ ├── wazuhserver.class.php
|
||||
│ ├── wazuhalert.class.php
|
||||
│ ├── wazuhconfig.class.php
|
||||
│ ├── wazuhtab.class.php
|
||||
│ └── wazuhapi.class.php
|
||||
├── front/ # Interface web
|
||||
│ ├── wazuhserver.php
|
||||
│ ├── wazuhserver.form.php
|
||||
│ └── wazuhconfig.php
|
||||
├── ajax/ # Requêtes AJAX
|
||||
│ ├── test_connection.php
|
||||
│ └── sync_alerts.php
|
||||
├── locales/ # Traductions
|
||||
│ ├── fr_FR.po
|
||||
│ ├── en_GB.po
|
||||
│ └── ...
|
||||
├── css/ # Feuilles de style
|
||||
│ └── style.css
|
||||
├── js/ # JavaScript
|
||||
│ └── wazuh.js
|
||||
├── pics/ # Images
|
||||
│ └── wazuh-logo.png
|
||||
└── sql/ # Scripts SQL
|
||||
├── install.sql
|
||||
└── uninstall.sql
|
||||
```
|
||||
|
||||
### Contribution
|
||||
|
||||
1. Fork le projet
|
||||
2. Créer une branche feature (`git checkout -b feature/nouvelle-fonctionnalite`)
|
||||
3. Commiter les modifications (`git commit -m 'Ajout nouvelle fonctionnalité'`)
|
||||
4. Pousser sur la branche (`git push origin feature/nouvelle-fonctionnalite`)
|
||||
5. Créer une Pull Request
|
||||
|
||||
## Licence
|
||||
|
||||
Ce plugin est distribué sous licence **GPL v2+**. Voir le fichier [LICENSE](LICENSE) pour plus de détails.
|
||||
|
||||
## Support
|
||||
|
||||
- 🐛 **Issues** : [GitHub Issues](https://github.com/siem-wazuh/glpi-plugin/issues)
|
||||
- 💬 **Discussions** : [GitHub Discussions](https://github.com/siem-wazuh/glpi-plugin/discussions)
|
||||
- 📧 **Email** : support@siem-wazuh.org
|
||||
- 📖 **Documentation** : [Wiki](https://github.com/siem-wazuh/glpi-plugin/wiki)
|
||||
|
||||
## Changelog
|
||||
|
||||
### Version 1.0.0 (2024-01-01)
|
||||
- 🎉 Version initiale
|
||||
- ✅ Support multiples serveurs Wazuh
|
||||
- ✅ Synchronisation automatique des alertes
|
||||
- ✅ Association avec assets GLPI
|
||||
- ✅ Création automatique de tickets
|
||||
- ✅ Interface multilingue (7 langues)
|
||||
- ✅ Gestion des droits utilisateur
|
||||
- ✅ API REST pour intégration
|
||||
- ✅ Logs de débogage complets
|
||||
|
||||
---
|
||||
|
||||
**Made with ❤️ by the SIEM-Wazuh Team**
|
106
SIEM-Wazuh.xml
Normal file
106
SIEM-Wazuh.xml
Normal file
@@ -0,0 +1,106 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<root>
|
||||
<name>SIEM - Wazuh</name>
|
||||
<key>siem-wazuh</key>
|
||||
<state>stable</state>
|
||||
<logo>https://raw.githubusercontent.com/siem-wazuh/glpi-plugin/main/pics/wazuh-logo.png</logo>
|
||||
<description>
|
||||
<short>
|
||||
<fr><![CDATA[Intégration SIEM Wazuh avec GLPI pour la gestion centralisée des alertes de sécurité]]></fr>
|
||||
<en><![CDATA[Wazuh SIEM integration with GLPI for centralized security alert management]]></en>
|
||||
<de><![CDATA[Wazuh SIEM Integration mit GLPI für zentrale Sicherheitswarnungen]]></de>
|
||||
<es><![CDATA[Integración SIEM Wazuh con GLPI para gestión centralizada de alertas de seguridad]]></es>
|
||||
<it><![CDATA[Integrazione SIEM Wazuh con GLPI per la gestione centralizzata degli avvisi di sicurezza]]></it>
|
||||
<pt><![CDATA[Integração SIEM Wazuh com GLPI para gestão centralizada de alertas de segurança]]></pt>
|
||||
<pl><![CDATA[Integracja SIEM Wazuh z GLPI do scentralizowanego zarządzania alertami bezpieczeństwa]]></pl>
|
||||
</short>
|
||||
<long>
|
||||
<fr><![CDATA[Ce plugin permet l'intégration complète de Wazuh SIEM avec GLPI. Il offre les fonctionnalités suivantes :
|
||||
|
||||
• Configuration de multiples serveurs Wazuh
|
||||
• Synchronisation automatique des alertes de sécurité
|
||||
• Association automatique des alertes aux assets de l'inventaire
|
||||
• Création automatique de tickets pour les incidents de sécurité
|
||||
• Interface multilingue (FR, EN, DE, ES, IT, PT, PL)
|
||||
• Tableau de bord avec statistiques des alertes
|
||||
• Gestion granulaire des droits utilisateur
|
||||
• Onglets dédiés sur les équipements informatiques
|
||||
|
||||
Le plugin surveille en continu les serveurs Wazuh configurés et crée automatiquement des tickets GLPI lorsque des alertes critiques sont détectées, permettant une réponse rapide aux incidents de sécurité.]]></fr>
|
||||
<en><![CDATA[This plugin enables complete integration of Wazuh SIEM with GLPI. It provides the following features:
|
||||
|
||||
• Configuration of multiple Wazuh servers
|
||||
• Automatic synchronization of security alerts
|
||||
• Automatic association of alerts with inventory assets
|
||||
• Automatic ticket creation for security incidents
|
||||
• Multilingual interface (FR, EN, DE, ES, IT, PT, PL)
|
||||
• Dashboard with alert statistics
|
||||
• Granular user rights management
|
||||
• Dedicated tabs on IT equipment
|
||||
|
||||
The plugin continuously monitors configured Wazuh servers and automatically creates GLPI tickets when critical alerts are detected, enabling rapid response to security incidents.]]></en>
|
||||
</long>
|
||||
</description>
|
||||
<homepage>https://github.com/siem-wazuh/glpi-plugin</homepage>
|
||||
<download>https://github.com/siem-wazuh/glpi-plugin/releases</download>
|
||||
<issues>https://github.com/siem-wazuh/glpi-plugin/issues</issues>
|
||||
<readme>https://github.com/siem-wazuh/glpi-plugin/blob/main/README.md</readme>
|
||||
<authors>
|
||||
<author>SIEM-Wazuh Team</author>
|
||||
</authors>
|
||||
<versions>
|
||||
<version>
|
||||
<num>1.0.0</num>
|
||||
<compatibility>~10.0.0</compatibility>
|
||||
<download_url>https://github.com/siem-wazuh/glpi-plugin/releases/download/1.0.0/glpi-siem-wazuh-1.0.0.tar.gz</download_url>
|
||||
<changelog><![CDATA[
|
||||
Version initiale 1.0.0 :
|
||||
- Configuration de serveurs Wazuh multiples
|
||||
- Synchronisation automatique des alertes
|
||||
- Association avec les assets GLPI
|
||||
- Création automatique de tickets
|
||||
- Interface multilingue
|
||||
- Gestion des droits utilisateur
|
||||
- Onglets sur les équipements
|
||||
- Tâches automatisées (cron)
|
||||
- API REST pour intégration externe
|
||||
]]></changelog>
|
||||
<php_min>7.4</php_min>
|
||||
<glpi_min>10.0.0</glpi_min>
|
||||
<glpi_max>10.0.99</glpi_max>
|
||||
</version>
|
||||
</versions>
|
||||
<langs>
|
||||
<lang>fr_FR</lang>
|
||||
<lang>en_GB</lang>
|
||||
<lang>de_DE</lang>
|
||||
<lang>es_ES</lang>
|
||||
<lang>it_IT</lang>
|
||||
<lang>pt_BR</lang>
|
||||
<lang>pl_PL</lang>
|
||||
</langs>
|
||||
<license>GPL v2+</license>
|
||||
<tags>
|
||||
<fr>
|
||||
<tag>SIEM</tag>
|
||||
<tag>Sécurité</tag>
|
||||
<tag>Wazuh</tag>
|
||||
<tag>Alertes</tag>
|
||||
<tag>Monitoring</tag>
|
||||
<tag>Intégration</tag>
|
||||
</fr>
|
||||
<en>
|
||||
<tag>SIEM</tag>
|
||||
<tag>Security</tag>
|
||||
<tag>Wazuh</tag>
|
||||
<tag>Alerts</tag>
|
||||
<tag>Monitoring</tag>
|
||||
<tag>Integration</tag>
|
||||
</en>
|
||||
</tags>
|
||||
<screenshots>
|
||||
<screenshot>https://raw.githubusercontent.com/siem-wazuh/glpi-plugin/main/pics/screenshot-1.png</screenshot>
|
||||
<screenshot>https://raw.githubusercontent.com/siem-wazuh/glpi-plugin/main/pics/screenshot-2.png</screenshot>
|
||||
<screenshot>https://raw.githubusercontent.com/siem-wazuh/glpi-plugin/main/pics/screenshot-3.png</screenshot>
|
||||
</screenshots>
|
||||
</root>
|
342
ajax/sync_alerts.php
Normal file
342
ajax/sync_alerts.php
Normal file
@@ -0,0 +1,342 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* AJAX - Synchronisation des alertes Wazuh
|
||||
*/
|
||||
|
||||
include ('../../../inc/includes.php');
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Vérification des droits et du plugin
|
||||
if (!Session::haveRight("plugin_siem_wazuh_server", UPDATE) || !Plugin::isPluginActive('siem-wazuh')) {
|
||||
echo json_encode(['success' => false, 'message' => __('Access denied', 'siem-wazuh')]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Vérification CSRF pour les requêtes POST
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !Session::validateCSRF($_POST)) {
|
||||
echo json_encode(['success' => false, 'message' => __('Invalid CSRF token', 'siem-wazuh')]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$response = ['success' => false, 'message' => '', 'stats' => []];
|
||||
|
||||
try {
|
||||
$action = $_POST['action'] ?? $_GET['action'] ?? 'sync';
|
||||
|
||||
switch ($action) {
|
||||
case 'sync':
|
||||
$server_id = intval($_POST['server_id'] ?? $_GET['server_id'] ?? 0);
|
||||
|
||||
if ($server_id > 0) {
|
||||
// Synchronisation d'un serveur spécifique
|
||||
$result = syncSingleServer($server_id);
|
||||
} else {
|
||||
// Synchronisation de tous les serveurs actifs
|
||||
$result = syncAllServers();
|
||||
}
|
||||
|
||||
$response = $result;
|
||||
break;
|
||||
|
||||
case 'sync_for_item':
|
||||
// Synchronisation pour un élément spécifique
|
||||
$item_id = intval($_POST['item_id'] ?? 0);
|
||||
$item_type = $_POST['item_type'] ?? '';
|
||||
|
||||
if ($item_id > 0 && !empty($item_type)) {
|
||||
$result = syncForItem($item_id, $item_type);
|
||||
$response = $result;
|
||||
} else {
|
||||
throw new Exception(__('Invalid item parameters', 'siem-wazuh'));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'status':
|
||||
// Statut de la synchronisation
|
||||
$response = getSyncStatus();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception(__('Unknown action', 'siem-wazuh'));
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$response = [
|
||||
'success' => false,
|
||||
'message' => $e->getMessage(),
|
||||
'error_type' => 'exception'
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
/**
|
||||
* Synchroniser un serveur spécifique
|
||||
*/
|
||||
function syncSingleServer($server_id) {
|
||||
$server = new PluginSiemWazuhServer();
|
||||
if (!$server->getFromDB($server_id)) {
|
||||
throw new Exception(__('Server not found', 'siem-wazuh'));
|
||||
}
|
||||
|
||||
$start_time = microtime(true);
|
||||
$result = $server->syncAlerts();
|
||||
$sync_time = round((microtime(true) - $start_time) * 1000, 2);
|
||||
|
||||
if ($result['success']) {
|
||||
// Extraire le nombre d'alertes du message
|
||||
$processed = 0;
|
||||
if (preg_match('/(\d+)/', $result['message'], $matches)) {
|
||||
$processed = intval($matches[1]);
|
||||
}
|
||||
|
||||
Event::log(
|
||||
$server_id,
|
||||
"PluginSiemWazuhServer",
|
||||
5,
|
||||
"sync",
|
||||
sprintf(__('Manual sync completed for server %s: %s'), $server->fields['name'], $result['message'])
|
||||
);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => $result['message'],
|
||||
'stats' => [
|
||||
'processed' => $processed,
|
||||
'sync_time' => $sync_time,
|
||||
'server_name' => $server->fields['name']
|
||||
]
|
||||
];
|
||||
} else {
|
||||
Event::log(
|
||||
$server_id,
|
||||
"PluginSiemWazuhServer",
|
||||
2,
|
||||
"sync",
|
||||
sprintf(__('Manual sync failed for server %s: %s'), $server->fields['name'], $result['message'])
|
||||
);
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $result['message'],
|
||||
'server_name' => $server->fields['name']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchroniser tous les serveurs actifs
|
||||
*/
|
||||
function syncAllServers() {
|
||||
global $DB;
|
||||
|
||||
$iterator = $DB->request([
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_servers',
|
||||
'WHERE' => ['is_active' => 1]
|
||||
]);
|
||||
|
||||
$total_processed = 0;
|
||||
$successful_servers = 0;
|
||||
$failed_servers = 0;
|
||||
$sync_results = [];
|
||||
|
||||
$start_time = microtime(true);
|
||||
|
||||
foreach ($iterator as $server_data) {
|
||||
try {
|
||||
$result = syncSingleServer($server_data['id']);
|
||||
|
||||
if ($result['success']) {
|
||||
$successful_servers++;
|
||||
$total_processed += $result['stats']['processed'] ?? 0;
|
||||
$sync_results[] = [
|
||||
'server' => $server_data['name'],
|
||||
'status' => 'success',
|
||||
'processed' => $result['stats']['processed'] ?? 0
|
||||
];
|
||||
} else {
|
||||
$failed_servers++;
|
||||
$sync_results[] = [
|
||||
'server' => $server_data['name'],
|
||||
'status' => 'failed',
|
||||
'error' => $result['message']
|
||||
];
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$failed_servers++;
|
||||
$sync_results[] = [
|
||||
'server' => $server_data['name'],
|
||||
'status' => 'error',
|
||||
'error' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$total_time = round((microtime(true) - $start_time) * 1000, 2);
|
||||
|
||||
$message = sprintf(
|
||||
__('Sync completed: %d servers processed, %d successful, %d failed, %d total alerts processed'),
|
||||
count($sync_results),
|
||||
$successful_servers,
|
||||
$failed_servers,
|
||||
$total_processed
|
||||
);
|
||||
|
||||
return [
|
||||
'success' => $failed_servers == 0,
|
||||
'message' => $message,
|
||||
'stats' => [
|
||||
'total_servers' => count($sync_results),
|
||||
'successful_servers' => $successful_servers,
|
||||
'failed_servers' => $failed_servers,
|
||||
'total_processed' => $total_processed,
|
||||
'sync_time' => $total_time
|
||||
],
|
||||
'details' => $sync_results
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchroniser pour un élément spécifique
|
||||
*/
|
||||
function syncForItem($item_id, $item_type) {
|
||||
// Cette fonction recherche et synchronise les alertes
|
||||
// pour un élément spécifique (ordinateur, équipement réseau, etc.)
|
||||
|
||||
$valid_types = ['Computer', 'NetworkEquipment', 'Peripheral', 'Phone', 'Printer'];
|
||||
if (!in_array($item_type, $valid_types)) {
|
||||
throw new Exception(__('Invalid item type', 'siem-wazuh'));
|
||||
}
|
||||
|
||||
$item = new $item_type();
|
||||
if (!$item->getFromDB($item_id)) {
|
||||
throw new Exception(__('Item not found', 'siem-wazuh'));
|
||||
}
|
||||
|
||||
// Récupérer les informations de l'élément pour le matching
|
||||
$search_criteria = [];
|
||||
|
||||
// Recherche par nom
|
||||
if (!empty($item->fields['name'])) {
|
||||
$search_criteria['hostname'] = $item->fields['name'];
|
||||
}
|
||||
|
||||
// Recherche par IP (si disponible)
|
||||
$ip_addresses = [];
|
||||
if (method_exists($item, 'getNetworkInterfaces')) {
|
||||
// Logique pour récupérer les adresses IP de l'élément
|
||||
// Cette partie nécessiterait une implémentation plus détaillée
|
||||
}
|
||||
|
||||
// Synchroniser avec tous les serveurs actifs en filtrant par critères
|
||||
global $DB;
|
||||
$iterator = $DB->request([
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_servers',
|
||||
'WHERE' => ['is_active' => 1]
|
||||
]);
|
||||
|
||||
$total_found = 0;
|
||||
|
||||
foreach ($iterator as $server_data) {
|
||||
$server = new PluginSiemWazuhServer();
|
||||
$server->fields = $server_data;
|
||||
|
||||
$api = new PluginSiemWazuhAPI($server);
|
||||
|
||||
// Construire les paramètres de recherche pour l'API
|
||||
$search_params = [];
|
||||
if (!empty($search_criteria['hostname'])) {
|
||||
$search_params['agent.name'] = $search_criteria['hostname'];
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $api->getAlerts($search_params);
|
||||
if ($result['success'] && isset($result['data']['data'])) {
|
||||
$alert = new PluginSiemWazuhAlert();
|
||||
$processed = $alert->processAlerts($server_data['id'], $result['data']['data']);
|
||||
$total_found += $processed;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Log l'erreur mais continue avec les autres serveurs
|
||||
error_log("SIEM-Wazuh: Error syncing for item $item_type:$item_id on server {$server_data['name']}: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => sprintf(__('%d alerts found and processed for %s'), $total_found, $item->getName()),
|
||||
'stats' => [
|
||||
'processed' => $total_found,
|
||||
'item_name' => $item->getName(),
|
||||
'item_type' => $item_type
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtenir le statut de synchronisation
|
||||
*/
|
||||
function getSyncStatus() {
|
||||
global $DB;
|
||||
|
||||
// Statistiques générales
|
||||
$stats = [];
|
||||
|
||||
// Nombre total de serveurs
|
||||
$stats['total_servers'] = countElementsInTable('glpi_plugin_siem_wazuh_servers');
|
||||
$stats['active_servers'] = countElementsInTable('glpi_plugin_siem_wazuh_servers', ['is_active' => 1]);
|
||||
|
||||
// Dernière synchronisation
|
||||
$last_sync = $DB->request([
|
||||
'SELECT' => ['MAX' => 'last_sync AS last_sync'],
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_servers',
|
||||
'WHERE' => ['is_active' => 1]
|
||||
])->current();
|
||||
|
||||
$stats['last_sync'] = $last_sync['last_sync'];
|
||||
|
||||
// Alertes par statut
|
||||
$alert_stats = [];
|
||||
$statuses = ['new', 'processed', 'ignored', 'ticket_created'];
|
||||
foreach ($statuses as $status) {
|
||||
$alert_stats[$status] = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['status' => $status]);
|
||||
}
|
||||
$stats['alerts'] = $alert_stats;
|
||||
|
||||
// Alertes par sévérité
|
||||
$severity_stats = [];
|
||||
$severities = ['low', 'medium', 'high', 'critical'];
|
||||
foreach ($severities as $severity) {
|
||||
$severity_stats[$severity] = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['severity' => $severity]);
|
||||
}
|
||||
$stats['severity'] = $severity_stats;
|
||||
|
||||
// Alertes des dernières 24h
|
||||
$stats['alerts_24h'] = countElementsInTable(
|
||||
'glpi_plugin_siem_wazuh_alerts',
|
||||
['date_creation' => ['>=', date('Y-m-d H:i:s', strtotime('-24 hours'))]]
|
||||
);
|
||||
|
||||
// Tickets créés aujourd'hui
|
||||
$stats['tickets_today'] = countElementsInTable(
|
||||
'glpi_plugin_siem_wazuh_alerts',
|
||||
[
|
||||
'status' => 'ticket_created',
|
||||
'date_mod' => ['>=', date('Y-m-d 00:00:00')]
|
||||
]
|
||||
);
|
||||
|
||||
// Vérifier s'il y a une synchronisation en cours
|
||||
$config = new PluginSiemWazuhConfig();
|
||||
$sync_in_progress = $config->getConfiguration('sync_in_progress', 0);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => __('Status retrieved successfully', 'siem-wazuh'),
|
||||
'stats' => $stats,
|
||||
'sync_in_progress' => (bool)$sync_in_progress,
|
||||
'timestamp' => date('Y-m-d H:i:s')
|
||||
];
|
||||
}
|
81
ajax/test_connection.php
Normal file
81
ajax/test_connection.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* AJAX - Test de connexion aux serveurs Wazuh
|
||||
*/
|
||||
|
||||
include ('../../../inc/includes.php');
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Vérification des droits et du plugin
|
||||
if (!Session::haveRight("plugin_siem_wazuh_server", READ) || !Plugin::isPluginActive('siem-wazuh')) {
|
||||
echo json_encode(['success' => false, 'message' => __('Access denied', 'siem-wazuh')]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Vérification CSRF
|
||||
if (!Session::validateCSRF($_POST)) {
|
||||
echo json_encode(['success' => false, 'message' => __('Invalid CSRF token', 'siem-wazuh')]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$response = ['success' => false, 'message' => ''];
|
||||
|
||||
try {
|
||||
$server_id = intval($_POST['server_id'] ?? 0);
|
||||
|
||||
if ($server_id <= 0) {
|
||||
throw new Exception(__('Invalid server ID', 'siem-wazuh'));
|
||||
}
|
||||
|
||||
$server = new PluginSiemWazuhServer();
|
||||
if (!$server->getFromDB($server_id)) {
|
||||
throw new Exception(__('Server not found', 'siem-wazuh'));
|
||||
}
|
||||
|
||||
// Test de connexion
|
||||
$result = $server->testConnection();
|
||||
|
||||
if ($result['success']) {
|
||||
$response = [
|
||||
'success' => true,
|
||||
'message' => $result['message'],
|
||||
'server_info' => $result['data'] ?? null,
|
||||
'timestamp' => date('Y-m-d H:i:s')
|
||||
];
|
||||
|
||||
// Log du test réussi
|
||||
Event::log(
|
||||
$server_id,
|
||||
"PluginSiemWazuhServer",
|
||||
5,
|
||||
"connection",
|
||||
sprintf(__('Connection test successful for server %s'), $server->fields['name'])
|
||||
);
|
||||
} else {
|
||||
$response = [
|
||||
'success' => false,
|
||||
'message' => $result['message'],
|
||||
'error_type' => 'connection_failed'
|
||||
];
|
||||
|
||||
// Log du test échoué
|
||||
Event::log(
|
||||
$server_id,
|
||||
"PluginSiemWazuhServer",
|
||||
2,
|
||||
"connection",
|
||||
sprintf(__('Connection test failed for server %s: %s'), $server->fields['name'], $result['message'])
|
||||
);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$response = [
|
||||
'success' => false,
|
||||
'message' => $e->getMessage(),
|
||||
'error_type' => 'exception'
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
487
css/style.css
Normal file
487
css/style.css
Normal file
@@ -0,0 +1,487 @@
|
||||
/**
|
||||
* SIEM-Wazuh Plugin for GLPI - Stylesheet
|
||||
* Styles for the SIEM-Wazuh plugin interface
|
||||
*/
|
||||
|
||||
/* General plugin styles */
|
||||
.siem-wazuh-container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.siem-wazuh-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 10px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.siem-wazuh-logo {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/* Server status indicators */
|
||||
.server-status {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.server-status.active {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.server-status.inactive {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.server-status.testing {
|
||||
background-color: #ffc107;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
/* Alert severity badges */
|
||||
.severity-badge {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.severity-low {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.severity-medium {
|
||||
background-color: #ffc107;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.severity-high {
|
||||
background-color: #fd7e14;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.severity-critical {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Status badges */
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.status-new {
|
||||
background-color: #17a2b8;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.status-processed {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.status-ignored {
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.status-ticket {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.status-ticket_created {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Rule level indicators */
|
||||
.rule-level-1,
|
||||
.rule-level-2,
|
||||
.rule-level-3,
|
||||
.rule-level-4 {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.rule-level-5,
|
||||
.rule-level-6,
|
||||
.rule-level-7 {
|
||||
background-color: #ffc107;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.rule-level-8,
|
||||
.rule-level-9,
|
||||
.rule-level-10,
|
||||
.rule-level-11 {
|
||||
background-color: #fd7e14;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.rule-level-12,
|
||||
.rule-level-13,
|
||||
.rule-level-14,
|
||||
.rule-level-15 {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Alert summary styles */
|
||||
.alert-summary {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.alert-summary .tab_cadre_fixe {
|
||||
border: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.alert-summary td {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
border-right: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.alert-summary td:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.alert-summary strong {
|
||||
font-size: 18px;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
/* Filters section */
|
||||
.alert-filters {
|
||||
background-color: #f8f9fa;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* Action buttons */
|
||||
.action-buttons {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.action-buttons .btn {
|
||||
margin: 0 2px;
|
||||
padding: 4px 8px;
|
||||
font-size: 11px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #007bff;
|
||||
border-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background-color: #28a745;
|
||||
border-color: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background-color: #ffc107;
|
||||
border-color: #ffc107;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background-color: #dc3545;
|
||||
border-color: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: #6c757d;
|
||||
border-color: #6c757d;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* JSON data display */
|
||||
.json-data {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
font-family: Monaco, Menlo, "Ubuntu Mono", monospace;
|
||||
font-size: 12px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
/* Configuration tabs */
|
||||
.config-tabs {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.config-tabs .nav-tabs {
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.config-tabs .nav-item {
|
||||
display: inline-block;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.config-tabs .nav-link {
|
||||
display: block;
|
||||
padding: 10px 15px;
|
||||
text-decoration: none;
|
||||
color: #495057;
|
||||
border: 1px solid transparent;
|
||||
border-top-left-radius: 0.25rem;
|
||||
border-top-right-radius: 0.25rem;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.config-tabs .nav-link:hover {
|
||||
border-color: #e9ecef #e9ecef #dee2e6;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.config-tabs .nav-link.active {
|
||||
color: #495057;
|
||||
background-color: #fff;
|
||||
border-color: #dee2e6 #dee2e6 #fff;
|
||||
}
|
||||
|
||||
/* Log level badges */
|
||||
.log-debug {
|
||||
background-color: #d1ecf1;
|
||||
color: #0c5460;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.log-info {
|
||||
background-color: #bee5eb;
|
||||
color: #0c5460;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.log-warning {
|
||||
background-color: #fff3cd;
|
||||
color: #856404;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.log-error {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.log-critical {
|
||||
background-color: #f5c6cb;
|
||||
color: #721c24;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Alert messages */
|
||||
.alert {
|
||||
padding: 12px 20px;
|
||||
margin: 15px 0;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
color: #155724;
|
||||
background-color: #d4edda;
|
||||
border-color: #c3e6cb;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
color: #0c5460;
|
||||
background-color: #d1ecf1;
|
||||
border-color: #bee5eb;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
color: #856404;
|
||||
background-color: #fff3cd;
|
||||
border-color: #ffeaa7;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
color: #721c24;
|
||||
background-color: #f8d7da;
|
||||
border-color: #f5c6cb;
|
||||
}
|
||||
|
||||
/* Statistics cards */
|
||||
.stats-card {
|
||||
background: #fff;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 5px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.stats-card h3 {
|
||||
color: #495057;
|
||||
font-size: 18px;
|
||||
margin-bottom: 15px;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
text-align: center;
|
||||
padding: 15px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #007bff;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* Responsive design */
|
||||
@media (max-width: 768px) {
|
||||
.alert-summary td {
|
||||
padding: 5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.alert-summary strong {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.action-buttons .btn {
|
||||
padding: 2px 4px;
|
||||
font-size: 10px;
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Loading animations */
|
||||
.loading-spinner {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid #f3f3f3;
|
||||
border-top: 2px solid #007bff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Tooltip styles */
|
||||
.wazuh-tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.wazuh-tooltip .tooltiptext {
|
||||
visibility: hidden;
|
||||
width: 200px;
|
||||
background-color: #555;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
bottom: 125%;
|
||||
left: 50%;
|
||||
margin-left: -100px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.wazuh-tooltip:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Dark theme support */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.siem-wazuh-header {
|
||||
background: #343a40;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.alert-filters {
|
||||
background-color: #343a40;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.json-data {
|
||||
background-color: #2d3748;
|
||||
color: #e2e8f0;
|
||||
border-color: #4a5568;
|
||||
}
|
||||
|
||||
.stats-card {
|
||||
background: #343a40;
|
||||
color: #fff;
|
||||
border-color: #4a5568;
|
||||
}
|
||||
}
|
706
front/wazuhalert.php
Normal file
706
front/wazuhalert.php
Normal file
@@ -0,0 +1,706 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* Classe de gestion des alertes Wazuh
|
||||
*/
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
die("Sorry. You can't access this file directly");
|
||||
}
|
||||
|
||||
class PluginSiemWazuhAlert extends CommonDBTM {
|
||||
|
||||
public $dohistory = true;
|
||||
|
||||
static $rightname = 'plugin_siem_wazuh_alert';
|
||||
|
||||
const MATRIX_FIELD = '';
|
||||
const SEARCHOPTIONS_INHERITANCE = true;
|
||||
|
||||
/**
|
||||
* Return the localized name of the current Type
|
||||
*/
|
||||
static function getTypeName($nb = 0) {
|
||||
return _n('Wazuh Alert', 'Wazuh Alerts', $nb, 'siem-wazuh');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define search options
|
||||
*/
|
||||
function rawSearchOptions() {
|
||||
$tab = [];
|
||||
|
||||
$tab[] = [
|
||||
'id' => 'common',
|
||||
'name' => __('Characteristics')
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '1',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'alert_id',
|
||||
'name' => __('Alert ID', 'siem-wazuh'),
|
||||
'datatype' => 'itemlink',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '2',
|
||||
'table' => 'glpi_plugin_siem_wazuh_servers',
|
||||
'field' => 'name',
|
||||
'name' => __('Wazuh Server', 'siem-wazuh'),
|
||||
'datatype' => 'dropdown',
|
||||
'massiveaction' => false,
|
||||
'linkfield' => 'wazuh_server_id'
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '3',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'rule_id',
|
||||
'name' => __('Rule ID', 'siem-wazuh'),
|
||||
'datatype' => 'integer',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '4',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'rule_level',
|
||||
'name' => __('Rule Level', 'siem-wazuh'),
|
||||
'datatype' => 'integer',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '5',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'rule_description',
|
||||
'name' => __('Rule Description', 'siem-wazuh'),
|
||||
'datatype' => 'text',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '6',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'agent_name',
|
||||
'name' => __('Agent Name', 'siem-wazuh'),
|
||||
'datatype' => 'string',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '7',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'agent_ip',
|
||||
'name' => __('Agent IP', 'siem-wazuh'),
|
||||
'datatype' => 'string',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '8',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'timestamp',
|
||||
'name' => __('Alert Timestamp', 'siem-wazuh'),
|
||||
'datatype' => 'datetime',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '9',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'status',
|
||||
'name' => __('Status'),
|
||||
'datatype' => 'specific',
|
||||
'searchtype' => 'equals',
|
||||
'massiveaction' => true
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '10',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'severity',
|
||||
'name' => __('Severity', 'siem-wazuh'),
|
||||
'datatype' => 'specific',
|
||||
'searchtype' => 'equals',
|
||||
'massiveaction' => true
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '11',
|
||||
'table' => 'glpi_computers',
|
||||
'field' => 'name',
|
||||
'name' => __('Associated Computer', 'siem-wazuh'),
|
||||
'datatype' => 'dropdown',
|
||||
'massiveaction' => false,
|
||||
'linkfield' => 'computer_id'
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '12',
|
||||
'table' => 'glpi_networkequipments',
|
||||
'field' => 'name',
|
||||
'name' => __('Associated Network Equipment', 'siem-wazuh'),
|
||||
'datatype' => 'dropdown',
|
||||
'massiveaction' => false,
|
||||
'linkfield' => 'networkequipment_id'
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '13',
|
||||
'table' => 'glpi_tickets',
|
||||
'field' => 'name',
|
||||
'name' => __('Associated Ticket', 'siem-wazuh'),
|
||||
'datatype' => 'dropdown',
|
||||
'massiveaction' => false,
|
||||
'linkfield' => 'ticket_id'
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '30',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'date_creation',
|
||||
'name' => __('Creation date'),
|
||||
'datatype' => 'datetime',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '31',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'date_mod',
|
||||
'name' => __('Last update'),
|
||||
'datatype' => 'datetime',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
return $tab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display specific values
|
||||
*/
|
||||
static function getSpecificValueToDisplay($field, $values, array $options = []) {
|
||||
if (!is_array($values)) {
|
||||
$values = [$field => $values];
|
||||
}
|
||||
|
||||
switch ($field) {
|
||||
case 'status':
|
||||
$statuses = self::getStatusArray();
|
||||
return $statuses[$values[$field]] ?? $values[$field];
|
||||
|
||||
case 'severity':
|
||||
$severities = self::getSeverityArray();
|
||||
return $severities[$values[$field]] ?? $values[$field];
|
||||
}
|
||||
|
||||
return parent::getSpecificValueToDisplay($field, $values, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status array
|
||||
*/
|
||||
static function getStatusArray() {
|
||||
return [
|
||||
'new' => __('New', 'siem-wazuh'),
|
||||
'processed' => __('Processed', 'siem-wazuh'),
|
||||
'ignored' => __('Ignored', 'siem-wazuh'),
|
||||
'ticket_created' => __('Ticket Created', 'siem-wazuh')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get severity array
|
||||
*/
|
||||
static function getSeverityArray() {
|
||||
return [
|
||||
'low' => __('Low', 'siem-wazuh'),
|
||||
'medium' => __('Medium', 'siem-wazuh'),
|
||||
'high' => __('High', 'siem-wazuh'),
|
||||
'critical' => __('Critical', 'siem-wazuh')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Display form
|
||||
*/
|
||||
function showForm($ID, $options = []) {
|
||||
$this->initForm($ID, $options);
|
||||
$this->showFormHeader($options);
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Alert ID', 'siem-wazuh')."</td>";
|
||||
echo "<td>".$this->fields["alert_id"]."</td>";
|
||||
echo "<td>".__('Rule ID', 'siem-wazuh')."</td>";
|
||||
echo "<td>".$this->fields["rule_id"]."</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Rule Level', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
echo $this->fields["rule_level"];
|
||||
if ($this->fields["rule_level"]) {
|
||||
$severity = self::getSeverityFromLevel($this->fields["rule_level"]);
|
||||
$severities = self::getSeverityArray();
|
||||
echo " <span class='badge severity-$severity'>".$severities[$severity]."</span>";
|
||||
}
|
||||
echo "</td>";
|
||||
echo "<td>".__('Agent Name', 'siem-wazuh')."</td>";
|
||||
echo "<td>".$this->fields["agent_name"]."</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Agent IP', 'siem-wazuh')."</td>";
|
||||
echo "<td>".$this->fields["agent_ip"]."</td>";
|
||||
echo "<td>".__('Timestamp', 'siem-wazuh')."</td>";
|
||||
echo "<td>".Html::convDateTime($this->fields["timestamp"])."</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Status')."</td>";
|
||||
echo "<td>";
|
||||
$statuses = self::getStatusArray();
|
||||
Dropdown::showFromArray('status', $statuses, ['value' => $this->fields["status"]]);
|
||||
echo "</td>";
|
||||
echo "<td>".__('Severity', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
$severities = self::getSeverityArray();
|
||||
Dropdown::showFromArray('severity', $severities, ['value' => $this->fields["severity"]]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Rule Description', 'siem-wazuh')."</td>";
|
||||
echo "<td colspan='3'>";
|
||||
echo nl2br(Html::entities_deep($this->fields["rule_description"]));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
// Affichage des associations
|
||||
if ($this->fields["computer_id"]) {
|
||||
$computer = new Computer();
|
||||
$computer->getFromDB($this->fields["computer_id"]);
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Associated Computer')."</td>";
|
||||
echo "<td>";
|
||||
echo $computer->getLink();
|
||||
echo "</td>";
|
||||
echo "<td></td><td></td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
if ($this->fields["networkequipment_id"]) {
|
||||
$netequip = new NetworkEquipment();
|
||||
$netequip->getFromDB($this->fields["networkequipment_id"]);
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Associated Network Equipment')."</td>";
|
||||
echo "<td>";
|
||||
echo $netequip->getLink();
|
||||
echo "</td>";
|
||||
echo "<td></td><td></td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
if ($this->fields["ticket_id"]) {
|
||||
$ticket = new Ticket();
|
||||
$ticket->getFromDB($this->fields["ticket_id"]);
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Associated Ticket')."</td>";
|
||||
echo "<td>";
|
||||
echo $ticket->getLink();
|
||||
echo "</td>";
|
||||
echo "<td></td><td></td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
// Affichage des données brutes
|
||||
if (!empty($this->fields["raw_data"])) {
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td colspan='4'>";
|
||||
echo "<details>";
|
||||
echo "<summary>".__('Raw Alert Data', 'siem-wazuh')."</summary>";
|
||||
echo "<pre class='json-data'>";
|
||||
echo json_encode(json_decode($this->fields["raw_data"]), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
echo "</pre>";
|
||||
echo "</details>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
$this->showFormButtons($options);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process alerts from Wazuh API
|
||||
*/
|
||||
function processAlerts($server_id, $alerts_data) {
|
||||
$processed = 0;
|
||||
$config = new PluginSiemWazuhConfig();
|
||||
|
||||
foreach ($alerts_data as $alert_data) {
|
||||
$alert_id = $alert_data['id'] ?? $alert_data['_id'] ?? null;
|
||||
if (!$alert_id) continue;
|
||||
|
||||
// Vérifier si l'alerte existe déjà
|
||||
if ($this->getFromDBByCrit([
|
||||
'wazuh_server_id' => $server_id,
|
||||
'alert_id' => $alert_id
|
||||
])) {
|
||||
continue; // Alerte déjà traitée
|
||||
}
|
||||
|
||||
// Extraire les informations de l'alerte
|
||||
$rule_id = $alert_data['rule']['id'] ?? null;
|
||||
$rule_level = $alert_data['rule']['level'] ?? 0;
|
||||
$rule_description = $alert_data['rule']['description'] ?? '';
|
||||
$agent_name = $alert_data['agent']['name'] ?? '';
|
||||
$agent_ip = $alert_data['agent']['ip'] ?? '';
|
||||
$timestamp = $alert_data['timestamp'] ?? $alert_data['@timestamp'] ?? date('Y-m-d H:i:s');
|
||||
|
||||
// Convertir le timestamp au format MySQL
|
||||
if (strpos($timestamp, 'T') !== false) {
|
||||
$timestamp = str_replace('T', ' ', substr($timestamp, 0, 19));
|
||||
}
|
||||
|
||||
// Déterminer la sévérité
|
||||
$severity = self::getSeverityFromLevel($rule_level);
|
||||
|
||||
// Créer l'alerte
|
||||
$alert_input = [
|
||||
'wazuh_server_id' => $server_id,
|
||||
'alert_id' => $alert_id,
|
||||
'rule_id' => $rule_id,
|
||||
'rule_level' => $rule_level,
|
||||
'rule_description' => $rule_description,
|
||||
'agent_id' => $alert_data['agent']['id'] ?? '',
|
||||
'agent_name' => $agent_name,
|
||||
'agent_ip' => $agent_ip,
|
||||
'timestamp' => $timestamp,
|
||||
'raw_data' => json_encode($alert_data),
|
||||
'severity' => $severity,
|
||||
'status' => 'new'
|
||||
];
|
||||
|
||||
// Tenter d'associer avec un asset
|
||||
$this->associateWithAsset($alert_input, $agent_name, $agent_ip);
|
||||
|
||||
if ($alert_id = $this->add($alert_input)) {
|
||||
$processed++;
|
||||
|
||||
// Créer automatiquement un ticket si configuré
|
||||
if ($config->getConfiguration('auto_create_ticket', 1)) {
|
||||
$this->createTicketFromAlert($alert_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $processed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate alert with GLPI asset
|
||||
*/
|
||||
private function associateWithAsset(&$alert_input, $agent_name, $agent_ip) {
|
||||
global $DB;
|
||||
|
||||
// Recherche par nom d'agent
|
||||
if (!empty($agent_name)) {
|
||||
// Recherche dans les ordinateurs
|
||||
$computer = $DB->request([
|
||||
'SELECT' => ['id'],
|
||||
'FROM' => 'glpi_computers',
|
||||
'WHERE' => [
|
||||
'name' => $agent_name,
|
||||
'is_deleted' => 0
|
||||
],
|
||||
'LIMIT' => 1
|
||||
])->current();
|
||||
|
||||
if ($computer) {
|
||||
$alert_input['computer_id'] = $computer['id'];
|
||||
return;
|
||||
}
|
||||
|
||||
// Recherche dans les équipements réseau
|
||||
$netequip = $DB->request([
|
||||
'SELECT' => ['id'],
|
||||
'FROM' => 'glpi_networkequipments',
|
||||
'WHERE' => [
|
||||
'name' => $agent_name,
|
||||
'is_deleted' => 0
|
||||
],
|
||||
'LIMIT' => 1
|
||||
])->current();
|
||||
|
||||
if ($netequip) {
|
||||
$alert_input['networkequipment_id'] = $netequip['id'];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Recherche par adresse IP
|
||||
if (!empty($agent_ip)) {
|
||||
// Recherche dans les ports réseau
|
||||
$port = $DB->request([
|
||||
'SELECT' => ['items_id', 'itemtype'],
|
||||
'FROM' => 'glpi_networkports',
|
||||
'INNER JOIN' => [
|
||||
'glpi_ipaddresses' => [
|
||||
'ON' => [
|
||||
'glpi_networkports' => 'id',
|
||||
'glpi_ipaddresses' => 'items_id'
|
||||
]
|
||||
]
|
||||
],
|
||||
'WHERE' => [
|
||||
'glpi_ipaddresses.name' => $agent_ip,
|
||||
'glpi_ipaddresses.itemtype' => 'NetworkPort'
|
||||
],
|
||||
'LIMIT' => 1
|
||||
])->current();
|
||||
|
||||
if ($port) {
|
||||
if ($port['itemtype'] == 'Computer') {
|
||||
$alert_input['computer_id'] = $port['items_id'];
|
||||
} elseif ($port['itemtype'] == 'NetworkEquipment') {
|
||||
$alert_input['networkequipment_id'] = $port['items_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ticket from alert
|
||||
*/
|
||||
function createTicketFromAlert($alert_id) {
|
||||
if (!$this->getFromDB($alert_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$server = new PluginSiemWazuhServer();
|
||||
if (!$server->getFromDB($this->fields['wazuh_server_id'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ticket = new Ticket();
|
||||
$config = new PluginSiemWazuhConfig();
|
||||
|
||||
// Déterminer la priorité basée sur le niveau de règle
|
||||
$priority = self::getPriorityFromLevel($this->fields['rule_level']);
|
||||
|
||||
$ticket_input = [
|
||||
'name' => sprintf(__('Wazuh Alert: %s', 'siem-wazuh'), $this->fields['rule_description']),
|
||||
'content' => $this->generateTicketContent(),
|
||||
'type' => $server->fields['ticket_type'] ?: 1,
|
||||
'itilcategories_id' => $server->fields['ticket_category'],
|
||||
'priority' => $priority,
|
||||
'urgency' => $priority,
|
||||
'impact' => $priority,
|
||||
'status' => CommonITILObject::INCOMING,
|
||||
'_add_validation' => 0
|
||||
];
|
||||
|
||||
// Associer le ticket à l'asset si trouvé
|
||||
if ($this->fields['computer_id']) {
|
||||
$ticket_input['_link']['items_id'] = $this->fields['computer_id'];
|
||||
$ticket_input['_link']['itemtype'] = 'Computer';
|
||||
} elseif ($this->fields['networkequipment_id']) {
|
||||
$ticket_input['_link']['items_id'] = $this->fields['networkequipment_id'];
|
||||
$ticket_input['_link']['itemtype'] = 'NetworkEquipment';
|
||||
}
|
||||
|
||||
$ticket_id = $ticket->add($ticket_input);
|
||||
|
||||
if ($ticket_id) {
|
||||
// Mettre à jour l'alerte
|
||||
$this->update([
|
||||
'id' => $alert_id,
|
||||
'ticket_id' => $ticket_id,
|
||||
'status' => 'ticket_created'
|
||||
]);
|
||||
|
||||
return $ticket_id;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ticket content from alert
|
||||
*/
|
||||
private function generateTicketContent() {
|
||||
$content = "[WAZUH-ALERT]\n\n";
|
||||
$content .= sprintf(__('Alert ID: %s', 'siem-wazuh'), $this->fields['alert_id']) . "\n";
|
||||
$content .= sprintf(__('Rule ID: %s', 'siem-wazuh'), $this->fields['rule_id']) . "\n";
|
||||
$content .= sprintf(__('Rule Level: %s', 'siem-wazuh'), $this->fields['rule_level']) . "\n";
|
||||
$content .= sprintf(__('Agent: %s (%s)', 'siem-wazuh'), $this->fields['agent_name'], $this->fields['agent_ip']) . "\n";
|
||||
$content .= sprintf(__('Timestamp: %s', 'siem-wazuh'), $this->fields['timestamp']) . "\n\n";
|
||||
$content .= sprintf(__('Description: %s', 'siem-wazuh'), $this->fields['rule_description']) . "\n\n";
|
||||
|
||||
if ($this->fields['raw_data']) {
|
||||
$content .= __('Raw Alert Data:', 'siem-wazuh') . "\n";
|
||||
$content .= "```json\n";
|
||||
$content .= json_encode(json_decode($this->fields['raw_data']), JSON_PRETTY_PRINT);
|
||||
$content .= "\n```";
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get severity from rule level
|
||||
*/
|
||||
static function getSeverityFromLevel($level) {
|
||||
if ($level >= 12) return 'critical';
|
||||
if ($level >= 10) return 'high';
|
||||
if ($level >= 7) return 'medium';
|
||||
return 'low';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get priority from rule level
|
||||
*/
|
||||
static function getPriorityFromLevel($level) {
|
||||
if ($level >= 12) return 5; // Very high
|
||||
if ($level >= 10) return 4; // High
|
||||
if ($level >= 7) return 3; // Medium
|
||||
if ($level >= 5) return 2; // Low
|
||||
return 1; // Very low
|
||||
}
|
||||
|
||||
/**
|
||||
* Show alerts for a server
|
||||
*/
|
||||
static function showForServer($server_id) {
|
||||
global $DB;
|
||||
|
||||
$iterator = $DB->request([
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_alerts',
|
||||
'WHERE' => ['wazuh_server_id' => $server_id],
|
||||
'ORDER' => 'timestamp DESC',
|
||||
'LIMIT' => 50
|
||||
]);
|
||||
|
||||
echo "<div class='spaced'>";
|
||||
echo "<table class='tab_cadre_fixehov'>";
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<th>".__('Alert ID', 'siem-wazuh')."</th>";
|
||||
echo "<th>".__('Rule', 'siem-wazuh')."</th>";
|
||||
echo "<th>".__('Level', 'siem-wazuh')."</th>";
|
||||
echo "<th>".__('Agent', 'siem-wazuh')."</th>";
|
||||
echo "<th>".__('Timestamp', 'siem-wazuh')."</th>";
|
||||
echo "<th>".__('Status')."</th>";
|
||||
echo "<th>".__('Ticket')."</th>";
|
||||
echo "</tr>";
|
||||
|
||||
$statuses = self::getStatusArray();
|
||||
|
||||
foreach ($iterator as $row) {
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td><a href='".PluginSiemWazuhAlert::getFormURLWithID($row['id'])."'>".$row['alert_id']."</a></td>";
|
||||
echo "<td>".$row['rule_id']." - ".Html::clean($row['rule_description'])."</td>";
|
||||
echo "<td>".$row['rule_level']."</td>";
|
||||
echo "<td>".$row['agent_name']." (".$row['agent_ip'].")</td>";
|
||||
echo "<td>".Html::convDateTime($row['timestamp'])."</td>";
|
||||
echo "<td>".$statuses[$row['status']]."</td>";
|
||||
echo "<td>";
|
||||
if ($row['ticket_id']) {
|
||||
$ticket = new Ticket();
|
||||
if ($ticket->getFromDB($row['ticket_id'])) {
|
||||
echo $ticket->getLink();
|
||||
}
|
||||
}
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
echo "</table>";
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Cron function to sync alerts
|
||||
*/
|
||||
static function cronSyncAlerts($task = null) {
|
||||
global $DB;
|
||||
|
||||
$config = new PluginSiemWazuhConfig();
|
||||
if (!$config->getConfiguration('sync_enabled', 1)) {
|
||||
return 0; // Synchronisation désactivée
|
||||
}
|
||||
|
||||
$iterator = $DB->request([
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_servers',
|
||||
'WHERE' => ['is_active' => 1]
|
||||
]);
|
||||
|
||||
$total_processed = 0;
|
||||
|
||||
foreach ($iterator as $server_data) {
|
||||
$server = new PluginSiemWazuhServer();
|
||||
$server->fields = $server_data;
|
||||
|
||||
$result = $server->syncAlerts();
|
||||
if ($result['success']) {
|
||||
$matches = [];
|
||||
if (preg_match('/(\d+)/', $result['message'], $matches)) {
|
||||
$total_processed += (int)$matches[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($task) {
|
||||
$task->addVolume($total_processed);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return $total_processed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cron function to cleanup old alerts
|
||||
*/
|
||||
static function cronCleanupOldAlerts($task = null) {
|
||||
global $DB;
|
||||
|
||||
$config = new PluginSiemWazuhConfig();
|
||||
$retention_days = $config->getConfiguration('alert_retention_days', 90);
|
||||
|
||||
$query = "DELETE FROM glpi_plugin_siem_wazuh_alerts
|
||||
WHERE date_creation < DATE_SUB(NOW(), INTERVAL $retention_days DAY)";
|
||||
|
||||
$result = $DB->query($query);
|
||||
$deleted = $DB->affectedRows();
|
||||
|
||||
if ($task) {
|
||||
$task->addVolume($deleted);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get menu content
|
||||
*/
|
||||
static function getMenuContent() {
|
||||
$menu = [];
|
||||
if (Session::haveRight(static::$rightname, READ)) {
|
||||
$menu['title'] = self::getMenuName();
|
||||
$menu['page'] = "/plugins/siem-wazuh/front/wazuhalert.php";
|
||||
$menu['links']['search'] = "/plugins/siem-wazuh/front/wazuhalert.php";
|
||||
}
|
||||
return $menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get menu name
|
||||
*/
|
||||
static function getMenuName() {
|
||||
return self::getTypeName(Session::getPluralNumber());
|
||||
}
|
||||
}
|
393
front/wazuhconfig.php
Normal file
393
front/wazuhconfig.php
Normal file
@@ -0,0 +1,393 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* Interface de configuration du plugin
|
||||
*/
|
||||
|
||||
include ('../../../inc/includes.php');
|
||||
|
||||
// Vérification des droits
|
||||
Session::checkRight("plugin_siem_wazuh_config", READ);
|
||||
|
||||
// Vérification du plugin
|
||||
if (!Plugin::isPluginActive('siem-wazuh')) {
|
||||
Html::displayNotFoundError();
|
||||
}
|
||||
|
||||
$config = new PluginSiemWazuhConfig();
|
||||
|
||||
// Traitement du formulaire
|
||||
$config->processConfigForm();
|
||||
|
||||
// Initialisation de l'affichage
|
||||
Html::header(
|
||||
PluginSiemWazuhConfig::getTypeName(1),
|
||||
$_SERVER['PHP_SELF'],
|
||||
'tools',
|
||||
'PluginSiemWazuhConfig'
|
||||
);
|
||||
|
||||
echo "<div class='spaced'>";
|
||||
|
||||
// Affichage des onglets de configuration
|
||||
$tabs = [
|
||||
'config' => __('Configuration', 'siem-wazuh'),
|
||||
'mapping' => __('Asset Mapping', 'siem-wazuh'),
|
||||
'notifications' => __('Notifications', 'siem-wazuh'),
|
||||
'debug' => __('Debug & Logs', 'siem-wazuh')
|
||||
];
|
||||
|
||||
$active_tab = $_GET['tab'] ?? 'config';
|
||||
|
||||
echo "<div class='config-tabs'>";
|
||||
echo "<ul class='nav nav-tabs'>";
|
||||
foreach ($tabs as $tab_key => $tab_name) {
|
||||
$active_class = ($active_tab == $tab_key) ? ' active' : '';
|
||||
echo "<li class='nav-item'>";
|
||||
echo "<a class='nav-link$active_class' href='?tab=$tab_key'>$tab_name</a>";
|
||||
echo "</li>";
|
||||
}
|
||||
echo "</ul>";
|
||||
echo "</div>";
|
||||
|
||||
echo "<div class='tab-content'>";
|
||||
|
||||
switch ($active_tab) {
|
||||
case 'config':
|
||||
// Configuration générale
|
||||
$config->showConfigForm();
|
||||
break;
|
||||
|
||||
case 'mapping':
|
||||
// Configuration du mapping des assets
|
||||
echo "<div class='tab-pane active'>";
|
||||
echo "<h3>" . __('Asset Mapping Configuration', 'siem-wazuh') . "</h3>";
|
||||
showMappingConfiguration($config);
|
||||
echo "</div>";
|
||||
break;
|
||||
|
||||
case 'notifications':
|
||||
// Configuration des notifications
|
||||
echo "<div class='tab-pane active'>";
|
||||
echo "<h3>" . __('Notification Configuration', 'siem-wazuh') . "</h3>";
|
||||
showNotificationConfiguration($config);
|
||||
echo "</div>";
|
||||
break;
|
||||
|
||||
case 'debug':
|
||||
// Configuration de debug et logs
|
||||
echo "<div class='tab-pane active'>";
|
||||
echo "<h3>" . __('Debug & Logs Configuration', 'siem-wazuh') . "</h3>";
|
||||
showDebugConfiguration($config);
|
||||
showRecentLogs();
|
||||
echo "</div>";
|
||||
break;
|
||||
}
|
||||
|
||||
echo "</div>";
|
||||
echo "</div>";
|
||||
|
||||
/**
|
||||
* Show mapping configuration
|
||||
*/
|
||||
function showMappingConfiguration($config) {
|
||||
echo "<form method='post' action=''>";
|
||||
echo "<table class='tab_cadre_fixe'>";
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<th colspan='4'>" . __('Asset Detection Rules', 'siem-wazuh') . "</th>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Match computers by hostname', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("match_computers_hostname", $config->getConfiguration('match_computers_hostname', 1));
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Match network equipment by hostname', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("match_netequip_hostname", $config->getConfiguration('match_netequip_hostname', 1));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Match by IP address', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("match_by_ip", $config->getConfiguration('match_by_ip', 1));
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Case sensitive matching', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("case_sensitive_matching", $config->getConfiguration('case_sensitive_matching', 0));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Hostname patterns to ignore', 'siem-wazuh') . "</td>";
|
||||
echo "<td colspan='3'>";
|
||||
echo "<textarea name='ignore_hostname_patterns' rows='3' cols='80' placeholder='localhost,127.0.0.1,*.local'>";
|
||||
echo $config->getConfiguration('ignore_hostname_patterns', '');
|
||||
echo "</textarea>";
|
||||
echo "<br><small>" . __('One pattern per line. Use * as wildcard.', 'siem-wazuh') . "</small>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<td class='center' colspan='4'>";
|
||||
echo "<input type='submit' name='update_mapping' value='" . _sx('button', 'Save') . "' class='submit'>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "</table>";
|
||||
echo Html::closeForm();
|
||||
|
||||
// Test de correspondance
|
||||
echo "<br>";
|
||||
echo "<table class='tab_cadre_fixe'>";
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<th colspan='2'>" . __('Test Asset Mapping', 'siem-wazuh') . "</th>";
|
||||
echo "</tr>";
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Agent name or IP', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
echo "<input type='text' name='test_agent' id='test_agent' size='30' placeholder='agent-hostname or 192.168.1.100'>";
|
||||
echo " <button type='button' onclick='testAssetMapping()' class='btn btn-primary'>" . __('Test', 'siem-wazuh') . "</button>";
|
||||
echo "<div id='mapping_result' style='margin-top: 10px;'></div>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
echo "</table>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Show notification configuration
|
||||
*/
|
||||
function showNotificationConfiguration($config) {
|
||||
echo "<form method='post' action=''>";
|
||||
echo "<table class='tab_cadre_fixe'>";
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<th colspan='4'>" . __('Email Notifications', 'siem-wazuh') . "</th>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Enable email notifications', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("email_notifications", $config->getConfiguration('email_notifications', 1));
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Notification for critical alerts only', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("notify_critical_only", $config->getConfiguration('notify_critical_only', 0));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Default notification recipients', 'siem-wazuh') . "</td>";
|
||||
echo "<td colspan='3'>";
|
||||
echo "<input type='text' name='notification_recipients' size='80' value='" . $config->getConfiguration('notification_recipients', '') . "' placeholder='admin@domain.com, security@domain.com'>";
|
||||
echo "<br><small>" . __('Comma-separated email addresses', 'siem-wazuh') . "</small>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<td class='center' colspan='4'>";
|
||||
echo "<input type='submit' name='update_notifications' value='" . _sx('button', 'Save') . "' class='submit'>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "</table>";
|
||||
echo Html::closeForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show debug configuration
|
||||
*/
|
||||
function showDebugConfiguration($config) {
|
||||
echo "<form method='post' action=''>";
|
||||
echo "<table class='tab_cadre_fixe'>";
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<th colspan='4'>" . __('Debug Configuration', 'siem-wazuh') . "</th>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Enable debug mode', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("debug_mode", $config->getConfiguration('debug_mode', 0));
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Log API requests', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("log_api_requests", $config->getConfiguration('log_api_requests', 0));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Log level', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
$log_levels = [
|
||||
'error' => __('Error only', 'siem-wazuh'),
|
||||
'warning' => __('Warning and above', 'siem-wazuh'),
|
||||
'info' => __('Info and above', 'siem-wazuh'),
|
||||
'debug' => __('All messages', 'siem-wazuh')
|
||||
];
|
||||
Dropdown::showFromArray('log_level', $log_levels, [
|
||||
'value' => $config->getConfiguration('log_level', 'info')
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Keep logs for (days)', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showNumber("log_retention_days", [
|
||||
'value' => $config->getConfiguration('log_retention_days', 30),
|
||||
'min' => 1,
|
||||
'max' => 365
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<td class='center' colspan='2'>";
|
||||
echo "<input type='submit' name='update_debug' value='" . _sx('button', 'Save') . "' class='submit'>";
|
||||
echo "</td>";
|
||||
echo "<td class='center' colspan='2'>";
|
||||
echo "<button type='button' onclick='clearLogs()' class='btn btn-warning'>" . __('Clear All Logs', 'siem-wazuh') . "</button>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "</table>";
|
||||
echo Html::closeForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show recent logs
|
||||
*/
|
||||
function showRecentLogs() {
|
||||
global $DB;
|
||||
|
||||
echo "<br>";
|
||||
echo "<table class='tab_cadre_fixehov'>";
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<th colspan='4'>" . __('Recent Logs', 'siem-wazuh') . " (100 dernières entrées)</th>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<th>" . __('Date') . "</th>";
|
||||
echo "<th>" . __('Level') . "</th>";
|
||||
echo "<th>" . __('Server') . "</th>";
|
||||
echo "<th>" . __('Message') . "</th>";
|
||||
echo "</tr>";
|
||||
|
||||
$iterator = $DB->request([
|
||||
'SELECT' => [
|
||||
'glpi_plugin_siem_wazuh_logs.*',
|
||||
'glpi_plugin_siem_wazuh_servers.name AS server_name'
|
||||
],
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_logs',
|
||||
'LEFT JOIN' => [
|
||||
'glpi_plugin_siem_wazuh_servers' => [
|
||||
'ON' => [
|
||||
'glpi_plugin_siem_wazuh_logs' => 'wazuh_server_id',
|
||||
'glpi_plugin_siem_wazuh_servers' => 'id'
|
||||
]
|
||||
]
|
||||
],
|
||||
'ORDER' => 'date_creation DESC',
|
||||
'LIMIT' => 100
|
||||
]);
|
||||
|
||||
if (count($iterator) == 0) {
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td colspan='4' class='center'>" . __('No logs found', 'siem-wazuh') . "</td>";
|
||||
echo "</tr>";
|
||||
} else {
|
||||
foreach ($iterator as $log) {
|
||||
$level_class = 'log-' . $log['level'];
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . Html::convDateTime($log['date_creation']) . "</td>";
|
||||
echo "<td><span class='badge $level_class'>" . ucfirst($log['level']) . "</span></td>";
|
||||
echo "<td>" . ($log['server_name'] ?: __('System', 'siem-wazuh')) . "</td>";
|
||||
echo "<td>" . Html::clean($log['message']) . "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
}
|
||||
|
||||
echo "</table>";
|
||||
}
|
||||
|
||||
// JavaScript pour les fonctionnalités interactives
|
||||
echo "<script>
|
||||
function testAssetMapping() {
|
||||
var agentValue = document.getElementById('test_agent').value;
|
||||
if (!agentValue) {
|
||||
alert('" . __('Please enter an agent name or IP address', 'siem-wazuh') . "');
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('" . Plugin::getWebDir('siem-wazuh') . "/ajax/test_mapping.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: 'agent=' + encodeURIComponent(agentValue)
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
var resultDiv = document.getElementById('mapping_result');
|
||||
if (data.success) {
|
||||
resultDiv.innerHTML = '<div class=\"alert alert-success\">' + data.message + '</div>';
|
||||
} else {
|
||||
resultDiv.innerHTML = '<div class=\"alert alert-info\">' + data.message + '</div>';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
document.getElementById('mapping_result').innerHTML = '<div class=\"alert alert-danger\">" . __('Test failed', 'siem-wazuh') . "</div>';
|
||||
});
|
||||
}
|
||||
|
||||
function clearLogs() {
|
||||
if (confirm('" . __('Are you sure you want to clear all logs?', 'siem-wazuh') . "')) {
|
||||
fetch('" . Plugin::getWebDir('siem-wazuh') . "/ajax/clear_logs.php', {
|
||||
method: 'POST'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert('" . __('Logs cleared successfully', 'siem-wazuh') . "');
|
||||
location.reload();
|
||||
} else {
|
||||
alert('" . __('Failed to clear logs', 'siem-wazuh') . "');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>";
|
||||
|
||||
// CSS pour les logs et badges
|
||||
echo "<style>
|
||||
.config-tabs .nav-tabs {
|
||||
border-bottom: 1px solid #ddd;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.config-tabs .nav-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.config-tabs .nav-link {
|
||||
display: block;
|
||||
padding: 10px 15px;
|
||||
text-decoration: none;
|
||||
color: #337ab7;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px 4px 0 0;
|
||||
margin-right: 2px;
|
||||
}
|
||||
.config-tabs .nav-link:hover,
|
||||
.config-tabs .nav-link.active {
|
||||
background-color: #337ab7;
|
||||
color: white;
|
||||
border-color: #337ab7;
|
||||
}
|
||||
.log-debug { background-color: #d1ecf1; color: #0c5460; }
|
||||
.log-info { background-color: #bee5eb; color: #0c5460; }
|
||||
.log-warning { background-color: #fff3cd; color: #856404; }
|
||||
.log-error { background-color: #f8d7da; color: #721c24; }
|
||||
.log-critical { background-color: #f5c6cb; color: #721c24; }
|
||||
.alert { padding: 10px; margin: 10px 0; border-radius: 4px; }
|
||||
.alert-success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
|
||||
.alert-danger { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
|
||||
.alert-info { background-color: #d1ecf1; color: #0c5460; border: 1px solid #bee5eb; }
|
||||
</style>";
|
||||
|
||||
Html::footer();
|
102
front/wazuhserver.form.php
Normal file
102
front/wazuhserver.form.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* Formulaire de gestion des serveurs Wazuh
|
||||
*/
|
||||
|
||||
include ('../../../inc/includes.php');
|
||||
|
||||
// Vérification des droits
|
||||
Session::checkRight("plugin_siem_wazuh_server", READ);
|
||||
|
||||
// Vérification du plugin
|
||||
if (!Plugin::isPluginActive('siem-wazuh')) {
|
||||
Html::displayNotFoundError();
|
||||
}
|
||||
|
||||
$server = new PluginSiemWazuhServer();
|
||||
|
||||
// Traitement des actions du formulaire
|
||||
if (isset($_POST["add"])) {
|
||||
$server->check(-1, CREATE, $_POST);
|
||||
if ($newID = $server->add($_POST)) {
|
||||
Event::log(
|
||||
$newID,
|
||||
"PluginSiemWazuhServer",
|
||||
4,
|
||||
"inventory",
|
||||
sprintf(__('%1$s adds the item %2$s'), $_SESSION["glpiname"], $_POST["name"])
|
||||
);
|
||||
if ($_SESSION['glpibackcreated']) {
|
||||
Html::redirect($server->getLinkURL());
|
||||
}
|
||||
}
|
||||
Html::back();
|
||||
|
||||
} elseif (isset($_POST["delete"])) {
|
||||
$server->check($_POST["id"], DELETE);
|
||||
if ($server->delete($_POST)) {
|
||||
Event::log(
|
||||
$_POST["id"],
|
||||
"PluginSiemWazuhServer",
|
||||
4,
|
||||
"inventory",
|
||||
sprintf(__('%s deletes an item'), $_SESSION["glpiname"])
|
||||
);
|
||||
}
|
||||
$server->redirectToList();
|
||||
|
||||
} elseif (isset($_POST["restore"])) {
|
||||
$server->check($_POST["id"], DELETE);
|
||||
if ($server->restore($_POST)) {
|
||||
Event::log(
|
||||
$_POST["id"],
|
||||
"PluginSiemWazuhServer",
|
||||
4,
|
||||
"inventory",
|
||||
sprintf(__('%s restores an item'), $_SESSION["glpiname"])
|
||||
);
|
||||
}
|
||||
$server->redirectToList();
|
||||
|
||||
} elseif (isset($_POST["purge"])) {
|
||||
$server->check($_POST["id"], PURGE);
|
||||
if ($server->delete($_POST, 1)) {
|
||||
Event::log(
|
||||
$_POST["id"],
|
||||
"PluginSiemWazuhServer",
|
||||
4,
|
||||
"inventory",
|
||||
sprintf(__('%s purges an item'), $_SESSION["glpiname"])
|
||||
);
|
||||
}
|
||||
$server->redirectToList();
|
||||
|
||||
} elseif (isset($_POST["update"])) {
|
||||
$server->check($_POST["id"], UPDATE);
|
||||
if ($server->update($_POST)) {
|
||||
Event::log(
|
||||
$_POST["id"],
|
||||
"PluginSiemWazuhServer",
|
||||
4,
|
||||
"inventory",
|
||||
sprintf(__('%s updates an item'), $_SESSION["glpiname"])
|
||||
);
|
||||
}
|
||||
Html::back();
|
||||
|
||||
} else {
|
||||
// Affichage du formulaire
|
||||
$menus = [
|
||||
'admin' => 'PluginSiemWazuhServer',
|
||||
'PluginSiemWazuhServer' => 'PluginSiemWazuhServer'
|
||||
];
|
||||
|
||||
PluginSiemWazuhServer::displayFullPageForItem(
|
||||
$_GET["id"],
|
||||
$menus,
|
||||
[
|
||||
'formoptions' => "method='post' enctype='multipart/form-data'"
|
||||
]
|
||||
);
|
||||
}
|
172
front/wazuhserver.php
Normal file
172
front/wazuhserver.php
Normal file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* Interface de gestion des serveurs Wazuh
|
||||
*/
|
||||
|
||||
include ('../../../inc/includes.php');
|
||||
|
||||
// Vérification des droits
|
||||
Session::checkRight("plugin_siem_wazuh_server", READ);
|
||||
|
||||
// Vérification du plugin
|
||||
if (!Plugin::isPluginActive('siem-wazuh')) {
|
||||
Html::displayNotFoundError();
|
||||
}
|
||||
|
||||
// Initialisation de l'affichage
|
||||
Html::header(
|
||||
PluginSiemWazuhServer::getTypeName(Session::getPluralNumber()),
|
||||
$_SERVER['PHP_SELF'],
|
||||
'admin',
|
||||
'PluginSiemWazuhServer'
|
||||
);
|
||||
|
||||
// Gestion des actions
|
||||
if (isset($_GET['action'])) {
|
||||
$server = new PluginSiemWazuhServer();
|
||||
|
||||
switch ($_GET['action']) {
|
||||
case 'test_connection':
|
||||
if (isset($_GET['id']) && $server->getFromDB($_GET['id'])) {
|
||||
$result = $server->testConnection();
|
||||
echo json_encode($result);
|
||||
exit;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'sync_alerts':
|
||||
if (isset($_GET['id']) && $server->getFromDB($_GET['id'])) {
|
||||
$result = $server->syncAlerts();
|
||||
echo json_encode($result);
|
||||
exit;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'toggle_active':
|
||||
if (isset($_GET['id']) && $server->getFromDB($_GET['id'])) {
|
||||
if (Session::haveRight("plugin_siem_wazuh_server", UPDATE)) {
|
||||
$new_status = $server->fields['is_active'] ? 0 : 1;
|
||||
$server->update([
|
||||
'id' => $_GET['id'],
|
||||
'is_active' => $new_status
|
||||
]);
|
||||
Session::addMessageAfterRedirect(
|
||||
$new_status ? __('Server activated', 'siem-wazuh') : __('Server deactivated', 'siem-wazuh')
|
||||
);
|
||||
}
|
||||
}
|
||||
Html::back();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialisation de la recherche
|
||||
$search = Search::show('PluginSiemWazuhServer');
|
||||
|
||||
// Ajout de CSS pour l'interface
|
||||
echo "<style>
|
||||
.server-status {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.server-status.active {
|
||||
background-color: #5cb85c;
|
||||
color: white;
|
||||
}
|
||||
.server-status.inactive {
|
||||
background-color: #d9534f;
|
||||
color: white;
|
||||
}
|
||||
.sync-status {
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
}
|
||||
.action-buttons {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.action-buttons .btn {
|
||||
margin: 0 2px;
|
||||
padding: 2px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
</style>";
|
||||
|
||||
// Ajout de JavaScript pour les actions AJAX
|
||||
echo "<script>
|
||||
function testWazuhConnection(serverId) {
|
||||
var button = document.querySelector('.test-connection-' + serverId);
|
||||
var resultDiv = document.getElementById('test-result-' + serverId);
|
||||
|
||||
if (button) {
|
||||
button.disabled = true;
|
||||
button.innerHTML = '" . __('Testing...', 'siem-wazuh') . "';
|
||||
}
|
||||
|
||||
fetch('?action=test_connection&id=' + serverId)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (resultDiv) {
|
||||
if (data.success) {
|
||||
resultDiv.innerHTML = '<div class=\"alert alert-success\">' + data.message + '</div>';
|
||||
} else {
|
||||
resultDiv.innerHTML = '<div class=\"alert alert-danger\">' + data.message + '</div>';
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
if (resultDiv) {
|
||||
resultDiv.innerHTML = '<div class=\"alert alert-danger\">" . __('Connection test failed', 'siem-wazuh') . "</div>';
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (button) {
|
||||
button.disabled = false;
|
||||
button.innerHTML = '" . __('Test Connection', 'siem-wazuh') . "';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function syncWazuhAlerts(serverId) {
|
||||
if (!confirm('" . __('Are you sure you want to synchronize alerts from this server?', 'siem-wazuh') . "')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var button = document.querySelector('.sync-alerts-' + serverId);
|
||||
if (button) {
|
||||
button.disabled = true;
|
||||
button.innerHTML = '" . __('Synchronizing...', 'siem-wazuh') . "';
|
||||
}
|
||||
|
||||
fetch('?action=sync_alerts&id=' + serverId)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert(data.message);
|
||||
location.reload();
|
||||
} else {
|
||||
alert('" . __('Sync failed:', 'siem-wazuh') . " ' + data.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('" . __('An error occurred during synchronization', 'siem-wazuh') . "');
|
||||
})
|
||||
.finally(() => {
|
||||
if (button) {
|
||||
button.disabled = false;
|
||||
button.innerHTML = '" . __('Sync Alerts', 'siem-wazuh') . "';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function toggleServerStatus(serverId) {
|
||||
if (confirm('" . __('Are you sure you want to change the status of this server?', 'siem-wazuh') . "')) {
|
||||
window.location.href = '?action=toggle_active&id=' + serverId;
|
||||
}
|
||||
}
|
||||
</script>";
|
||||
|
||||
Html::footer();
|
287
hook.php
Normal file
287
hook.php
Normal file
@@ -0,0 +1,287 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* Hook file for installation/uninstallation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Plugin install process
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
function plugin_siem_wazuh_install() {
|
||||
global $DB;
|
||||
|
||||
$version = plugin_version_siem_wazuh();
|
||||
|
||||
// Lecture du fichier SQL d'installation
|
||||
$sql_file = GLPI_ROOT . "/plugins/siem-wazuh/sql/install.sql";
|
||||
|
||||
if (!file_exists($sql_file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sql_content = file_get_contents($sql_file);
|
||||
$queries = explode(';', $sql_content);
|
||||
|
||||
foreach ($queries as $query) {
|
||||
$query = trim($query);
|
||||
if (!empty($query)) {
|
||||
$DB->queryOrDie($query, "Erreur lors de l'installation du plugin SIEM-Wazuh : " . $DB->error());
|
||||
}
|
||||
}
|
||||
|
||||
// Création des droits par défaut
|
||||
plugin_siem_wazuh_create_default_rights();
|
||||
|
||||
// Création de la tâche cron
|
||||
plugin_siem_wazuh_create_cron_tasks();
|
||||
|
||||
// Configuration par défaut
|
||||
plugin_siem_wazuh_create_default_config();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin uninstall process
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
function plugin_siem_wazuh_uninstall() {
|
||||
global $DB;
|
||||
|
||||
// Lecture du fichier SQL de désinstallation
|
||||
$sql_file = GLPI_ROOT . "/plugins/siem-wazuh/sql/uninstall.sql";
|
||||
|
||||
if (file_exists($sql_file)) {
|
||||
$sql_content = file_get_contents($sql_file);
|
||||
$queries = explode(';', $sql_content);
|
||||
|
||||
foreach ($queries as $query) {
|
||||
$query = trim($query);
|
||||
if (!empty($query)) {
|
||||
$DB->queryOrDie($query, "Erreur lors de la désinstallation du plugin SIEM-Wazuh : " . $DB->error());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Suppression des tâches cron
|
||||
plugin_siem_wazuh_remove_cron_tasks();
|
||||
|
||||
// Suppression des droits
|
||||
plugin_siem_wazuh_remove_rights();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin update process
|
||||
*
|
||||
* @param string $current_version
|
||||
* @return boolean
|
||||
*/
|
||||
function plugin_siem_wazuh_update($current_version) {
|
||||
global $DB;
|
||||
|
||||
// Mise à jour de la version 1.0.0
|
||||
if (version_compare($current_version, '1.0.0', '<')) {
|
||||
// Ajout de nouvelles colonnes si nécessaire
|
||||
$migration = new Migration(PLUGIN_SIEM_WAZUH_VERSION);
|
||||
|
||||
// Exemple de migration
|
||||
if (!$DB->fieldExists('glpi_plugin_siem_wazuh_servers', 'ticket_category')) {
|
||||
$migration->addField('glpi_plugin_siem_wazuh_servers', 'ticket_category', 'int(11) DEFAULT NULL');
|
||||
}
|
||||
|
||||
$migration->executeMigration();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default rights
|
||||
*/
|
||||
function plugin_siem_wazuh_create_default_rights() {
|
||||
global $DB;
|
||||
|
||||
$rights = [
|
||||
'plugin_siem_wazuh_config' => ['name' => __('SIEM Wazuh Configuration', 'siem-wazuh')],
|
||||
'plugin_siem_wazuh_server' => ['name' => __('Wazuh Servers', 'siem-wazuh')],
|
||||
'plugin_siem_wazuh_alert' => ['name' => __('Wazuh Alerts', 'siem-wazuh')]
|
||||
];
|
||||
|
||||
foreach ($rights as $right => $data) {
|
||||
// Ajout du droit s'il n'existe pas
|
||||
$query = "SELECT id FROM glpi_profilerights WHERE name = '$right'";
|
||||
$result = $DB->query($query);
|
||||
|
||||
if ($DB->numrows($result) == 0) {
|
||||
$query = "INSERT INTO glpi_profilerights (profiles_id, name, rights)
|
||||
SELECT id, '$right', '0' FROM glpi_profiles";
|
||||
$DB->query($query);
|
||||
|
||||
// Attribution des droits au profil Super-Admin
|
||||
$query = "UPDATE glpi_profilerights SET rights = '" . (READ | UPDATE | CREATE | DELETE | PURGE) . "'
|
||||
WHERE name = '$right' AND profiles_id IN (
|
||||
SELECT id FROM glpi_profiles WHERE name = 'Super-Admin'
|
||||
)";
|
||||
$DB->query($query);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove rights
|
||||
*/
|
||||
function plugin_siem_wazuh_remove_rights() {
|
||||
global $DB;
|
||||
|
||||
$rights = [
|
||||
'plugin_siem_wazuh_config',
|
||||
'plugin_siem_wazuh_server',
|
||||
'plugin_siem_wazuh_alert'
|
||||
];
|
||||
|
||||
foreach ($rights as $right) {
|
||||
$query = "DELETE FROM glpi_profilerights WHERE name = '$right'";
|
||||
$DB->query($query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create cron tasks
|
||||
*/
|
||||
function plugin_siem_wazuh_create_cron_tasks() {
|
||||
CronTask::Register('PluginSiemWazuhAlert', 'sync_alerts', 5 * MINUTE_TIMESTAMP, [
|
||||
'comment' => __('Synchronize Wazuh alerts', 'siem-wazuh'),
|
||||
'mode' => CronTask::MODE_EXTERNAL
|
||||
]);
|
||||
|
||||
CronTask::Register('PluginSiemWazuhAlert', 'cleanup_old_alerts', DAY_TIMESTAMP, [
|
||||
'comment' => __('Cleanup old alerts', 'siem-wazuh'),
|
||||
'mode' => CronTask::MODE_EXTERNAL
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove cron tasks
|
||||
*/
|
||||
function plugin_siem_wazuh_remove_cron_tasks() {
|
||||
global $DB;
|
||||
|
||||
$query = "DELETE FROM glpi_crontasks WHERE itemtype LIKE 'PluginSiemWazuh%'";
|
||||
$DB->query($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default configuration
|
||||
*/
|
||||
function plugin_siem_wazuh_create_default_config() {
|
||||
$config = new PluginSiemWazuhConfig();
|
||||
|
||||
$default_configs = [
|
||||
'auto_create_ticket' => '1',
|
||||
'default_ticket_priority' => '3',
|
||||
'alert_retention_days' => '90',
|
||||
'sync_enabled' => '1',
|
||||
'max_alerts_per_sync' => '100',
|
||||
'notification_enabled' => '1'
|
||||
];
|
||||
|
||||
foreach ($default_configs as $name => $value) {
|
||||
$config->setConfiguration($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database relations
|
||||
*/
|
||||
function plugin_siem_wazuh_getDatabaseRelations() {
|
||||
$plugin_relations = [];
|
||||
|
||||
$plugin_relations["glpi_plugin_siem_wazuh_alerts"] = [
|
||||
"glpi_computers" => "computer_id",
|
||||
"glpi_networkequipments" => "networkequipment_id",
|
||||
"glpi_tickets" => "ticket_id"
|
||||
];
|
||||
|
||||
return $plugin_relations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define dropdown relations
|
||||
*/
|
||||
function plugin_siem_wazuh_getDropdown() {
|
||||
return [
|
||||
'PluginSiemWazuhServer' => __('Wazuh Servers', 'siem-wazuh')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for profile changes
|
||||
*/
|
||||
function plugin_siem_wazuh_profile_form($prof) {
|
||||
global $DB;
|
||||
|
||||
if ($prof->getID()
|
||||
&& Session::haveRight("profile", READ)) {
|
||||
|
||||
$prof_id = $prof->getID();
|
||||
|
||||
$query = "SELECT * FROM glpi_plugin_siem_wazuh_profiles WHERE profiles_id = '$prof_id'";
|
||||
$result = $DB->query($query);
|
||||
|
||||
if ($DB->numrows($result)) {
|
||||
$rights = $DB->fetchAssoc($result);
|
||||
} else {
|
||||
$rights = [
|
||||
'wazuh_config' => '',
|
||||
'wazuh_server' => '',
|
||||
'wazuh_alert' => ''
|
||||
];
|
||||
}
|
||||
|
||||
echo "<div class='spaced-form'>";
|
||||
echo "<table class='tab_cadre_fixehov'>";
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<th colspan='2'>" . __('SIEM Wazuh Rights', 'siem-wazuh') . "</th>";
|
||||
echo "</tr>";
|
||||
|
||||
$right_names = [
|
||||
'wazuh_config' => __('Configuration', 'siem-wazuh'),
|
||||
'wazuh_server' => __('Servers', 'siem-wazuh'),
|
||||
'wazuh_alert' => __('Alerts', 'siem-wazuh')
|
||||
];
|
||||
|
||||
foreach ($right_names as $field => $label) {
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<td>$label</td>";
|
||||
echo "<td>";
|
||||
Profile::dropdownNoneReadWrite($field, $rights[$field], 1, 1, 1);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
echo "</table>";
|
||||
echo "</div>";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save profile rights
|
||||
*/
|
||||
function plugin_siem_wazuh_profile_save($prof) {
|
||||
global $DB;
|
||||
|
||||
$prof_id = $prof->getID();
|
||||
|
||||
if (isset($_POST['wazuh_config']) || isset($_POST['wazuh_server']) || isset($_POST['wazuh_alert'])) {
|
||||
$query = "REPLACE INTO glpi_plugin_siem_wazuh_profiles
|
||||
(profiles_id, wazuh_config, wazuh_server, wazuh_alert)
|
||||
VALUES ('$prof_id', '" . $_POST['wazuh_config'] . "',
|
||||
'" . $_POST['wazuh_server'] . "', '" . $_POST['wazuh_alert'] . "')";
|
||||
$DB->query($query);
|
||||
}
|
||||
}
|
706
inc/wazuhalert.class.php
Normal file
706
inc/wazuhalert.class.php
Normal file
@@ -0,0 +1,706 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* Classe de gestion des alertes Wazuh
|
||||
*/
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
die("Sorry. You can't access this file directly");
|
||||
}
|
||||
|
||||
class PluginSiemWazuhAlert extends CommonDBTM {
|
||||
|
||||
public $dohistory = true;
|
||||
|
||||
static $rightname = 'plugin_siem_wazuh_alert';
|
||||
|
||||
const MATRIX_FIELD = '';
|
||||
const SEARCHOPTIONS_INHERITANCE = true;
|
||||
|
||||
/**
|
||||
* Return the localized name of the current Type
|
||||
*/
|
||||
static function getTypeName($nb = 0) {
|
||||
return _n('Wazuh Alert', 'Wazuh Alerts', $nb, 'siem-wazuh');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define search options
|
||||
*/
|
||||
function rawSearchOptions() {
|
||||
$tab = [];
|
||||
|
||||
$tab[] = [
|
||||
'id' => 'common',
|
||||
'name' => __('Characteristics')
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '1',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'alert_id',
|
||||
'name' => __('Alert ID', 'siem-wazuh'),
|
||||
'datatype' => 'itemlink',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '2',
|
||||
'table' => 'glpi_plugin_siem_wazuh_servers',
|
||||
'field' => 'name',
|
||||
'name' => __('Wazuh Server', 'siem-wazuh'),
|
||||
'datatype' => 'dropdown',
|
||||
'massiveaction' => false,
|
||||
'linkfield' => 'wazuh_server_id'
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '3',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'rule_id',
|
||||
'name' => __('Rule ID', 'siem-wazuh'),
|
||||
'datatype' => 'integer',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '4',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'rule_level',
|
||||
'name' => __('Rule Level', 'siem-wazuh'),
|
||||
'datatype' => 'integer',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '5',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'rule_description',
|
||||
'name' => __('Rule Description', 'siem-wazuh'),
|
||||
'datatype' => 'text',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '6',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'agent_name',
|
||||
'name' => __('Agent Name', 'siem-wazuh'),
|
||||
'datatype' => 'string',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '7',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'agent_ip',
|
||||
'name' => __('Agent IP', 'siem-wazuh'),
|
||||
'datatype' => 'string',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '8',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'timestamp',
|
||||
'name' => __('Alert Timestamp', 'siem-wazuh'),
|
||||
'datatype' => 'datetime',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '9',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'status',
|
||||
'name' => __('Status'),
|
||||
'datatype' => 'specific',
|
||||
'searchtype' => 'equals',
|
||||
'massiveaction' => true
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '10',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'severity',
|
||||
'name' => __('Severity', 'siem-wazuh'),
|
||||
'datatype' => 'specific',
|
||||
'searchtype' => 'equals',
|
||||
'massiveaction' => true
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '11',
|
||||
'table' => 'glpi_computers',
|
||||
'field' => 'name',
|
||||
'name' => __('Associated Computer', 'siem-wazuh'),
|
||||
'datatype' => 'dropdown',
|
||||
'massiveaction' => false,
|
||||
'linkfield' => 'computer_id'
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '12',
|
||||
'table' => 'glpi_networkequipments',
|
||||
'field' => 'name',
|
||||
'name' => __('Associated Network Equipment', 'siem-wazuh'),
|
||||
'datatype' => 'dropdown',
|
||||
'massiveaction' => false,
|
||||
'linkfield' => 'networkequipment_id'
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '13',
|
||||
'table' => 'glpi_tickets',
|
||||
'field' => 'name',
|
||||
'name' => __('Associated Ticket', 'siem-wazuh'),
|
||||
'datatype' => 'dropdown',
|
||||
'massiveaction' => false,
|
||||
'linkfield' => 'ticket_id'
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '30',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'date_creation',
|
||||
'name' => __('Creation date'),
|
||||
'datatype' => 'datetime',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '31',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'date_mod',
|
||||
'name' => __('Last update'),
|
||||
'datatype' => 'datetime',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
return $tab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display specific values
|
||||
*/
|
||||
static function getSpecificValueToDisplay($field, $values, array $options = []) {
|
||||
if (!is_array($values)) {
|
||||
$values = [$field => $values];
|
||||
}
|
||||
|
||||
switch ($field) {
|
||||
case 'status':
|
||||
$statuses = self::getStatusArray();
|
||||
return $statuses[$values[$field]] ?? $values[$field];
|
||||
|
||||
case 'severity':
|
||||
$severities = self::getSeverityArray();
|
||||
return $severities[$values[$field]] ?? $values[$field];
|
||||
}
|
||||
|
||||
return parent::getSpecificValueToDisplay($field, $values, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status array
|
||||
*/
|
||||
static function getStatusArray() {
|
||||
return [
|
||||
'new' => __('New', 'siem-wazuh'),
|
||||
'processed' => __('Processed', 'siem-wazuh'),
|
||||
'ignored' => __('Ignored', 'siem-wazuh'),
|
||||
'ticket_created' => __('Ticket Created', 'siem-wazuh')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get severity array
|
||||
*/
|
||||
static function getSeverityArray() {
|
||||
return [
|
||||
'low' => __('Low', 'siem-wazuh'),
|
||||
'medium' => __('Medium', 'siem-wazuh'),
|
||||
'high' => __('High', 'siem-wazuh'),
|
||||
'critical' => __('Critical', 'siem-wazuh')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Display form
|
||||
*/
|
||||
function showForm($ID, $options = []) {
|
||||
$this->initForm($ID, $options);
|
||||
$this->showFormHeader($options);
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Alert ID', 'siem-wazuh')."</td>";
|
||||
echo "<td>".$this->fields["alert_id"]."</td>";
|
||||
echo "<td>".__('Rule ID', 'siem-wazuh')."</td>";
|
||||
echo "<td>".$this->fields["rule_id"]."</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Rule Level', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
echo $this->fields["rule_level"];
|
||||
if ($this->fields["rule_level"]) {
|
||||
$severity = self::getSeverityFromLevel($this->fields["rule_level"]);
|
||||
$severities = self::getSeverityArray();
|
||||
echo " <span class='badge severity-$severity'>".$severities[$severity]."</span>";
|
||||
}
|
||||
echo "</td>";
|
||||
echo "<td>".__('Agent Name', 'siem-wazuh')."</td>";
|
||||
echo "<td>".$this->fields["agent_name"]."</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Agent IP', 'siem-wazuh')."</td>";
|
||||
echo "<td>".$this->fields["agent_ip"]."</td>";
|
||||
echo "<td>".__('Timestamp', 'siem-wazuh')."</td>";
|
||||
echo "<td>".Html::convDateTime($this->fields["timestamp"])."</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Status')."</td>";
|
||||
echo "<td>";
|
||||
$statuses = self::getStatusArray();
|
||||
Dropdown::showFromArray('status', $statuses, ['value' => $this->fields["status"]]);
|
||||
echo "</td>";
|
||||
echo "<td>".__('Severity', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
$severities = self::getSeverityArray();
|
||||
Dropdown::showFromArray('severity', $severities, ['value' => $this->fields["severity"]]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Rule Description', 'siem-wazuh')."</td>";
|
||||
echo "<td colspan='3'>";
|
||||
echo nl2br(Html::entities_deep($this->fields["rule_description"]));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
// Affichage des associations
|
||||
if ($this->fields["computer_id"]) {
|
||||
$computer = new Computer();
|
||||
$computer->getFromDB($this->fields["computer_id"]);
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Associated Computer')."</td>";
|
||||
echo "<td>";
|
||||
echo $computer->getLink();
|
||||
echo "</td>";
|
||||
echo "<td></td><td></td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
if ($this->fields["networkequipment_id"]) {
|
||||
$netequip = new NetworkEquipment();
|
||||
$netequip->getFromDB($this->fields["networkequipment_id"]);
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Associated Network Equipment')."</td>";
|
||||
echo "<td>";
|
||||
echo $netequip->getLink();
|
||||
echo "</td>";
|
||||
echo "<td></td><td></td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
if ($this->fields["ticket_id"]) {
|
||||
$ticket = new Ticket();
|
||||
$ticket->getFromDB($this->fields["ticket_id"]);
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Associated Ticket')."</td>";
|
||||
echo "<td>";
|
||||
echo $ticket->getLink();
|
||||
echo "</td>";
|
||||
echo "<td></td><td></td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
// Affichage des données brutes
|
||||
if (!empty($this->fields["raw_data"])) {
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td colspan='4'>";
|
||||
echo "<details>";
|
||||
echo "<summary>".__('Raw Alert Data', 'siem-wazuh')."</summary>";
|
||||
echo "<pre class='json-data'>";
|
||||
echo json_encode(json_decode($this->fields["raw_data"]), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
echo "</pre>";
|
||||
echo "</details>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
$this->showFormButtons($options);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process alerts from Wazuh API
|
||||
*/
|
||||
function processAlerts($server_id, $alerts_data) {
|
||||
$processed = 0;
|
||||
$config = new PluginSiemWazuhConfig();
|
||||
|
||||
foreach ($alerts_data as $alert_data) {
|
||||
$alert_id = $alert_data['id'] ?? $alert_data['_id'] ?? null;
|
||||
if (!$alert_id) continue;
|
||||
|
||||
// Vérifier si l'alerte existe déjà
|
||||
if ($this->getFromDBByCrit([
|
||||
'wazuh_server_id' => $server_id,
|
||||
'alert_id' => $alert_id
|
||||
])) {
|
||||
continue; // Alerte déjà traitée
|
||||
}
|
||||
|
||||
// Extraire les informations de l'alerte
|
||||
$rule_id = $alert_data['rule']['id'] ?? null;
|
||||
$rule_level = $alert_data['rule']['level'] ?? 0;
|
||||
$rule_description = $alert_data['rule']['description'] ?? '';
|
||||
$agent_name = $alert_data['agent']['name'] ?? '';
|
||||
$agent_ip = $alert_data['agent']['ip'] ?? '';
|
||||
$timestamp = $alert_data['timestamp'] ?? $alert_data['@timestamp'] ?? date('Y-m-d H:i:s');
|
||||
|
||||
// Convertir le timestamp au format MySQL
|
||||
if (strpos($timestamp, 'T') !== false) {
|
||||
$timestamp = str_replace('T', ' ', substr($timestamp, 0, 19));
|
||||
}
|
||||
|
||||
// Déterminer la sévérité
|
||||
$severity = self::getSeverityFromLevel($rule_level);
|
||||
|
||||
// Créer l'alerte
|
||||
$alert_input = [
|
||||
'wazuh_server_id' => $server_id,
|
||||
'alert_id' => $alert_id,
|
||||
'rule_id' => $rule_id,
|
||||
'rule_level' => $rule_level,
|
||||
'rule_description' => $rule_description,
|
||||
'agent_id' => $alert_data['agent']['id'] ?? '',
|
||||
'agent_name' => $agent_name,
|
||||
'agent_ip' => $agent_ip,
|
||||
'timestamp' => $timestamp,
|
||||
'raw_data' => json_encode($alert_data),
|
||||
'severity' => $severity,
|
||||
'status' => 'new'
|
||||
];
|
||||
|
||||
// Tenter d'associer avec un asset
|
||||
$this->associateWithAsset($alert_input, $agent_name, $agent_ip);
|
||||
|
||||
if ($alert_id = $this->add($alert_input)) {
|
||||
$processed++;
|
||||
|
||||
// Créer automatiquement un ticket si configuré
|
||||
if ($config->getConfiguration('auto_create_ticket', 1)) {
|
||||
$this->createTicketFromAlert($alert_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $processed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate alert with GLPI asset
|
||||
*/
|
||||
private function associateWithAsset(&$alert_input, $agent_name, $agent_ip) {
|
||||
global $DB;
|
||||
|
||||
// Recherche par nom d'agent
|
||||
if (!empty($agent_name)) {
|
||||
// Recherche dans les ordinateurs
|
||||
$computer = $DB->request([
|
||||
'SELECT' => ['id'],
|
||||
'FROM' => 'glpi_computers',
|
||||
'WHERE' => [
|
||||
'name' => $agent_name,
|
||||
'is_deleted' => 0
|
||||
],
|
||||
'LIMIT' => 1
|
||||
])->current();
|
||||
|
||||
if ($computer) {
|
||||
$alert_input['computer_id'] = $computer['id'];
|
||||
return;
|
||||
}
|
||||
|
||||
// Recherche dans les équipements réseau
|
||||
$netequip = $DB->request([
|
||||
'SELECT' => ['id'],
|
||||
'FROM' => 'glpi_networkequipments',
|
||||
'WHERE' => [
|
||||
'name' => $agent_name,
|
||||
'is_deleted' => 0
|
||||
],
|
||||
'LIMIT' => 1
|
||||
])->current();
|
||||
|
||||
if ($netequip) {
|
||||
$alert_input['networkequipment_id'] = $netequip['id'];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Recherche par adresse IP
|
||||
if (!empty($agent_ip)) {
|
||||
// Recherche dans les ports réseau
|
||||
$port = $DB->request([
|
||||
'SELECT' => ['items_id', 'itemtype'],
|
||||
'FROM' => 'glpi_networkports',
|
||||
'INNER JOIN' => [
|
||||
'glpi_ipaddresses' => [
|
||||
'ON' => [
|
||||
'glpi_networkports' => 'id',
|
||||
'glpi_ipaddresses' => 'items_id'
|
||||
]
|
||||
]
|
||||
],
|
||||
'WHERE' => [
|
||||
'glpi_ipaddresses.name' => $agent_ip,
|
||||
'glpi_ipaddresses.itemtype' => 'NetworkPort'
|
||||
],
|
||||
'LIMIT' => 1
|
||||
])->current();
|
||||
|
||||
if ($port) {
|
||||
if ($port['itemtype'] == 'Computer') {
|
||||
$alert_input['computer_id'] = $port['items_id'];
|
||||
} elseif ($port['itemtype'] == 'NetworkEquipment') {
|
||||
$alert_input['networkequipment_id'] = $port['items_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ticket from alert
|
||||
*/
|
||||
function createTicketFromAlert($alert_id) {
|
||||
if (!$this->getFromDB($alert_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$server = new PluginSiemWazuhServer();
|
||||
if (!$server->getFromDB($this->fields['wazuh_server_id'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ticket = new Ticket();
|
||||
$config = new PluginSiemWazuhConfig();
|
||||
|
||||
// Déterminer la priorité basée sur le niveau de règle
|
||||
$priority = self::getPriorityFromLevel($this->fields['rule_level']);
|
||||
|
||||
$ticket_input = [
|
||||
'name' => sprintf(__('Wazuh Alert: %s', 'siem-wazuh'), $this->fields['rule_description']),
|
||||
'content' => $this->generateTicketContent(),
|
||||
'type' => $server->fields['ticket_type'] ?: 1,
|
||||
'itilcategories_id' => $server->fields['ticket_category'],
|
||||
'priority' => $priority,
|
||||
'urgency' => $priority,
|
||||
'impact' => $priority,
|
||||
'status' => CommonITILObject::INCOMING,
|
||||
'_add_validation' => 0
|
||||
];
|
||||
|
||||
// Associer le ticket à l'asset si trouvé
|
||||
if ($this->fields['computer_id']) {
|
||||
$ticket_input['_link']['items_id'] = $this->fields['computer_id'];
|
||||
$ticket_input['_link']['itemtype'] = 'Computer';
|
||||
} elseif ($this->fields['networkequipment_id']) {
|
||||
$ticket_input['_link']['items_id'] = $this->fields['networkequipment_id'];
|
||||
$ticket_input['_link']['itemtype'] = 'NetworkEquipment';
|
||||
}
|
||||
|
||||
$ticket_id = $ticket->add($ticket_input);
|
||||
|
||||
if ($ticket_id) {
|
||||
// Mettre à jour l'alerte
|
||||
$this->update([
|
||||
'id' => $alert_id,
|
||||
'ticket_id' => $ticket_id,
|
||||
'status' => 'ticket_created'
|
||||
]);
|
||||
|
||||
return $ticket_id;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ticket content from alert
|
||||
*/
|
||||
private function generateTicketContent() {
|
||||
$content = "[WAZUH-ALERT]\n\n";
|
||||
$content .= sprintf(__('Alert ID: %s', 'siem-wazuh'), $this->fields['alert_id']) . "\n";
|
||||
$content .= sprintf(__('Rule ID: %s', 'siem-wazuh'), $this->fields['rule_id']) . "\n";
|
||||
$content .= sprintf(__('Rule Level: %s', 'siem-wazuh'), $this->fields['rule_level']) . "\n";
|
||||
$content .= sprintf(__('Agent: %s (%s)', 'siem-wazuh'), $this->fields['agent_name'], $this->fields['agent_ip']) . "\n";
|
||||
$content .= sprintf(__('Timestamp: %s', 'siem-wazuh'), $this->fields['timestamp']) . "\n\n";
|
||||
$content .= sprintf(__('Description: %s', 'siem-wazuh'), $this->fields['rule_description']) . "\n\n";
|
||||
|
||||
if ($this->fields['raw_data']) {
|
||||
$content .= __('Raw Alert Data:', 'siem-wazuh') . "\n";
|
||||
$content .= "```json\n";
|
||||
$content .= json_encode(json_decode($this->fields['raw_data']), JSON_PRETTY_PRINT);
|
||||
$content .= "\n```";
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get severity from rule level
|
||||
*/
|
||||
static function getSeverityFromLevel($level) {
|
||||
if ($level >= 12) return 'critical';
|
||||
if ($level >= 10) return 'high';
|
||||
if ($level >= 7) return 'medium';
|
||||
return 'low';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get priority from rule level
|
||||
*/
|
||||
static function getPriorityFromLevel($level) {
|
||||
if ($level >= 12) return 5; // Very high
|
||||
if ($level >= 10) return 4; // High
|
||||
if ($level >= 7) return 3; // Medium
|
||||
if ($level >= 5) return 2; // Low
|
||||
return 1; // Very low
|
||||
}
|
||||
|
||||
/**
|
||||
* Show alerts for a server
|
||||
*/
|
||||
static function showForServer($server_id) {
|
||||
global $DB;
|
||||
|
||||
$iterator = $DB->request([
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_alerts',
|
||||
'WHERE' => ['wazuh_server_id' => $server_id],
|
||||
'ORDER' => 'timestamp DESC',
|
||||
'LIMIT' => 50
|
||||
]);
|
||||
|
||||
echo "<div class='spaced'>";
|
||||
echo "<table class='tab_cadre_fixehov'>";
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<th>".__('Alert ID', 'siem-wazuh')."</th>";
|
||||
echo "<th>".__('Rule', 'siem-wazuh')."</th>";
|
||||
echo "<th>".__('Level', 'siem-wazuh')."</th>";
|
||||
echo "<th>".__('Agent', 'siem-wazuh')."</th>";
|
||||
echo "<th>".__('Timestamp', 'siem-wazuh')."</th>";
|
||||
echo "<th>".__('Status')."</th>";
|
||||
echo "<th>".__('Ticket')."</th>";
|
||||
echo "</tr>";
|
||||
|
||||
$statuses = self::getStatusArray();
|
||||
|
||||
foreach ($iterator as $row) {
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td><a href='".PluginSiemWazuhAlert::getFormURLWithID($row['id'])."'>".$row['alert_id']."</a></td>";
|
||||
echo "<td>".$row['rule_id']." - ".Html::clean($row['rule_description'])."</td>";
|
||||
echo "<td>".$row['rule_level']."</td>";
|
||||
echo "<td>".$row['agent_name']." (".$row['agent_ip'].")</td>";
|
||||
echo "<td>".Html::convDateTime($row['timestamp'])."</td>";
|
||||
echo "<td>".$statuses[$row['status']]."</td>";
|
||||
echo "<td>";
|
||||
if ($row['ticket_id']) {
|
||||
$ticket = new Ticket();
|
||||
if ($ticket->getFromDB($row['ticket_id'])) {
|
||||
echo $ticket->getLink();
|
||||
}
|
||||
}
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
echo "</table>";
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Cron function to sync alerts
|
||||
*/
|
||||
static function cronSyncAlerts($task = null) {
|
||||
global $DB;
|
||||
|
||||
$config = new PluginSiemWazuhConfig();
|
||||
if (!$config->getConfiguration('sync_enabled', 1)) {
|
||||
return 0; // Synchronisation désactivée
|
||||
}
|
||||
|
||||
$iterator = $DB->request([
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_servers',
|
||||
'WHERE' => ['is_active' => 1]
|
||||
]);
|
||||
|
||||
$total_processed = 0;
|
||||
|
||||
foreach ($iterator as $server_data) {
|
||||
$server = new PluginSiemWazuhServer();
|
||||
$server->fields = $server_data;
|
||||
|
||||
$result = $server->syncAlerts();
|
||||
if ($result['success']) {
|
||||
$matches = [];
|
||||
if (preg_match('/(\d+)/', $result['message'], $matches)) {
|
||||
$total_processed += (int)$matches[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($task) {
|
||||
$task->addVolume($total_processed);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return $total_processed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cron function to cleanup old alerts
|
||||
*/
|
||||
static function cronCleanupOldAlerts($task = null) {
|
||||
global $DB;
|
||||
|
||||
$config = new PluginSiemWazuhConfig();
|
||||
$retention_days = $config->getConfiguration('alert_retention_days', 90);
|
||||
|
||||
$query = "DELETE FROM glpi_plugin_siem_wazuh_alerts
|
||||
WHERE date_creation < DATE_SUB(NOW(), INTERVAL $retention_days DAY)";
|
||||
|
||||
$result = $DB->query($query);
|
||||
$deleted = $DB->affectedRows();
|
||||
|
||||
if ($task) {
|
||||
$task->addVolume($deleted);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get menu content
|
||||
*/
|
||||
static function getMenuContent() {
|
||||
$menu = [];
|
||||
if (Session::haveRight(static::$rightname, READ)) {
|
||||
$menu['title'] = self::getMenuName();
|
||||
$menu['page'] = "/plugins/siem-wazuh/front/wazuhalert.php";
|
||||
$menu['links']['search'] = "/plugins/siem-wazuh/front/wazuhalert.php";
|
||||
}
|
||||
return $menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get menu name
|
||||
*/
|
||||
static function getMenuName() {
|
||||
return self::getTypeName(Session::getPluralNumber());
|
||||
}
|
||||
}
|
515
inc/wazuhapi.class.php
Normal file
515
inc/wazuhapi.class.php
Normal file
@@ -0,0 +1,515 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* Classe de gestion de l'API Wazuh
|
||||
*/
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
die("Sorry. You can't access this file directly");
|
||||
}
|
||||
|
||||
class PluginSiemWazuhAPI {
|
||||
|
||||
private $server;
|
||||
private $token;
|
||||
private $config;
|
||||
|
||||
const API_VERSION = 'v1';
|
||||
const TIMEOUT = 30;
|
||||
const MAX_RETRIES = 3;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct(PluginSiemWazuhServer $server) {
|
||||
$this->server = $server;
|
||||
$this->config = new PluginSiemWazuhConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test connection to Wazuh server
|
||||
*/
|
||||
public function testConnection() {
|
||||
try {
|
||||
$result = $this->authenticate();
|
||||
if ($result['success']) {
|
||||
// Test basic API call
|
||||
$info = $this->makeRequest('GET', '/');
|
||||
if ($info['success']) {
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => __('Connection successful', 'siem-wazuh'),
|
||||
'data' => $info['data']
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => __('Connection failed:', 'siem-wazuh') . ' ' . $info['message']
|
||||
];
|
||||
}
|
||||
} else {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => __('Authentication failed:', 'siem-wazuh') . ' ' . $result['message']
|
||||
];
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => __('Connection error:', 'siem-wazuh') . ' ' . $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate with Wazuh API
|
||||
*/
|
||||
private function authenticate() {
|
||||
if (!empty($this->token)) {
|
||||
return ['success' => true, 'token' => $this->token];
|
||||
}
|
||||
|
||||
$url = rtrim($this->server->fields['wazuh_url'], '/') . ':' . $this->server->fields['wazuh_port'] . '/security/user/authenticate';
|
||||
|
||||
$credentials = base64_encode($this->server->fields['wazuh_login'] . ':' . $this->server->getPassword());
|
||||
|
||||
$headers = [
|
||||
'Authorization: Basic ' . $credentials,
|
||||
'Content-Type: application/json'
|
||||
];
|
||||
|
||||
try {
|
||||
$response = $this->httpRequest('POST', $url, null, $headers);
|
||||
|
||||
if ($response['success'] && isset($response['data']['data']['token'])) {
|
||||
$this->token = $response['data']['data']['token'];
|
||||
return ['success' => true, 'token' => $this->token];
|
||||
} else {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $response['message'] ?? __('Authentication failed', 'siem-wazuh')
|
||||
];
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get alerts from Wazuh
|
||||
*/
|
||||
public function getAlerts($params = []) {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
|
||||
$default_params = [
|
||||
'offset' => 0,
|
||||
'limit' => $this->config->getConfiguration('max_alerts_per_sync', 100),
|
||||
'sort' => '-timestamp',
|
||||
'rule.level' => '>=' . $this->config->getConfiguration('min_rule_level', 5)
|
||||
];
|
||||
|
||||
// Merge with provided parameters
|
||||
$params = array_merge($default_params, $params);
|
||||
|
||||
// Construire l'URL avec les paramètres
|
||||
$query_string = http_build_query($params);
|
||||
$endpoint = '/alerts?' . $query_string;
|
||||
|
||||
return $this->makeRequest('GET', $endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get alert by ID
|
||||
*/
|
||||
public function getAlert($alert_id) {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
|
||||
return $this->makeRequest('GET', '/alerts/' . $alert_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get agents
|
||||
*/
|
||||
public function getAgents($params = []) {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
|
||||
$default_params = [
|
||||
'offset' => 0,
|
||||
'limit' => 500,
|
||||
'sort' => '+name'
|
||||
];
|
||||
|
||||
$params = array_merge($default_params, $params);
|
||||
$query_string = http_build_query($params);
|
||||
$endpoint = '/agents?' . $query_string;
|
||||
|
||||
return $this->makeRequest('GET', $endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get agent by ID
|
||||
*/
|
||||
public function getAgent($agent_id) {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
|
||||
return $this->makeRequest('GET', '/agents/' . $agent_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rules
|
||||
*/
|
||||
public function getRules($params = []) {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
|
||||
$default_params = [
|
||||
'offset' => 0,
|
||||
'limit' => 500,
|
||||
'sort' => '+id'
|
||||
];
|
||||
|
||||
$params = array_merge($default_params, $params);
|
||||
$query_string = http_build_query($params);
|
||||
$endpoint = '/rules?' . $query_string;
|
||||
|
||||
return $this->makeRequest('GET', $endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get decoders
|
||||
*/
|
||||
public function getDecoders($params = []) {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
|
||||
$default_params = [
|
||||
'offset' => 0,
|
||||
'limit' => 500
|
||||
];
|
||||
|
||||
$params = array_merge($default_params, $params);
|
||||
$query_string = http_build_query($params);
|
||||
$endpoint = '/decoders?' . $query_string;
|
||||
|
||||
return $this->makeRequest('GET', $endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cluster status
|
||||
*/
|
||||
public function getClusterStatus() {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
|
||||
return $this->makeRequest('GET', '/cluster/status');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get manager information
|
||||
*/
|
||||
public function getManagerInfo() {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
|
||||
return $this->makeRequest('GET', '/manager/info');
|
||||
}
|
||||
|
||||
/**
|
||||
* Make API request
|
||||
*/
|
||||
private function makeRequest($method, $endpoint, $data = null, $extra_headers = []) {
|
||||
if (!$this->token) {
|
||||
$auth_result = $this->authenticate();
|
||||
if (!$auth_result['success']) {
|
||||
return $auth_result;
|
||||
}
|
||||
}
|
||||
|
||||
$url = rtrim($this->server->fields['wazuh_url'], '/') . ':' . $this->server->fields['wazuh_port'] . $endpoint;
|
||||
|
||||
$headers = array_merge([
|
||||
'Authorization: Bearer ' . $this->token,
|
||||
'Content-Type: application/json'
|
||||
], $extra_headers);
|
||||
|
||||
return $this->httpRequest($method, $url, $data, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make HTTP request with retry logic
|
||||
*/
|
||||
private function httpRequest($method, $url, $data = null, $headers = []) {
|
||||
$retries = 0;
|
||||
$last_error = null;
|
||||
|
||||
while ($retries < self::MAX_RETRIES) {
|
||||
try {
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => self::TIMEOUT,
|
||||
CURLOPT_CONNECTTIMEOUT => 10,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_SSL_VERIFYHOST => false,
|
||||
CURLOPT_CUSTOMREQUEST => $method,
|
||||
CURLOPT_HTTPHEADER => $headers,
|
||||
CURLOPT_USERAGENT => 'GLPI-SIEM-Wazuh-Plugin/1.0'
|
||||
]);
|
||||
|
||||
if ($data !== null) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, is_array($data) ? json_encode($data) : $data);
|
||||
}
|
||||
|
||||
// Logging for debug mode
|
||||
if ($this->config->getConfiguration('debug_mode', 0)) {
|
||||
$this->logDebug("HTTP Request: $method $url", [
|
||||
'headers' => $headers,
|
||||
'data' => $data,
|
||||
'retry' => $retries
|
||||
]);
|
||||
}
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curl_error = curl_error($ch);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
if ($response === false) {
|
||||
throw new Exception("cURL Error: $curl_error");
|
||||
}
|
||||
|
||||
$decoded_response = json_decode($response, true);
|
||||
|
||||
if ($this->config->getConfiguration('debug_mode', 0)) {
|
||||
$this->logDebug("HTTP Response: $http_code", [
|
||||
'response' => $decoded_response,
|
||||
'raw_response' => substr($response, 0, 1000)
|
||||
]);
|
||||
}
|
||||
|
||||
if ($http_code >= 200 && $http_code < 300) {
|
||||
return [
|
||||
'success' => true,
|
||||
'data' => $decoded_response,
|
||||
'http_code' => $http_code
|
||||
];
|
||||
} elseif ($http_code == 401) {
|
||||
// Token expired, clear it and retry authentication
|
||||
$this->token = null;
|
||||
if ($retries < self::MAX_RETRIES - 1) {
|
||||
$retries++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$error_message = "HTTP $http_code";
|
||||
if ($decoded_response && isset($decoded_response['detail'])) {
|
||||
$error_message .= ": " . $decoded_response['detail'];
|
||||
} elseif ($decoded_response && isset($decoded_response['message'])) {
|
||||
$error_message .= ": " . $decoded_response['message'];
|
||||
}
|
||||
|
||||
throw new Exception($error_message);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$last_error = $e;
|
||||
$retries++;
|
||||
|
||||
if ($retries < self::MAX_RETRIES) {
|
||||
sleep(pow(2, $retries)); // Exponential backoff
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->logError("HTTP Request failed after $retries retries", [
|
||||
'url' => $url,
|
||||
'method' => $method,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => $last_error ? $last_error->getMessage() : __('Request failed after maximum retries', 'siem-wazuh')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Query Wazuh Indexer (OpenSearch/Elasticsearch)
|
||||
*/
|
||||
public function queryIndexer($index, $query = [], $params = []) {
|
||||
if (empty($this->server->fields['indexer_url'])) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => __('Indexer not configured', 'siem-wazuh')
|
||||
];
|
||||
}
|
||||
|
||||
$url = rtrim($this->server->fields['indexer_url'], '/') . ':' . $this->server->fields['indexer_port'] . '/' . $index . '/_search';
|
||||
|
||||
$headers = [
|
||||
'Content-Type: application/json'
|
||||
];
|
||||
|
||||
if (!empty($this->server->fields['indexer_login'])) {
|
||||
$credentials = base64_encode($this->server->fields['indexer_login'] . ':' . $this->server->getPassword('indexer_password'));
|
||||
$headers[] = 'Authorization: Basic ' . $credentials;
|
||||
}
|
||||
|
||||
$default_query = [
|
||||
'size' => $this->config->getConfiguration('max_alerts_per_sync', 100),
|
||||
'sort' => [
|
||||
['timestamp' => ['order' => 'desc']]
|
||||
]
|
||||
];
|
||||
|
||||
$final_query = array_merge($default_query, $query);
|
||||
|
||||
return $this->httpRequest('POST', $url, $final_query, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recent alerts from indexer
|
||||
*/
|
||||
public function getRecentAlertsFromIndexer($minutes = 60) {
|
||||
$query = [
|
||||
'query' => [
|
||||
'bool' => [
|
||||
'must' => [
|
||||
[
|
||||
'range' => [
|
||||
'timestamp' => [
|
||||
'gte' => 'now-' . $minutes . 'm',
|
||||
'lt' => 'now'
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$min_level = $this->config->getConfiguration('min_rule_level', 5);
|
||||
if ($min_level > 0) {
|
||||
$query['query']['bool']['must'][] = [
|
||||
'range' => [
|
||||
'rule.level' => [
|
||||
'gte' => $min_level
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
return $this->queryIndexer('wazuh-alerts-*', $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log debug message
|
||||
*/
|
||||
private function logDebug($message, $context = []) {
|
||||
if ($this->config->getConfiguration('debug_mode', 0)) {
|
||||
$this->log('debug', $message, $context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log error message
|
||||
*/
|
||||
private function logError($message, $context = []) {
|
||||
$this->log('error', $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log message
|
||||
*/
|
||||
private function log($level, $message, $context = []) {
|
||||
global $DB;
|
||||
|
||||
$log_levels = ['debug', 'info', 'warning', 'error', 'critical'];
|
||||
$current_log_level = $this->config->getConfiguration('log_level', 'info');
|
||||
$current_level_index = array_search($current_log_level, $log_levels);
|
||||
$message_level_index = array_search($level, $log_levels);
|
||||
|
||||
// Only log if message level is equal or higher than configured level
|
||||
if ($message_level_index >= $current_level_index) {
|
||||
try {
|
||||
$DB->insert('glpi_plugin_siem_wazuh_logs', [
|
||||
'wazuh_server_id' => $this->server->getID(),
|
||||
'level' => $level,
|
||||
'message' => $message,
|
||||
'context' => json_encode($context),
|
||||
'date_creation' => $_SESSION['glpi_currenttime']
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
// Ignore logging errors to prevent recursion
|
||||
error_log("SIEM-Wazuh Plugin: Failed to log message - " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get API statistics
|
||||
*/
|
||||
public function getStatistics() {
|
||||
global $DB;
|
||||
|
||||
$stats = [];
|
||||
|
||||
// Requests count by server
|
||||
$iterator = $DB->request([
|
||||
'SELECT' => ['COUNT' => '* as count'],
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_logs',
|
||||
'WHERE' => [
|
||||
'wazuh_server_id' => $this->server->getID(),
|
||||
'date_creation' => ['>=', date('Y-m-d 00:00:00')]
|
||||
]
|
||||
]);
|
||||
|
||||
$stats['requests_today'] = $iterator->current()['count'];
|
||||
|
||||
// Error rate
|
||||
$iterator = $DB->request([
|
||||
'SELECT' => ['COUNT' => '* as count'],
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_logs',
|
||||
'WHERE' => [
|
||||
'wazuh_server_id' => $this->server->getID(),
|
||||
'level' => ['IN', ['error', 'critical']],
|
||||
'date_creation' => ['>=', date('Y-m-d 00:00:00')]
|
||||
]
|
||||
]);
|
||||
|
||||
$stats['errors_today'] = $iterator->current()['count'];
|
||||
$stats['error_rate'] = $stats['requests_today'] > 0 ?
|
||||
round(($stats['errors_today'] / $stats['requests_today']) * 100, 2) : 0;
|
||||
|
||||
return $stats;
|
||||
}
|
||||
}
|
423
inc/wazuhconfig.class.php
Normal file
423
inc/wazuhconfig.class.php
Normal file
@@ -0,0 +1,423 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* Classe de gestion de la configuration
|
||||
*/
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
die("Sorry. You can't access this file directly");
|
||||
}
|
||||
|
||||
class PluginSiemWazuhConfig extends CommonDBTM {
|
||||
|
||||
public $dohistory = true;
|
||||
|
||||
static $rightname = 'plugin_siem_wazuh_config';
|
||||
|
||||
const MATRIX_FIELD = '';
|
||||
const SEARCHOPTIONS_INHERITANCE = true;
|
||||
|
||||
/**
|
||||
* Return the localized name of the current Type
|
||||
*/
|
||||
static function getTypeName($nb = 0) {
|
||||
return __('SIEM Wazuh Configuration', 'siem-wazuh');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration value
|
||||
*/
|
||||
function getConfiguration($name, $default = null, $context = 'global') {
|
||||
global $DB;
|
||||
|
||||
$iterator = $DB->request([
|
||||
'FROM' => $this->getTable(),
|
||||
'WHERE' => [
|
||||
'name' => $name,
|
||||
'context' => $context
|
||||
],
|
||||
'LIMIT' => 1
|
||||
]);
|
||||
|
||||
if (count($iterator) > 0) {
|
||||
$row = $iterator->current();
|
||||
return $row['value'];
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set configuration value
|
||||
*/
|
||||
function setConfiguration($name, $value, $context = 'global') {
|
||||
global $DB;
|
||||
|
||||
// Vérifier si la configuration existe
|
||||
$iterator = $DB->request([
|
||||
'FROM' => $this->getTable(),
|
||||
'WHERE' => [
|
||||
'name' => $name,
|
||||
'context' => $context
|
||||
],
|
||||
'LIMIT' => 1
|
||||
]);
|
||||
|
||||
if (count($iterator) > 0) {
|
||||
// Mettre à jour
|
||||
$row = $iterator->current();
|
||||
return $this->update([
|
||||
'id' => $row['id'],
|
||||
'value' => $value
|
||||
]);
|
||||
} else {
|
||||
// Créer
|
||||
return $this->add([
|
||||
'name' => $name,
|
||||
'value' => $value,
|
||||
'context' => $context
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display configuration form
|
||||
*/
|
||||
function showConfigForm() {
|
||||
global $CFG_GLPI;
|
||||
|
||||
echo "<form name='form' method='post' action='" . Toolbox::getItemTypeFormURL(__CLASS__) . "'>";
|
||||
echo "<div class='spaced' id='tabsbody'>";
|
||||
echo "<table class='tab_cadre_fixe'>";
|
||||
|
||||
echo "<tr><th colspan='4'>" . __('General Configuration', 'siem-wazuh') . "</th></tr>";
|
||||
|
||||
// Configuration générale
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td width='30%'>" . __('Enable synchronization', 'siem-wazuh') . "</td>";
|
||||
echo "<td width='20%'>";
|
||||
Dropdown::showYesNo("sync_enabled", $this->getConfiguration('sync_enabled', 1));
|
||||
echo "</td>";
|
||||
echo "<td width='30%'>" . __('Auto-create tickets', 'siem-wazuh') . "</td>";
|
||||
echo "<td width='20%'>";
|
||||
Dropdown::showYesNo("auto_create_ticket", $this->getConfiguration('auto_create_ticket', 1));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Maximum alerts per sync', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showNumber("max_alerts_per_sync", [
|
||||
'value' => $this->getConfiguration('max_alerts_per_sync', 100),
|
||||
'min' => 10,
|
||||
'max' => 1000,
|
||||
'step' => 10
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Alert retention (days)', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showNumber("alert_retention_days", [
|
||||
'value' => $this->getConfiguration('alert_retention_days', 90),
|
||||
'min' => 7,
|
||||
'max' => 365,
|
||||
'step' => 7
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Minimum rule level', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showNumber("min_rule_level", [
|
||||
'value' => $this->getConfiguration('min_rule_level', 5),
|
||||
'min' => 0,
|
||||
'max' => 15
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Enable notifications', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("notification_enabled", $this->getConfiguration('notification_enabled', 1));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
// Configuration des tickets par défaut
|
||||
echo "<tr><th colspan='4'>" . __('Default Ticket Settings', 'siem-wazuh') . "</th></tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Default Priority', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Ticket::dropdownPriority('default_ticket_priority', $this->getConfiguration('default_ticket_priority', 3));
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Default Urgency', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Ticket::dropdownUrgency('default_ticket_urgency', $this->getConfiguration('default_ticket_urgency', 3));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Default Impact', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Ticket::dropdownImpact('default_ticket_impact', $this->getConfiguration('default_ticket_impact', 3));
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Auto-assign tickets', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("auto_assign_tickets", $this->getConfiguration('auto_assign_tickets', 0));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
if ($this->getConfiguration('auto_assign_tickets', 0)) {
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Default Assignee', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
User::dropdown([
|
||||
'name' => 'default_assignee',
|
||||
'value' => $this->getConfiguration('default_assignee', 0),
|
||||
'right' => 'own_ticket'
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Default Group', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Group::dropdown([
|
||||
'name' => 'default_group',
|
||||
'value' => $this->getConfiguration('default_group', 0)
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
// Configuration de mapping
|
||||
echo "<tr><th colspan='4'>" . __('Asset Mapping Configuration', 'siem-wazuh') . "</th></tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Match by hostname', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("match_by_hostname", $this->getConfiguration('match_by_hostname', 1));
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Match by IP address', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("match_by_ip", $this->getConfiguration('match_by_ip', 1));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Case sensitive matching', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("case_sensitive_matching", $this->getConfiguration('case_sensitive_matching', 0));
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Create unknown assets', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("create_unknown_assets", $this->getConfiguration('create_unknown_assets', 0));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
// Configuration de debug
|
||||
echo "<tr><th colspan='4'>" . __('Debug Configuration', 'siem-wazuh') . "</th></tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Enable debug mode', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("debug_mode", $this->getConfiguration('debug_mode', 0));
|
||||
echo "</td>";
|
||||
echo "<td>" . __('Log level', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
$log_levels = [
|
||||
'error' => __('Error only', 'siem-wazuh'),
|
||||
'warning' => __('Warning and above', 'siem-wazuh'),
|
||||
'info' => __('Info and above', 'siem-wazuh'),
|
||||
'debug' => __('All messages', 'siem-wazuh')
|
||||
];
|
||||
Dropdown::showFromArray('log_level', $log_levels, [
|
||||
'value' => $this->getConfiguration('log_level', 'info')
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Keep debug logs (days)', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showNumber("debug_retention_days", [
|
||||
'value' => $this->getConfiguration('debug_retention_days', 30),
|
||||
'min' => 1,
|
||||
'max' => 90
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "<td></td><td></td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<td class='center' colspan='4'>";
|
||||
echo "<input type='submit' name='update_config' value='" . _sx('button', 'Save') . "' class='submit'>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "</table>";
|
||||
echo "</div>";
|
||||
Html::closeForm();
|
||||
|
||||
// Affichage des statistiques
|
||||
$this->showStatistics();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process configuration form
|
||||
*/
|
||||
function processConfigForm() {
|
||||
if (isset($_POST['update_config'])) {
|
||||
$config_fields = [
|
||||
'sync_enabled', 'auto_create_ticket', 'max_alerts_per_sync', 'alert_retention_days',
|
||||
'min_rule_level', 'notification_enabled', 'default_ticket_priority',
|
||||
'default_ticket_urgency', 'default_ticket_impact', 'auto_assign_tickets',
|
||||
'default_assignee', 'default_group', 'match_by_hostname', 'match_by_ip',
|
||||
'case_sensitive_matching', 'create_unknown_assets', 'debug_mode',
|
||||
'log_level', 'debug_retention_days'
|
||||
];
|
||||
|
||||
foreach ($config_fields as $field) {
|
||||
if (isset($_POST[$field])) {
|
||||
$this->setConfiguration($field, $_POST[$field]);
|
||||
}
|
||||
}
|
||||
|
||||
Session::addMessageAfterRedirect(__('Configuration updated successfully', 'siem-wazuh'));
|
||||
Html::back();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show statistics
|
||||
*/
|
||||
function showStatistics() {
|
||||
global $DB;
|
||||
|
||||
echo "<div class='spaced'>";
|
||||
echo "<table class='tab_cadre_fixe'>";
|
||||
echo "<tr><th colspan='4'>" . __('Statistics', 'siem-wazuh') . "</th></tr>";
|
||||
|
||||
// Statistiques des serveurs
|
||||
$servers_count = countElementsInTable('glpi_plugin_siem_wazuh_servers');
|
||||
$active_servers = countElementsInTable('glpi_plugin_siem_wazuh_servers', ['is_active' => 1]);
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Total servers', 'siem-wazuh') . "</td>";
|
||||
echo "<td>$servers_count</td>";
|
||||
echo "<td>" . __('Active servers', 'siem-wazuh') . "</td>";
|
||||
echo "<td>$active_servers</td>";
|
||||
echo "</tr>";
|
||||
|
||||
// Statistiques des alertes
|
||||
$total_alerts = countElementsInTable('glpi_plugin_siem_wazuh_alerts');
|
||||
$new_alerts = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['status' => 'new']);
|
||||
$processed_alerts = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['status' => 'processed']);
|
||||
$tickets_created = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['status' => 'ticket_created']);
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Total alerts', 'siem-wazuh') . "</td>";
|
||||
echo "<td>$total_alerts</td>";
|
||||
echo "<td>" . __('New alerts', 'siem-wazuh') . "</td>";
|
||||
echo "<td>$new_alerts</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Processed alerts', 'siem-wazuh') . "</td>";
|
||||
echo "<td>$processed_alerts</td>";
|
||||
echo "<td>" . __('Tickets created', 'siem-wazuh') . "</td>";
|
||||
echo "<td>$tickets_created</td>";
|
||||
echo "</tr>";
|
||||
|
||||
// Alertes par sévérité
|
||||
$severities = PluginSiemWazuhAlert::getSeverityArray();
|
||||
foreach ($severities as $severity => $label) {
|
||||
$count = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['severity' => $severity]);
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . sprintf(__('%s alerts', 'siem-wazuh'), $label) . "</td>";
|
||||
echo "<td>$count</td>";
|
||||
if (next($severities) !== false) {
|
||||
$next_severity = key($severities);
|
||||
$next_label = current($severities);
|
||||
$next_count = countElementsInTable('glpi_plugin_siem_wazuh_alerts', ['severity' => $next_severity]);
|
||||
echo "<td>" . sprintf(__('%s alerts', 'siem-wazuh'), $next_label) . "</td>";
|
||||
echo "<td>$next_count</td>";
|
||||
next($severities);
|
||||
} else {
|
||||
echo "<td></td><td></td>";
|
||||
}
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
// Dernière synchronisation
|
||||
$last_sync = $DB->request([
|
||||
'SELECT' => ['MAX' => 'last_sync AS last_sync'],
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_servers',
|
||||
'WHERE' => ['is_active' => 1]
|
||||
])->current();
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Last global sync', 'siem-wazuh') . "</td>";
|
||||
echo "<td colspan='3'>";
|
||||
echo $last_sync['last_sync'] ? Html::convDateTime($last_sync['last_sync']) : __('Never');
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "</table>";
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get menu content
|
||||
*/
|
||||
static function getMenuContent() {
|
||||
$menu = [];
|
||||
if (Session::haveRight(static::$rightname, READ)) {
|
||||
$menu['title'] = self::getMenuName();
|
||||
$menu['page'] = "/plugins/siem-wazuh/front/wazuhconfig.php";
|
||||
$menu['links']['config'] = "/plugins/siem-wazuh/front/wazuhconfig.php";
|
||||
}
|
||||
return $menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get menu name
|
||||
*/
|
||||
static function getMenuName() {
|
||||
return self::getTypeName(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default configurations
|
||||
*/
|
||||
static function getDefaultConfigurations() {
|
||||
return [
|
||||
'sync_enabled' => 1,
|
||||
'auto_create_ticket' => 1,
|
||||
'max_alerts_per_sync' => 100,
|
||||
'alert_retention_days' => 90,
|
||||
'min_rule_level' => 5,
|
||||
'notification_enabled' => 1,
|
||||
'default_ticket_priority' => 3,
|
||||
'default_ticket_urgency' => 3,
|
||||
'default_ticket_impact' => 3,
|
||||
'auto_assign_tickets' => 0,
|
||||
'default_assignee' => 0,
|
||||
'default_group' => 0,
|
||||
'match_by_hostname' => 1,
|
||||
'match_by_ip' => 1,
|
||||
'case_sensitive_matching' => 0,
|
||||
'create_unknown_assets' => 0,
|
||||
'debug_mode' => 0,
|
||||
'log_level' => 'info',
|
||||
'debug_retention_days' => 30
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Install default configuration
|
||||
*/
|
||||
static function installDefaultConfiguration() {
|
||||
$config = new self();
|
||||
$defaults = self::getDefaultConfigurations();
|
||||
|
||||
foreach ($defaults as $name => $value) {
|
||||
$config->setConfiguration($name, $value);
|
||||
}
|
||||
}
|
||||
}
|
411
inc/wazuhserver.class.php
Normal file
411
inc/wazuhserver.class.php
Normal file
@@ -0,0 +1,411 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* Classe de gestion des serveurs Wazuh
|
||||
*/
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
die("Sorry. You can't access this file directly");
|
||||
}
|
||||
|
||||
class PluginSiemWazuhServer extends CommonDBTM {
|
||||
|
||||
public $dohistory = true;
|
||||
|
||||
static protected $forward_entity_to = ['PluginSiemWazuhAlert'];
|
||||
|
||||
static $rightname = 'plugin_siem_wazuh_server';
|
||||
|
||||
const MATRIX_FIELD = '';
|
||||
const SEARCHOPTIONS_INHERITANCE = true;
|
||||
|
||||
/**
|
||||
* Return the localized name of the current Type
|
||||
*/
|
||||
static function getTypeName($nb = 0) {
|
||||
return _n('Wazuh Server', 'Wazuh Servers', $nb, 'siem-wazuh');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define tab name
|
||||
*/
|
||||
function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) {
|
||||
if (!$withtemplate) {
|
||||
$nb = 0;
|
||||
switch ($item->getType()) {
|
||||
case 'PluginSiemWazuhServer':
|
||||
if ($_SESSION['glpishow_count_on_tabs']) {
|
||||
$nb = countElementsInTable('glpi_plugin_siem_wazuh_alerts',
|
||||
"`wazuh_server_id` = '".$item->getID()."'");
|
||||
}
|
||||
return self::createTabEntry(__('Alerts', 'siem-wazuh'), $nb);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Display tab content
|
||||
*/
|
||||
static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) {
|
||||
if ($item->getType() == 'PluginSiemWazuhServer') {
|
||||
PluginSiemWazuhAlert::showForServer($item->getID());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define search options
|
||||
*/
|
||||
function rawSearchOptions() {
|
||||
$tab = [];
|
||||
|
||||
$tab[] = [
|
||||
'id' => 'common',
|
||||
'name' => __('Characteristics')
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '1',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'name',
|
||||
'name' => __('Name'),
|
||||
'datatype' => 'itemlink',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '2',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'wazuh_url',
|
||||
'name' => __('Wazuh URL', 'siem-wazuh'),
|
||||
'datatype' => 'string',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '3',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'wazuh_port',
|
||||
'name' => __('Wazuh Port', 'siem-wazuh'),
|
||||
'datatype' => 'integer',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '4',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'is_active',
|
||||
'name' => __('Active'),
|
||||
'datatype' => 'bool',
|
||||
'massiveaction' => true
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '5',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'sync_interval',
|
||||
'name' => __('Sync Interval (seconds)', 'siem-wazuh'),
|
||||
'datatype' => 'integer',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '6',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'last_sync',
|
||||
'name' => __('Last Sync', 'siem-wazuh'),
|
||||
'datatype' => 'datetime',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '7',
|
||||
'table' => 'glpi_itilcategories',
|
||||
'field' => 'name',
|
||||
'name' => __('Default Ticket Category', 'siem-wazuh'),
|
||||
'datatype' => 'dropdown',
|
||||
'massiveaction' => false,
|
||||
'linkfield' => 'ticket_category'
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '30',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'date_creation',
|
||||
'name' => __('Creation date'),
|
||||
'datatype' => 'datetime',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
$tab[] = [
|
||||
'id' => '31',
|
||||
'table' => $this->getTable(),
|
||||
'field' => 'date_mod',
|
||||
'name' => __('Last update'),
|
||||
'datatype' => 'datetime',
|
||||
'massiveaction' => false
|
||||
];
|
||||
|
||||
return $tab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display form
|
||||
*/
|
||||
function showForm($ID, $options = []) {
|
||||
$this->initForm($ID, $options);
|
||||
$this->showFormHeader($options);
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Name')."</td>";
|
||||
echo "<td>";
|
||||
Html::autocompletionTextField($this, "name", ['size' => 50]);
|
||||
echo "</td>";
|
||||
echo "<td>".__('Active')."</td>";
|
||||
echo "<td>";
|
||||
Dropdown::showYesNo("is_active", $this->fields["is_active"]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Wazuh Server URL', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
Html::autocompletionTextField($this, "wazuh_url", ['size' => 50, 'placeholder' => 'https://wazuh-server.domain.com']);
|
||||
echo "</td>";
|
||||
echo "<td>".__('Wazuh API Port', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
Html::autocompletionTextField($this, "wazuh_port", ['size' => 10, 'value' => ($this->fields["wazuh_port"] ?: 55000)]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Wazuh API Login', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
Html::autocompletionTextField($this, "wazuh_login", ['size' => 30]);
|
||||
echo "</td>";
|
||||
echo "<td>".__('Wazuh API Password', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
echo "<input type='password' name='wazuh_password' value='' size='30' autocomplete='new-password'>";
|
||||
if (!empty($this->fields["wazuh_password"])) {
|
||||
echo "<br><small>".__('Leave empty to keep current password', 'siem-wazuh')."</small>";
|
||||
}
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Indexer Server URL', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
Html::autocompletionTextField($this, "indexer_url", ['size' => 50, 'placeholder' => 'https://wazuh-indexer.domain.com']);
|
||||
echo "</td>";
|
||||
echo "<td>".__('Indexer API Port', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
Html::autocompletionTextField($this, "indexer_port", ['size' => 10, 'value' => ($this->fields["indexer_port"] ?: 9200)]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Indexer API Login', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
Html::autocompletionTextField($this, "indexer_login", ['size' => 30]);
|
||||
echo "</td>";
|
||||
echo "<td>".__('Indexer API Password', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
echo "<input type='password' name='indexer_password' value='' size='30' autocomplete='new-password'>";
|
||||
if (!empty($this->fields["indexer_password"])) {
|
||||
echo "<br><small>".__('Leave empty to keep current password', 'siem-wazuh')."</small>";
|
||||
}
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Sync Interval (seconds)', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
Html::autocompletionTextField($this, "sync_interval", ['size' => 10, 'value' => ($this->fields["sync_interval"] ?: 300)]);
|
||||
echo "</td>";
|
||||
echo "<td>".__('Default Ticket Type', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
Ticket::dropdownType('ticket_type', $this->fields["ticket_type"]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>".__('Default Ticket Category', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
ITILCategory::dropdown(['name' => 'ticket_category', 'value' => $this->fields["ticket_category"]]);
|
||||
echo "</td>";
|
||||
echo "<td>".__('Last Sync', 'siem-wazuh')."</td>";
|
||||
echo "<td>";
|
||||
echo ($this->fields["last_sync"] ? Html::convDateTime($this->fields["last_sync"]) : __('Never'));
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
// Bouton de test de connexion
|
||||
if ($ID > 0) {
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td colspan='4' class='center'>";
|
||||
echo "<button type='button' class='btn btn-primary' onclick='testWazuhConnection($ID)'>";
|
||||
echo __('Test Connection', 'siem-wazuh');
|
||||
echo "</button>";
|
||||
echo "<div id='test-result-$ID' class='mt-2'></div>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
$this->showFormButtons($options);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare input for add
|
||||
*/
|
||||
function prepareInputForAdd($input) {
|
||||
if (!empty($input['wazuh_password'])) {
|
||||
$input['wazuh_password'] = Toolbox::encrypt($input['wazuh_password'], GLPIKEY);
|
||||
}
|
||||
if (!empty($input['indexer_password'])) {
|
||||
$input['indexer_password'] = Toolbox::encrypt($input['indexer_password'], GLPIKEY);
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare input for update
|
||||
*/
|
||||
function prepareInputForUpdate($input) {
|
||||
if (!empty($input['wazuh_password'])) {
|
||||
$input['wazuh_password'] = Toolbox::encrypt($input['wazuh_password'], GLPIKEY);
|
||||
} else {
|
||||
unset($input['wazuh_password']);
|
||||
}
|
||||
|
||||
if (!empty($input['indexer_password'])) {
|
||||
$input['indexer_password'] = Toolbox::encrypt($input['indexer_password'], GLPIKEY);
|
||||
} else {
|
||||
unset($input['indexer_password']);
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get decrypted password
|
||||
*/
|
||||
function getPassword($field = 'wazuh_password') {
|
||||
if (!empty($this->fields[$field])) {
|
||||
return Toolbox::decrypt($this->fields[$field], GLPIKEY);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Test connection to Wazuh server
|
||||
*/
|
||||
function testConnection() {
|
||||
if (empty($this->fields['wazuh_url']) || empty($this->fields['wazuh_login'])) {
|
||||
return ['success' => false, 'message' => __('Missing connection parameters', 'siem-wazuh')];
|
||||
}
|
||||
|
||||
$api = new PluginSiemWazuhAPI($this);
|
||||
return $api->testConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize alerts from this server
|
||||
*/
|
||||
function syncAlerts() {
|
||||
if (!$this->fields['is_active']) {
|
||||
return ['success' => false, 'message' => __('Server is not active', 'siem-wazuh')];
|
||||
}
|
||||
|
||||
$api = new PluginSiemWazuhAPI($this);
|
||||
$result = $api->getAlerts();
|
||||
|
||||
if ($result['success']) {
|
||||
$alert = new PluginSiemWazuhAlert();
|
||||
$processed = $alert->processAlerts($this->getID(), $result['data']);
|
||||
|
||||
// Mise à jour de la dernière synchronisation
|
||||
$this->update([
|
||||
'id' => $this->getID(),
|
||||
'last_sync' => $_SESSION['glpi_currenttime']
|
||||
]);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => sprintf(__('%d alerts processed', 'siem-wazuh'), $processed)
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get servers for dropdown
|
||||
*/
|
||||
static function getDropdownValues($post) {
|
||||
global $DB;
|
||||
|
||||
$where = ['is_active' => 1];
|
||||
if (isset($post['searchText']) && !empty($post['searchText'])) {
|
||||
$where[] = ['name' => ['LIKE', '%' . $post['searchText'] . '%']];
|
||||
}
|
||||
|
||||
$iterator = $DB->request([
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_servers',
|
||||
'WHERE' => $where,
|
||||
'ORDER' => 'name'
|
||||
]);
|
||||
|
||||
$values = [];
|
||||
foreach ($iterator as $row) {
|
||||
$values[] = [
|
||||
'id' => $row['id'],
|
||||
'text' => $row['name']
|
||||
];
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get menu content
|
||||
*/
|
||||
static function getMenuContent() {
|
||||
$menu = [];
|
||||
if (Session::haveRight(static::$rightname, READ)) {
|
||||
$menu['title'] = self::getMenuName();
|
||||
$menu['page'] = "/plugins/siem-wazuh/front/wazuhserver.php";
|
||||
$menu['links']['search'] = "/plugins/siem-wazuh/front/wazuhserver.php";
|
||||
if (Session::haveRight(static::$rightname, CREATE)) {
|
||||
$menu['links']['add'] = "/plugins/siem-wazuh/front/wazuhserver.form.php";
|
||||
}
|
||||
}
|
||||
return $menu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get menu name
|
||||
*/
|
||||
static function getMenuName() {
|
||||
return self::getTypeName(Session::getPluralNumber());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean database relations
|
||||
*/
|
||||
function cleanDBonPurge() {
|
||||
$this->deleteChildrenAndRelationsFromDb(
|
||||
[
|
||||
PluginSiemWazuhAlert::class
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get used configuration
|
||||
*/
|
||||
static function getUsedConfig() {
|
||||
return ['plugin_siem_wazuh_server'];
|
||||
}
|
||||
}
|
454
inc/wazuhtab.class.php
Normal file
454
inc/wazuhtab.class.php
Normal file
@@ -0,0 +1,454 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* Classe de gestion des onglets Wazuh sur les assets
|
||||
*/
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
die("Sorry. You can't access this file directly");
|
||||
}
|
||||
|
||||
class PluginSiemWazuhTab extends CommonGLPI {
|
||||
|
||||
static $rightname = 'plugin_siem_wazuh_alert';
|
||||
|
||||
/**
|
||||
* Get tab name for item
|
||||
*/
|
||||
function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) {
|
||||
if (!$withtemplate && Session::haveRight(static::$rightname, READ)) {
|
||||
if (in_array($item->getType(), ['Computer', 'NetworkEquipment', 'Peripheral', 'Phone', 'Printer'])) {
|
||||
$nb = 0;
|
||||
if ($_SESSION['glpishow_count_on_tabs']) {
|
||||
$nb = self::countForItem($item);
|
||||
}
|
||||
return self::createTabEntry(__('Wazuh Alerts', 'siem-wazuh'), $nb);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Display tab content
|
||||
*/
|
||||
static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) {
|
||||
if (in_array($item->getType(), ['Computer', 'NetworkEquipment', 'Peripheral', 'Phone', 'Printer'])) {
|
||||
self::showForItem($item);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count alerts for item
|
||||
*/
|
||||
static function countForItem(CommonGLPI $item) {
|
||||
global $DB;
|
||||
|
||||
$itemtype = $item->getType();
|
||||
$items_id = $item->getID();
|
||||
|
||||
$field_map = [
|
||||
'Computer' => 'computer_id',
|
||||
'NetworkEquipment' => 'networkequipment_id',
|
||||
'Peripheral' => 'peripheral_id',
|
||||
'Phone' => 'phone_id',
|
||||
'Printer' => 'printer_id'
|
||||
];
|
||||
|
||||
if (!isset($field_map[$itemtype])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return countElementsInTable('glpi_plugin_siem_wazuh_alerts', [
|
||||
$field_map[$itemtype] => $items_id
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show alerts for item
|
||||
*/
|
||||
static function showForItem(CommonGLPI $item) {
|
||||
global $DB, $CFG_GLPI;
|
||||
|
||||
$itemtype = $item->getType();
|
||||
$items_id = $item->getID();
|
||||
|
||||
$field_map = [
|
||||
'Computer' => 'computer_id',
|
||||
'NetworkEquipment' => 'networkequipment_id',
|
||||
'Peripheral' => 'peripheral_id',
|
||||
'Phone' => 'phone_id',
|
||||
'Printer' => 'printer_id'
|
||||
];
|
||||
|
||||
if (!isset($field_map[$itemtype])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$field = $field_map[$itemtype];
|
||||
$canupdate = Session::haveRight(static::$rightname, UPDATE);
|
||||
|
||||
// Récupération des alertes
|
||||
$iterator = $DB->request([
|
||||
'SELECT' => [
|
||||
'glpi_plugin_siem_wazuh_alerts.*',
|
||||
'glpi_plugin_siem_wazuh_servers.name AS server_name'
|
||||
],
|
||||
'FROM' => 'glpi_plugin_siem_wazuh_alerts',
|
||||
'LEFT JOIN' => [
|
||||
'glpi_plugin_siem_wazuh_servers' => [
|
||||
'ON' => [
|
||||
'glpi_plugin_siem_wazuh_alerts' => 'wazuh_server_id',
|
||||
'glpi_plugin_siem_wazuh_servers' => 'id'
|
||||
]
|
||||
]
|
||||
],
|
||||
'WHERE' => [$field => $items_id],
|
||||
'ORDER' => ['timestamp DESC', 'rule_level DESC'],
|
||||
'LIMIT' => 100
|
||||
]);
|
||||
|
||||
$alerts = iterator_to_array($iterator);
|
||||
|
||||
echo "<div class='spaced'>";
|
||||
|
||||
// Résumé des alertes
|
||||
self::showAlertsSummary($alerts);
|
||||
|
||||
if (count($alerts) > 0) {
|
||||
// Filtres
|
||||
self::showAlertsFilters($item, $field);
|
||||
|
||||
// Tableau des alertes
|
||||
self::showAlertsTable($alerts, $canupdate);
|
||||
|
||||
// Actions de masse
|
||||
if ($canupdate && count($alerts) > 0) {
|
||||
self::showMassActions($item, $field);
|
||||
}
|
||||
} else {
|
||||
echo "<div class='center'>";
|
||||
echo "<p>" . __('No Wazuh alerts found for this item', 'siem-wazuh') . "</p>";
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Show alerts summary
|
||||
*/
|
||||
static function showAlertsSummary($alerts) {
|
||||
$stats = [
|
||||
'total' => count($alerts),
|
||||
'new' => 0,
|
||||
'processed' => 0,
|
||||
'ticket_created' => 0,
|
||||
'ignored' => 0,
|
||||
'critical' => 0,
|
||||
'high' => 0,
|
||||
'medium' => 0,
|
||||
'low' => 0
|
||||
];
|
||||
|
||||
foreach ($alerts as $alert) {
|
||||
$stats[$alert['status']]++;
|
||||
$stats[$alert['severity']]++;
|
||||
}
|
||||
|
||||
echo "<div class='alert-summary'>";
|
||||
echo "<table class='tab_cadre_fixe'>";
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<th colspan='8'>" . __('Alerts Summary', 'siem-wazuh') . "</th>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td class='center'><strong>" . $stats['total'] . "</strong><br>" . __('Total', 'siem-wazuh') . "</td>";
|
||||
echo "<td class='center status-new'><strong>" . $stats['new'] . "</strong><br>" . __('New', 'siem-wazuh') . "</td>";
|
||||
echo "<td class='center status-processed'><strong>" . $stats['processed'] . "</strong><br>" . __('Processed', 'siem-wazuh') . "</td>";
|
||||
echo "<td class='center status-ticket'><strong>" . $stats['ticket_created'] . "</strong><br>" . __('Ticket Created', 'siem-wazuh') . "</td>";
|
||||
echo "<td class='center severity-critical'><strong>" . $stats['critical'] . "</strong><br>" . __('Critical', 'siem-wazuh') . "</td>";
|
||||
echo "<td class='center severity-high'><strong>" . $stats['high'] . "</strong><br>" . __('High', 'siem-wazuh') . "</td>";
|
||||
echo "<td class='center severity-medium'><strong>" . $stats['medium'] . "</strong><br>" . __('Medium', 'siem-wazuh') . "</td>";
|
||||
echo "<td class='center severity-low'><strong>" . $stats['low'] . "</strong><br>" . __('Low', 'siem-wazuh') . "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "</table>";
|
||||
echo "</div>";
|
||||
echo "<br>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Show alerts filters
|
||||
*/
|
||||
static function showAlertsFilters($item, $field) {
|
||||
echo "<div class='alert-filters'>";
|
||||
echo "<form method='get' action=''>";
|
||||
echo "<input type='hidden' name='id' value='" . $item->getID() . "'>";
|
||||
echo "<input type='hidden' name='_glpi_tab' value='PluginSiemWazuhTab$1'>";
|
||||
|
||||
echo "<table class='tab_cadre_fixe'>";
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<th colspan='4'>" . __('Filters', 'siem-wazuh') . "</th>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Status') . "</td>";
|
||||
echo "<td>";
|
||||
$statuses = ['all' => __('All')] + PluginSiemWazuhAlert::getStatusArray();
|
||||
Dropdown::showFromArray('filter_status', $statuses, [
|
||||
'value' => $_GET['filter_status'] ?? 'all'
|
||||
]);
|
||||
echo "</td>";
|
||||
|
||||
echo "<td>" . __('Severity', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
$severities = ['all' => __('All')] + PluginSiemWazuhAlert::getSeverityArray();
|
||||
Dropdown::showFromArray('filter_severity', $severities, [
|
||||
'value' => $_GET['filter_severity'] ?? 'all'
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Date from', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Html::showDateField('filter_date_from', [
|
||||
'value' => $_GET['filter_date_from'] ?? ''
|
||||
]);
|
||||
echo "</td>";
|
||||
|
||||
echo "<td>" . __('Date to', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
Html::showDateField('filter_date_to', [
|
||||
'value' => $_GET['filter_date_to'] ?? ''
|
||||
]);
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td colspan='4' class='center'>";
|
||||
echo "<input type='submit' value='" . _sx('button', 'Filter') . "' class='submit'>";
|
||||
echo " ";
|
||||
echo "<input type='button' value='" . _sx('button', 'Reset') . "' class='submit' onclick='window.location.href=\"" . $item->getLinkURL() . "&_glpi_tab=PluginSiemWazuhTab$1\"'>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "</table>";
|
||||
echo "</form>";
|
||||
echo "</div>";
|
||||
echo "<br>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Show alerts table
|
||||
*/
|
||||
static function showAlertsTable($alerts, $canupdate) {
|
||||
echo "<div class='alert-list'>";
|
||||
echo "<form method='post' name='alerts_form' id='alerts_form' action='" . Plugin::getWebDir('siem-wazuh') . "/front/wazuhalert.form.php'>";
|
||||
echo "<table class='tab_cadre_fixehov'>";
|
||||
|
||||
$header = "<tr class='tab_bg_2'>";
|
||||
if ($canupdate) {
|
||||
$header .= "<th width='10'>";
|
||||
$header .= "<input type='checkbox' name='select_all' onclick='checkAll(this)'>";
|
||||
$header .= "</th>";
|
||||
}
|
||||
$header .= "<th>" . __('Alert ID', 'siem-wazuh') . "</th>";
|
||||
$header .= "<th>" . __('Server', 'siem-wazuh') . "</th>";
|
||||
$header .= "<th>" . __('Rule', 'siem-wazuh') . "</th>";
|
||||
$header .= "<th>" . __('Level', 'siem-wazuh') . "</th>";
|
||||
$header .= "<th>" . __('Agent', 'siem-wazuh') . "</th>";
|
||||
$header .= "<th>" . __('Timestamp', 'siem-wazuh') . "</th>";
|
||||
$header .= "<th>" . __('Status') . "</th>";
|
||||
$header .= "<th>" . __('Severity', 'siem-wazuh') . "</th>";
|
||||
$header .= "<th>" . __('Ticket') . "</th>";
|
||||
$header .= "</tr>";
|
||||
|
||||
echo $header;
|
||||
|
||||
if (count($alerts) == 0) {
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td colspan='" . ($canupdate ? "10" : "9") . "' class='center'>";
|
||||
echo __('No alerts found', 'siem-wazuh');
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
$statuses = PluginSiemWazuhAlert::getStatusArray();
|
||||
$severities = PluginSiemWazuhAlert::getSeverityArray();
|
||||
|
||||
foreach ($alerts as $alert) {
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
|
||||
if ($canupdate) {
|
||||
echo "<td>";
|
||||
echo "<input type='checkbox' name='selected_alerts[]' value='" . $alert['id'] . "'>";
|
||||
echo "</td>";
|
||||
}
|
||||
|
||||
echo "<td>";
|
||||
echo "<a href='" . PluginSiemWazuhAlert::getFormURLWithID($alert['id']) . "'>";
|
||||
echo $alert['alert_id'];
|
||||
echo "</a>";
|
||||
echo "</td>";
|
||||
|
||||
echo "<td>" . $alert['server_name'] . "</td>";
|
||||
|
||||
echo "<td>";
|
||||
echo "<strong>" . $alert['rule_id'] . "</strong><br>";
|
||||
echo "<small>" . Html::clean($alert['rule_description']) . "</small>";
|
||||
echo "</td>";
|
||||
|
||||
echo "<td class='center'>";
|
||||
echo "<span class='rule-level-" . $alert['rule_level'] . "'>";
|
||||
echo $alert['rule_level'];
|
||||
echo "</span>";
|
||||
echo "</td>";
|
||||
|
||||
echo "<td>";
|
||||
echo $alert['agent_name'] . "<br>";
|
||||
echo "<small>" . $alert['agent_ip'] . "</small>";
|
||||
echo "</td>";
|
||||
|
||||
echo "<td>" . Html::convDateTime($alert['timestamp']) . "</td>";
|
||||
|
||||
echo "<td>";
|
||||
echo "<span class='status-badge status-" . $alert['status'] . "'>";
|
||||
echo $statuses[$alert['status']];
|
||||
echo "</span>";
|
||||
echo "</td>";
|
||||
|
||||
echo "<td>";
|
||||
echo "<span class='severity-badge severity-" . $alert['severity'] . "'>";
|
||||
echo $severities[$alert['severity']];
|
||||
echo "</span>";
|
||||
echo "</td>";
|
||||
|
||||
echo "<td>";
|
||||
if ($alert['ticket_id']) {
|
||||
$ticket = new Ticket();
|
||||
if ($ticket->getFromDB($alert['ticket_id'])) {
|
||||
echo $ticket->getLink();
|
||||
}
|
||||
} else {
|
||||
echo "-";
|
||||
}
|
||||
echo "</td>";
|
||||
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
echo "</table>";
|
||||
|
||||
if ($canupdate && count($alerts) > 0) {
|
||||
echo "<div class='center' style='margin-top: 10px;'>";
|
||||
echo "<input type='submit' name='mark_processed' value='" . __('Mark as Processed', 'siem-wazuh') . "' class='submit'>";
|
||||
echo " ";
|
||||
echo "<input type='submit' name='mark_ignored' value='" . __('Mark as Ignored', 'siem-wazuh') . "' class='submit'>";
|
||||
echo " ";
|
||||
echo "<input type='submit' name='create_tickets' value='" . __('Create Tickets', 'siem-wazuh') . "' class='submit'>";
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
echo Html::closeForm();
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Show mass actions
|
||||
*/
|
||||
static function showMassActions($item, $field) {
|
||||
echo "<br>";
|
||||
echo "<div class='mass-actions'>";
|
||||
echo "<table class='tab_cadre_fixe'>";
|
||||
echo "<tr class='tab_bg_2'>";
|
||||
echo "<th colspan='2'>" . __('Mass Actions', 'siem-wazuh') . "</th>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Sync new alerts', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
echo "<button type='button' class='btn btn-primary' onclick='syncAlertsForItem(" . $item->getID() . ", \"" . $item->getType() . "\")'>";
|
||||
echo __('Sync Now', 'siem-wazuh');
|
||||
echo "</button>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "<tr class='tab_bg_1'>";
|
||||
echo "<td>" . __('Export alerts', 'siem-wazuh') . "</td>";
|
||||
echo "<td>";
|
||||
echo "<a href='" . Plugin::getWebDir('siem-wazuh') . "/front/wazuhalert.php?export=1&" . $field . "=" . $item->getID() . "' class='btn'>";
|
||||
echo __('Export CSV', 'siem-wazuh');
|
||||
echo "</a>";
|
||||
echo "</td>";
|
||||
echo "</tr>";
|
||||
|
||||
echo "</table>";
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get search options for item
|
||||
*/
|
||||
static function getSearchOptionsForItem($itemtype) {
|
||||
$tab = [];
|
||||
|
||||
$tab[5150]['table'] = 'glpi_plugin_siem_wazuh_alerts';
|
||||
$tab[5150]['field'] = 'id';
|
||||
$tab[5150]['name'] = __('Wazuh Alerts', 'siem-wazuh');
|
||||
$tab[5150]['forcegroupby'] = true;
|
||||
$tab[5150]['usehaving'] = true;
|
||||
$tab[5150]['datatype'] = 'count';
|
||||
$tab[5150]['massiveaction'] = false;
|
||||
|
||||
$field_map = [
|
||||
'Computer' => 'computer_id',
|
||||
'NetworkEquipment' => 'networkequipment_id',
|
||||
'Peripheral' => 'peripheral_id',
|
||||
'Phone' => 'phone_id',
|
||||
'Printer' => 'printer_id'
|
||||
];
|
||||
|
||||
if (isset($field_map[$itemtype])) {
|
||||
$tab[5150]['linkfield'] = $field_map[$itemtype];
|
||||
}
|
||||
|
||||
return $tab;
|
||||
}
|
||||
}
|
||||
|
||||
// JavaScript pour les fonctionnalités interactives
|
||||
echo "<script type='text/javascript'>
|
||||
function checkAll(checkbox) {
|
||||
var checkboxes = document.querySelectorAll('input[name=\"selected_alerts[]\"]');
|
||||
for (var i = 0; i < checkboxes.length; i++) {
|
||||
checkboxes[i].checked = checkbox.checked;
|
||||
}
|
||||
}
|
||||
|
||||
function syncAlertsForItem(itemId, itemType) {
|
||||
if (confirm('" . __('Are you sure you want to sync alerts for this item?', 'siem-wazuh') . "')) {
|
||||
// AJAX call to sync alerts
|
||||
fetch('" . Plugin::getWebDir('siem-wazuh') . "/ajax/sync_alerts.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: 'item_id=' + itemId + '&item_type=' + itemType
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert('" . __('Sync completed successfully', 'siem-wazuh') . "');
|
||||
location.reload();
|
||||
} else {
|
||||
alert('" . __('Sync failed:', 'siem-wazuh') . " ' + data.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('" . __('An error occurred during sync', 'siem-wazuh') . "');
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>";
|
0
js/config.js
Normal file
0
js/config.js
Normal file
487
js/wazuh.js
Normal file
487
js/wazuh.js
Normal file
@@ -0,0 +1,487 @@
|
||||
/**
|
||||
* SIEM-Wazuh Plugin for GLPI - Main JavaScript
|
||||
* Handles all interactive features for the Wazuh plugin
|
||||
*/
|
||||
|
||||
// Plugin namespace
|
||||
var WazuhPlugin = WazuhPlugin || {};
|
||||
|
||||
/**
|
||||
* Initialize the plugin
|
||||
*/
|
||||
WazuhPlugin.init = function() {
|
||||
// Initialize tooltips
|
||||
this.initTooltips();
|
||||
|
||||
// Initialize auto-refresh
|
||||
this.initAutoRefresh();
|
||||
|
||||
// Initialize form validation
|
||||
this.initFormValidation();
|
||||
|
||||
// Initialize keyboard shortcuts
|
||||
this.initKeyboardShortcuts();
|
||||
|
||||
console.log('SIEM-Wazuh Plugin initialized');
|
||||
};
|
||||
|
||||
/**
|
||||
* Test connection to Wazuh server
|
||||
*/
|
||||
WazuhPlugin.testConnection = function(serverId) {
|
||||
const button = document.querySelector('.test-connection-' + serverId);
|
||||
const resultDiv = document.getElementById('test-result-' + serverId);
|
||||
|
||||
if (!button || !resultDiv) {
|
||||
console.error('Test connection elements not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Show loading state
|
||||
button.disabled = true;
|
||||
button.innerHTML = '<span class="loading-spinner"></span>' + LANG.TESTING;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('server_id', serverId);
|
||||
formData.append('_glpi_csrf_token', getAjaxCsrfToken());
|
||||
|
||||
fetch(CFG_GLPI.root_doc + '/plugins/siem-wazuh/ajax/test_connection.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('HTTP ' + response.status);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
let alertClass = data.success ? 'alert-success' : 'alert-danger';
|
||||
let message = data.message;
|
||||
|
||||
if (data.success && data.server_info) {
|
||||
message += '<br><small><strong>Server Info:</strong><br>';
|
||||
message += 'Version: ' + (data.server_info.api_version || 'Unknown') + '<br>';
|
||||
message += 'Timestamp: ' + data.timestamp + '</small>';
|
||||
}
|
||||
|
||||
resultDiv.innerHTML = '<div class="alert ' + alertClass + '">' + message + '</div>';
|
||||
|
||||
// Auto-hide after 10 seconds for success messages
|
||||
if (data.success) {
|
||||
setTimeout(() => {
|
||||
resultDiv.innerHTML = '';
|
||||
}, 10000);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Connection test error:', error);
|
||||
resultDiv.innerHTML = '<div class="alert alert-danger">' + LANG.CONNECTION_TEST_FAILED + ': ' + error.message + '</div>';
|
||||
})
|
||||
.finally(() => {
|
||||
button.disabled = false;
|
||||
button.innerHTML = LANG.TEST_CONNECTION;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Synchronize alerts from Wazuh server
|
||||
*/
|
||||
WazuhPlugin.syncAlerts = function(serverId, showConfirm = true) {
|
||||
if (showConfirm && !confirm(LANG.CONFIRM_SYNC_ALERTS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const button = document.querySelector('.sync-alerts-' + serverId);
|
||||
if (button) {
|
||||
button.disabled = true;
|
||||
button.innerHTML = '<span class="loading-spinner"></span>' + LANG.SYNCHRONIZING;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('server_id', serverId);
|
||||
formData.append('action', 'sync');
|
||||
formData.append('_glpi_csrf_token', getAjaxCsrfToken());
|
||||
|
||||
fetch(CFG_GLPI.root_doc + '/plugins/siem-wazuh/ajax/sync_alerts.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
this.showNotification(data.message, 'success');
|
||||
|
||||
// Update statistics if available
|
||||
if (data.stats) {
|
||||
this.updateStatistics(data.stats);
|
||||
}
|
||||
|
||||
// Refresh the page after 2 seconds
|
||||
setTimeout(() => {
|
||||
location.reload();
|
||||
}, 2000);
|
||||
} else {
|
||||
this.showNotification(LANG.SYNC_FAILED + ' ' + data.message, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Sync error:', error);
|
||||
this.showNotification(LANG.SYNC_ERROR, 'error');
|
||||
})
|
||||
.finally(() => {
|
||||
if (button) {
|
||||
button.disabled = false;
|
||||
button.innerHTML = LANG.SYNC_ALERTS;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sync alerts for specific item
|
||||
*/
|
||||
WazuhPlugin.syncAlertsForItem = function(itemId, itemType) {
|
||||
if (!confirm(LANG.CONFIRM_SYNC_ITEM)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('item_id', itemId);
|
||||
formData.append('item_type', itemType);
|
||||
formData.append('action', 'sync_for_item');
|
||||
formData.append('_glpi_csrf_token', getAjaxCsrfToken());
|
||||
|
||||
fetch(CFG_GLPI.root_doc + '/plugins/siem-wazuh/ajax/sync_alerts.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
this.showNotification(data.message, 'success');
|
||||
setTimeout(() => location.reload(), 2000);
|
||||
} else {
|
||||
this.showNotification(LANG.SYNC_FAILED + ' ' + data.message, 'error');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Item sync error:', error);
|
||||
this.showNotification(LANG.SYNC_ERROR, 'error');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle server active status
|
||||
*/
|
||||
WazuhPlugin.toggleServerStatus = function(serverId) {
|
||||
if (confirm(LANG.CONFIRM_TOGGLE_STATUS)) {
|
||||
window.location.href = '?action=toggle_active&id=' + serverId;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check/uncheck all alerts
|
||||
*/
|
||||
WazuhPlugin.checkAllAlerts = function(checkbox) {
|
||||
const checkboxes = document.querySelectorAll('input[name="selected_alerts[]"]');
|
||||
for (let i = 0; i < checkboxes.length; i++) {
|
||||
checkboxes[i].checked = checkbox.checked;
|
||||
}
|
||||
this.updateBulkActionButtons();
|
||||
};
|
||||
|
||||
/**
|
||||
* Update bulk action buttons based on selection
|
||||
*/
|
||||
WazuhPlugin.updateBulkActionButtons = function() {
|
||||
const checkboxes = document.querySelectorAll('input[name="selected_alerts[]"]:checked');
|
||||
const bulkActions = document.querySelectorAll('.bulk-action-btn');
|
||||
|
||||
bulkActions.forEach(button => {
|
||||
button.disabled = checkboxes.length === 0;
|
||||
});
|
||||
|
||||
// Update counter if exists
|
||||
const counter = document.getElementById('selected-count');
|
||||
if (counter) {
|
||||
counter.textContent = checkboxes.length;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Filter alerts table
|
||||
*/
|
||||
WazuhPlugin.filterAlerts = function(filters) {
|
||||
const table = document.querySelector('.alert-list table');
|
||||
if (!table) return;
|
||||
|
||||
const rows = table.querySelectorAll('tbody tr');
|
||||
|
||||
rows.forEach(row => {
|
||||
let visible = true;
|
||||
|
||||
// Apply filters
|
||||
Object.keys(filters).forEach(filterKey => {
|
||||
const filterValue = filters[filterKey];
|
||||
if (filterValue && filterValue !== 'all') {
|
||||
const cell = row.querySelector('[data-' + filterKey + ']');
|
||||
if (cell && cell.getAttribute('data-' + filterKey) !== filterValue) {
|
||||
visible = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
row.style.display = visible ? '' : 'none';
|
||||
});
|
||||
|
||||
this.updateFilterStats();
|
||||
};
|
||||
|
||||
/**
|
||||
* Update filter statistics
|
||||
*/
|
||||
WazuhPlugin.updateFilterStats = function() {
|
||||
const visibleRows = document.querySelectorAll('.alert-list tbody tr:not([style*="none"])');
|
||||
const totalRows = document.querySelectorAll('.alert-list tbody tr');
|
||||
|
||||
const statsElement = document.getElementById('filter-stats');
|
||||
if (statsElement) {
|
||||
statsElement.textContent = `${visibleRows.length} of ${totalRows.length} alerts shown`;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Export alerts to CSV
|
||||
*/
|
||||
WazuhPlugin.exportAlerts = function(params = {}) {
|
||||
const url = new URL(CFG_GLPI.root_doc + '/plugins/siem-wazuh/front/wazuhalert.php');
|
||||
url.searchParams.append('export', '1');
|
||||
|
||||
Object.keys(params).forEach(key => {
|
||||
url.searchParams.append(key, params[key]);
|
||||
});
|
||||
|
||||
window.open(url.toString(), '_blank');
|
||||
};
|
||||
|
||||
/**
|
||||
* Show notification message
|
||||
*/
|
||||
WazuhPlugin.showNotification = function(message, type = 'info', duration = 5000) {
|
||||
// Try to use GLPI's notification system first
|
||||
if (typeof displayAjaxMessageAfterRedirect === 'function') {
|
||||
displayAjaxMessageAfterRedirect();
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback to custom notification
|
||||
const notification = document.createElement('div');
|
||||
notification.className = `alert alert-${type === 'error' ? 'danger' : type} wazuh-notification`;
|
||||
notification.style.cssText = `
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 9999;
|
||||
max-width: 400px;
|
||||
animation: slideIn 0.3s ease-out;
|
||||
`;
|
||||
notification.innerHTML = `
|
||||
<button type="button" class="close" onclick="this.parentElement.remove()">
|
||||
<span>×</span>
|
||||
</button>
|
||||
${message}
|
||||
`;
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// Auto-remove after duration
|
||||
if (duration > 0) {
|
||||
setTimeout(() => {
|
||||
if (notification.parentNode) {
|
||||
notification.remove();
|
||||
}
|
||||
}, duration);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update statistics display
|
||||
*/
|
||||
WazuhPlugin.updateStatistics = function(stats) {
|
||||
Object.keys(stats).forEach(key => {
|
||||
const element = document.getElementById('stat-' + key);
|
||||
if (element) {
|
||||
element.textContent = stats[key];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize tooltips
|
||||
*/
|
||||
WazuhPlugin.initTooltips = function() {
|
||||
// Simple tooltip implementation
|
||||
const tooltipElements = document.querySelectorAll('[data-tooltip]');
|
||||
|
||||
tooltipElements.forEach(element => {
|
||||
element.addEventListener('mouseenter', function() {
|
||||
const tooltip = document.createElement('div');
|
||||
tooltip.className = 'wazuh-tooltip-popup';
|
||||
tooltip.textContent = this.getAttribute('data-tooltip');
|
||||
tooltip.style.cssText = `
|
||||
position: absolute;
|
||||
background: #333;
|
||||
color: white;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
`;
|
||||
|
||||
document.body.appendChild(tooltip);
|
||||
|
||||
// Position tooltip
|
||||
const rect = this.getBoundingClientRect();
|
||||
tooltip.style.left = (rect.left + rect.width / 2 - tooltip.offsetWidth / 2) + 'px';
|
||||
tooltip.style.top = (rect.top - tooltip.offsetHeight - 5) + 'px';
|
||||
|
||||
this._tooltip = tooltip;
|
||||
});
|
||||
|
||||
element.addEventListener('mouseleave', function() {
|
||||
if (this._tooltip) {
|
||||
this._tooltip.remove();
|
||||
this._tooltip = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize auto-refresh functionality
|
||||
*/
|
||||
WazuhPlugin.initAutoRefresh = function() {
|
||||
const refreshInterval = parseInt(document.body.getAttribute('data-refresh-interval')) || 0;
|
||||
|
||||
if (refreshInterval > 0) {
|
||||
setInterval(() => {
|
||||
// Only refresh if page is visible
|
||||
if (!document.hidden) {
|
||||
this.refreshData();
|
||||
}
|
||||
}, refreshInterval * 1000);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Refresh data without full page reload
|
||||
*/
|
||||
WazuhPlugin.refreshData = function() {
|
||||
// Update statistics
|
||||
fetch(CFG_GLPI.root_doc + '/plugins/siem-wazuh/ajax/sync_alerts.php?action=status')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
this.updateStatistics(data.stats);
|
||||
}
|
||||
})
|
||||
.catch(error => console.warn('Auto-refresh failed:', error));
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize form validation
|
||||
*/
|
||||
WazuhPlugin.initFormValidation = function() {
|
||||
// Validate server URLs
|
||||
const urlInputs = document.querySelectorAll('input[name="wazuh_url"], input[name="indexer_url"]');
|
||||
urlInputs.forEach(input => {
|
||||
input.addEventListener('blur', function() {
|
||||
if (this.value && !this.value.match(/^https?:\/\/.+/)) {
|
||||
this.setCustomValidity('Please enter a valid URL starting with http:// or https://');
|
||||
} else {
|
||||
this.setCustomValidity('');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Validate port numbers
|
||||
const portInputs = document.querySelectorAll('input[name="wazuh_port"], input[name="indexer_port"]');
|
||||
portInputs.forEach(input => {
|
||||
input.addEventListener('blur', function() {
|
||||
const port = parseInt(this.value);
|
||||
if (this.value && (isNaN(port) || port < 1 || port > 65535)) {
|
||||
this.setCustomValidity('Please enter a valid port number (1-65535)');
|
||||
} else {
|
||||
this.setCustomValidity('');
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize keyboard shortcuts
|
||||
*/
|
||||
WazuhPlugin.initKeyboardShortcuts = function() {
|
||||
document.addEventListener('keydown', function(e) {
|
||||
// Ctrl+Shift+S: Sync all servers
|
||||
if (e.ctrlKey && e.shiftKey && e.key === 'S') {
|
||||
e.preventDefault();
|
||||
const syncButton = document.querySelector('.sync-all-btn');
|
||||
if (syncButton) {
|
||||
syncButton.click();
|
||||
}
|
||||
}
|
||||
|
||||
// Ctrl+Shift+T: Test connection
|
||||
if (e.ctrlKey && e.shiftKey && e.key === 'T') {
|
||||
e.preventDefault();
|
||||
const testButton = document.querySelector('.test-connection-btn');
|
||||
if (testButton) {
|
||||
testButton.click();
|
||||
}
|
||||
}
|
||||
|
||||
// Escape: Close modals/notifications
|
||||
if (e.key === 'Escape') {
|
||||
const notifications = document.querySelectorAll('.wazuh-notification');
|
||||
notifications.forEach(notification => notification.remove());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility function to get CSRF token
|
||||
*/
|
||||
function getAjaxCsrfToken() {
|
||||
return $('meta[name="glpi-csrf-token"]').attr('content') || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Language strings (will be populated by PHP)
|
||||
*/
|
||||
var LANG = LANG || {
|
||||
TESTING: 'Testing...',
|
||||
SYNCHRONIZING: 'Synchronizing...',
|
||||
TEST_CONNECTION: 'Test Connection',
|
||||
SYNC_ALERTS: 'Sync Alerts',
|
||||
CONNECTION_TEST_FAILED: 'Connection test failed',
|
||||
SYNC_FAILED: 'Sync failed:',
|
||||
SYNC_ERROR: 'An error occurred during synchronization',
|
||||
CONFIRM_SYNC_ALERTS: 'Are you sure you want to synchronize alerts from this server?',
|
||||
CONFIRM_SYNC_ITEM: 'Are you sure you want to sync alerts for this item?',
|
||||
CONFIRM_TOGGLE_STATUS: 'Are you sure you want to change the status of this server?'
|
||||
};
|
||||
|
||||
// Initialize when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
WazuhPlugin.init();
|
||||
});
|
||||
|
||||
// Expose to global scope for legacy compatibility
|
||||
window.WazuhPlugin = WazuhPlugin;
|
||||
window.testWazuhConnection = WazuhPlugin.testConnection.bind(WazuhPlugin);
|
||||
window.syncWazuhAlerts = WazuhPlugin.syncAlerts.bind(WazuhPlugin);
|
||||
window.syncAlertsForItem = WazuhPlugin.syncAlertsForItem.bind(WazuhPlugin);
|
||||
window.toggleServerStatus = WazuhPlugin.toggleServerStatus.bind(WazuhPlugin);
|
||||
window.checkAll = WazuhPlugin.checkAllAlerts.bind(WazuhPlugin);
|
0
locales/de_DE.mo
Normal file
0
locales/de_DE.mo
Normal file
188
locales/de_DE.po
Normal file
188
locales/de_DE.po
Normal file
@@ -0,0 +1,188 @@
|
||||
# SIEM-Wazuh Plugin for GLPI - German Translation
|
||||
# Copyright (C) 2024 SIEM-Wazuh Team
|
||||
# This file is distributed under the same license as GLPI.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: GLPI Plugin SIEM-Wazuh 1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-01-01 00:00+0000\n"
|
||||
"PO-Revision-Date: 2024-01-01 00:00+0000\n"
|
||||
"Last-Translator: SIEM-Wazuh Team\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de_DE\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
# Plugin information
|
||||
msgid "SIEM - Wazuh"
|
||||
msgstr "SIEM - Wazuh"
|
||||
|
||||
msgid "Wazuh Server"
|
||||
msgid_plural "Wazuh Servers"
|
||||
msgstr[0] "Wazuh Server"
|
||||
msgstr[1] "Wazuh Server"
|
||||
|
||||
msgid "Wazuh Alert"
|
||||
msgid_plural "Wazuh Alerts"
|
||||
msgstr[0] "Wazuh Warnung"
|
||||
msgstr[1] "Wazuh Warnungen"
|
||||
|
||||
msgid "SIEM Wazuh Configuration"
|
||||
msgstr "SIEM Wazuh Konfiguration"
|
||||
|
||||
# Server configuration
|
||||
msgid "Wazuh Server URL"
|
||||
msgstr "Wazuh Server URL"
|
||||
|
||||
msgid "Wazuh API Port"
|
||||
msgstr "Wazuh API Port"
|
||||
|
||||
msgid "Wazuh API Login"
|
||||
msgstr "Wazuh API Anmeldung"
|
||||
|
||||
msgid "Wazuh API Password"
|
||||
msgstr "Wazuh API Passwort"
|
||||
|
||||
msgid "Indexer Server URL"
|
||||
msgstr "Indexer Server URL"
|
||||
|
||||
msgid "Sync Interval (seconds)"
|
||||
msgstr "Synchronisationsintervall (Sekunden)"
|
||||
|
||||
msgid "Last Sync"
|
||||
msgstr "Letzte Synchronisation"
|
||||
|
||||
msgid "Default Ticket Type"
|
||||
msgstr "Standard Ticket-Typ"
|
||||
|
||||
msgid "Default Ticket Category"
|
||||
msgstr "Standard Ticket-Kategorie"
|
||||
|
||||
# Alerts
|
||||
msgid "Alert ID"
|
||||
msgstr "Warn-ID"
|
||||
|
||||
msgid "Rule ID"
|
||||
msgstr "Regel-ID"
|
||||
|
||||
msgid "Rule Level"
|
||||
msgstr "Regelebene"
|
||||
|
||||
msgid "Rule Description"
|
||||
msgstr "Regelbeschreibung"
|
||||
|
||||
msgid "Agent Name"
|
||||
msgstr "Agent Name"
|
||||
|
||||
msgid "Agent IP"
|
||||
msgstr "Agent IP"
|
||||
|
||||
msgid "Alert Timestamp"
|
||||
msgstr "Warnzeit"
|
||||
|
||||
msgid "Severity"
|
||||
msgstr "Schweregrad"
|
||||
|
||||
# Status values
|
||||
msgid "New"
|
||||
msgstr "Neu"
|
||||
|
||||
msgid "Processed"
|
||||
msgstr "Bearbeitet"
|
||||
|
||||
msgid "Ignored"
|
||||
msgstr "Ignoriert"
|
||||
|
||||
msgid "Ticket Created"
|
||||
msgstr "Ticket erstellt"
|
||||
|
||||
# Severity values
|
||||
msgid "Low"
|
||||
msgstr "Niedrig"
|
||||
|
||||
msgid "Medium"
|
||||
msgstr "Mittel"
|
||||
|
||||
msgid "High"
|
||||
msgstr "Hoch"
|
||||
|
||||
msgid "Critical"
|
||||
msgstr "Kritisch"
|
||||
|
||||
# Actions
|
||||
msgid "Test Connection"
|
||||
msgstr "Verbindung testen"
|
||||
|
||||
msgid "Sync Alerts"
|
||||
msgstr "Warnungen synchronisieren"
|
||||
|
||||
msgid "Sync Now"
|
||||
msgstr "Jetzt synchronisieren"
|
||||
|
||||
msgid "Export CSV"
|
||||
msgstr "CSV exportieren"
|
||||
|
||||
# Configuration
|
||||
msgid "General Configuration"
|
||||
msgstr "Allgemeine Konfiguration"
|
||||
|
||||
msgid "Enable synchronization"
|
||||
msgstr "Synchronisation aktivieren"
|
||||
|
||||
msgid "Auto-create tickets"
|
||||
msgstr "Tickets automatisch erstellen"
|
||||
|
||||
msgid "Maximum alerts per sync"
|
||||
msgstr "Maximale Warnungen pro Synchronisation"
|
||||
|
||||
msgid "Alert retention (days)"
|
||||
msgstr "Warnungsaufbewahrung (Tage)"
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr "Konfiguration"
|
||||
|
||||
msgid "Servers"
|
||||
msgstr "Server"
|
||||
|
||||
msgid "Alerts"
|
||||
msgstr "Warnungen"
|
||||
|
||||
# Messages
|
||||
msgid "Connection successful"
|
||||
msgstr "Verbindung erfolgreich"
|
||||
|
||||
msgid "Connection failed:"
|
||||
msgstr "Verbindung fehlgeschlagen:"
|
||||
|
||||
msgid "Server activated"
|
||||
msgstr "Server aktiviert"
|
||||
|
||||
msgid "Server deactivated"
|
||||
msgstr "Server deaktiviert"
|
||||
|
||||
msgid "Testing..."
|
||||
msgstr "Teste..."
|
||||
|
||||
msgid "Synchronizing..."
|
||||
msgstr "Synchronisiere..."
|
||||
|
||||
msgid "Never"
|
||||
msgstr "Nie"
|
||||
|
||||
msgid "Statistics"
|
||||
msgstr "Statistiken"
|
||||
|
||||
msgid "Total servers"
|
||||
msgstr "Server insgesamt"
|
||||
|
||||
msgid "Active servers"
|
||||
msgstr "Aktive Server"
|
||||
|
||||
msgid "Total alerts"
|
||||
msgstr "Warnungen insgesamt"
|
||||
|
||||
msgid "New alerts"
|
||||
msgstr "Neue Warnungen"
|
0
locales/en_GB.mo
Normal file
0
locales/en_GB.mo
Normal file
543
locales/en_GB.po
Normal file
543
locales/en_GB.po
Normal file
@@ -0,0 +1,543 @@
|
||||
# SIEM-Wazuh Plugin for GLPI - English Translation
|
||||
# Copyright (C) 2024 SIEM-Wazuh Team
|
||||
# This file is distributed under the same license as GLPI.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: GLPI Plugin SIEM-Wazuh 1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-01-01 00:00+0000\n"
|
||||
"PO-Revision-Date: 2024-01-01 00:00+0000\n"
|
||||
"Last-Translator: SIEM-Wazuh Team\n"
|
||||
"Language-Team: English\n"
|
||||
"Language: en_GB\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
# Plugin information
|
||||
msgid "SIEM - Wazuh"
|
||||
msgstr "SIEM - Wazuh"
|
||||
|
||||
msgid "Wazuh Server"
|
||||
msgid_plural "Wazuh Servers"
|
||||
msgstr[0] "Wazuh Server"
|
||||
msgstr[1] "Wazuh Servers"
|
||||
|
||||
msgid "Wazuh Alert"
|
||||
msgid_plural "Wazuh Alerts"
|
||||
msgstr[0] "Wazuh Alert"
|
||||
msgstr[1] "Wazuh Alerts"
|
||||
|
||||
msgid "SIEM Wazuh Configuration"
|
||||
msgstr "SIEM Wazuh Configuration"
|
||||
|
||||
# Server configuration
|
||||
msgid "Wazuh Server URL"
|
||||
msgstr "Wazuh Server URL"
|
||||
|
||||
msgid "Wazuh API Port"
|
||||
msgstr "Wazuh API Port"
|
||||
|
||||
msgid "Wazuh API Login"
|
||||
msgstr "Wazuh API Login"
|
||||
|
||||
msgid "Wazuh API Password"
|
||||
msgstr "Wazuh API Password"
|
||||
|
||||
msgid "Indexer Server URL"
|
||||
msgstr "Indexer Server URL"
|
||||
|
||||
msgid "Indexer API Port"
|
||||
msgstr "Indexer API Port"
|
||||
|
||||
msgid "Indexer API Login"
|
||||
msgstr "Indexer API Login"
|
||||
|
||||
msgid "Indexer API Password"
|
||||
msgstr "Indexer API Password"
|
||||
|
||||
msgid "Sync Interval (seconds)"
|
||||
msgstr "Sync Interval (seconds)"
|
||||
|
||||
msgid "Last Sync"
|
||||
msgstr "Last Sync"
|
||||
|
||||
msgid "Default Ticket Type"
|
||||
msgstr "Default Ticket Type"
|
||||
|
||||
msgid "Default Ticket Category"
|
||||
msgstr "Default Ticket Category"
|
||||
|
||||
msgid "Leave empty to keep current password"
|
||||
msgstr "Leave empty to keep current password"
|
||||
|
||||
# Alerts
|
||||
msgid "Alert ID"
|
||||
msgstr "Alert ID"
|
||||
|
||||
msgid "Rule ID"
|
||||
msgstr "Rule ID"
|
||||
|
||||
msgid "Rule Level"
|
||||
msgstr "Rule Level"
|
||||
|
||||
msgid "Rule Description"
|
||||
msgstr "Rule Description"
|
||||
|
||||
msgid "Agent Name"
|
||||
msgstr "Agent Name"
|
||||
|
||||
msgid "Agent IP"
|
||||
msgstr "Agent IP"
|
||||
|
||||
msgid "Alert Timestamp"
|
||||
msgstr "Alert Timestamp"
|
||||
|
||||
msgid "Severity"
|
||||
msgstr "Severity"
|
||||
|
||||
msgid "Associated Computer"
|
||||
msgstr "Associated Computer"
|
||||
|
||||
msgid "Associated Network Equipment"
|
||||
msgstr "Associated Network Equipment"
|
||||
|
||||
msgid "Associated Ticket"
|
||||
msgstr "Associated Ticket"
|
||||
|
||||
msgid "Raw Alert Data"
|
||||
msgstr "Raw Alert Data"
|
||||
|
||||
# Status values
|
||||
msgid "New"
|
||||
msgstr "New"
|
||||
|
||||
msgid "Processed"
|
||||
msgstr "Processed"
|
||||
|
||||
msgid "Ignored"
|
||||
msgstr "Ignored"
|
||||
|
||||
msgid "Ticket Created"
|
||||
msgstr "Ticket Created"
|
||||
|
||||
# Severity values
|
||||
msgid "Low"
|
||||
msgstr "Low"
|
||||
|
||||
msgid "Medium"
|
||||
msgstr "Medium"
|
||||
|
||||
msgid "High"
|
||||
msgstr "High"
|
||||
|
||||
msgid "Critical"
|
||||
msgstr "Critical"
|
||||
|
||||
# Actions
|
||||
msgid "Test Connection"
|
||||
msgstr "Test Connection"
|
||||
|
||||
msgid "Sync Alerts"
|
||||
msgstr "Sync Alerts"
|
||||
|
||||
msgid "Sync Now"
|
||||
msgstr "Sync Now"
|
||||
|
||||
msgid "Export CSV"
|
||||
msgstr "Export CSV"
|
||||
|
||||
msgid "Mark as Processed"
|
||||
msgstr "Mark as Processed"
|
||||
|
||||
msgid "Mark as Ignored"
|
||||
msgstr "Mark as Ignored"
|
||||
|
||||
msgid "Create Tickets"
|
||||
msgstr "Create Tickets"
|
||||
|
||||
# Configuration
|
||||
msgid "General Configuration"
|
||||
msgstr "General Configuration"
|
||||
|
||||
msgid "Enable synchronization"
|
||||
msgstr "Enable synchronization"
|
||||
|
||||
msgid "Auto-create tickets"
|
||||
msgstr "Auto-create tickets"
|
||||
|
||||
msgid "Maximum alerts per sync"
|
||||
msgstr "Maximum alerts per sync"
|
||||
|
||||
msgid "Alert retention (days)"
|
||||
msgstr "Alert retention (days)"
|
||||
|
||||
msgid "Minimum rule level"
|
||||
msgstr "Minimum rule level"
|
||||
|
||||
msgid "Enable notifications"
|
||||
msgstr "Enable notifications"
|
||||
|
||||
msgid "Default Ticket Settings"
|
||||
msgstr "Default Ticket Settings"
|
||||
|
||||
msgid "Default Priority"
|
||||
msgstr "Default Priority"
|
||||
|
||||
msgid "Default Urgency"
|
||||
msgstr "Default Urgency"
|
||||
|
||||
msgid "Default Impact"
|
||||
msgstr "Default Impact"
|
||||
|
||||
msgid "Auto-assign tickets"
|
||||
msgstr "Auto-assign tickets"
|
||||
|
||||
msgid "Default Assignee"
|
||||
msgstr "Default Assignee"
|
||||
|
||||
msgid "Default Group"
|
||||
msgstr "Default Group"
|
||||
|
||||
msgid "Asset Mapping Configuration"
|
||||
msgstr "Asset Mapping Configuration"
|
||||
|
||||
msgid "Match by hostname"
|
||||
msgstr "Match by hostname"
|
||||
|
||||
msgid "Match by IP address"
|
||||
msgstr "Match by IP address"
|
||||
|
||||
msgid "Case sensitive matching"
|
||||
msgstr "Case sensitive matching"
|
||||
|
||||
msgid "Create unknown assets"
|
||||
msgstr "Create unknown assets"
|
||||
|
||||
msgid "Debug Configuration"
|
||||
msgstr "Debug Configuration"
|
||||
|
||||
msgid "Enable debug mode"
|
||||
msgstr "Enable debug mode"
|
||||
|
||||
msgid "Log level"
|
||||
msgstr "Log level"
|
||||
|
||||
msgid "Keep debug logs (days)"
|
||||
msgstr "Keep debug logs (days)"
|
||||
|
||||
msgid "Error only"
|
||||
msgstr "Error only"
|
||||
|
||||
msgid "Warning and above"
|
||||
msgstr "Warning and above"
|
||||
|
||||
msgid "Info and above"
|
||||
msgstr "Info and above"
|
||||
|
||||
msgid "All messages"
|
||||
msgstr "All messages"
|
||||
|
||||
# Statistics
|
||||
msgid "Statistics"
|
||||
msgstr "Statistics"
|
||||
|
||||
msgid "Total servers"
|
||||
msgstr "Total servers"
|
||||
|
||||
msgid "Active servers"
|
||||
msgstr "Active servers"
|
||||
|
||||
msgid "Total alerts"
|
||||
msgstr "Total alerts"
|
||||
|
||||
msgid "New alerts"
|
||||
msgstr "New alerts"
|
||||
|
||||
msgid "Processed alerts"
|
||||
msgstr "Processed alerts"
|
||||
|
||||
msgid "Tickets created"
|
||||
msgstr "Tickets created"
|
||||
|
||||
msgid "%s alerts"
|
||||
msgstr "%s alerts"
|
||||
|
||||
msgid "Last global sync"
|
||||
msgstr "Last global sync"
|
||||
|
||||
msgid "Never"
|
||||
msgstr "Never"
|
||||
|
||||
# Tab interface
|
||||
msgid "Wazuh Alerts"
|
||||
msgstr "Wazuh Alerts"
|
||||
|
||||
msgid "Alerts Summary"
|
||||
msgstr "Alerts Summary"
|
||||
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
msgid "Filters"
|
||||
msgstr "Filters"
|
||||
|
||||
msgid "Date from"
|
||||
msgstr "Date from"
|
||||
|
||||
msgid "Date to"
|
||||
msgstr "Date to"
|
||||
|
||||
msgid "Reset"
|
||||
msgstr "Reset"
|
||||
|
||||
msgid "Filter"
|
||||
msgstr "Filter"
|
||||
|
||||
msgid "No Wazuh alerts found for this item"
|
||||
msgstr "No Wazuh alerts found for this item"
|
||||
|
||||
msgid "No alerts found"
|
||||
msgstr "No alerts found"
|
||||
|
||||
msgid "Mass Actions"
|
||||
msgstr "Mass Actions"
|
||||
|
||||
msgid "Sync new alerts"
|
||||
msgstr "Sync new alerts"
|
||||
|
||||
msgid "Export alerts"
|
||||
msgstr "Export alerts"
|
||||
|
||||
# Messages
|
||||
msgid "Connection successful"
|
||||
msgstr "Connection successful"
|
||||
|
||||
msgid "Connection failed:"
|
||||
msgstr "Connection failed:"
|
||||
|
||||
msgid "Authentication failed:"
|
||||
msgstr "Authentication failed:"
|
||||
|
||||
msgid "Connection error:"
|
||||
msgstr "Connection error:"
|
||||
|
||||
msgid "Missing connection parameters"
|
||||
msgstr "Missing connection parameters"
|
||||
|
||||
msgid "Server is not active"
|
||||
msgstr "Server is not active"
|
||||
|
||||
msgid "%d alerts processed"
|
||||
msgstr "%d alerts processed"
|
||||
|
||||
msgid "Configuration updated successfully"
|
||||
msgstr "Configuration updated successfully"
|
||||
|
||||
msgid "Server activated"
|
||||
msgstr "Server activated"
|
||||
|
||||
msgid "Server deactivated"
|
||||
msgstr "Server deactivated"
|
||||
|
||||
msgid "Testing..."
|
||||
msgstr "Testing..."
|
||||
|
||||
msgid "Connection test failed"
|
||||
msgstr "Connection test failed"
|
||||
|
||||
msgid "Synchronizing..."
|
||||
msgstr "Synchronizing..."
|
||||
|
||||
msgid "Sync completed successfully"
|
||||
msgstr "Sync completed successfully"
|
||||
|
||||
msgid "Sync failed:"
|
||||
msgstr "Sync failed:"
|
||||
|
||||
msgid "An error occurred during sync"
|
||||
msgstr "An error occurred during sync"
|
||||
|
||||
msgid "An error occurred during synchronization"
|
||||
msgstr "An error occurred during synchronization"
|
||||
|
||||
msgid "Are you sure you want to change the status of this server?"
|
||||
msgstr "Are you sure you want to change the status of this server?"
|
||||
|
||||
msgid "Are you sure you want to synchronize alerts from this server?"
|
||||
msgstr "Are you sure you want to synchronize alerts from this server?"
|
||||
|
||||
msgid "Are you sure you want to sync alerts for this item?"
|
||||
msgstr "Are you sure you want to sync alerts for this item?"
|
||||
|
||||
# Ticket content
|
||||
msgid "Wazuh Alert: %s"
|
||||
msgstr "Wazuh Alert: %s"
|
||||
|
||||
msgid "Alert ID: %s"
|
||||
msgstr "Alert ID: %s"
|
||||
|
||||
msgid "Rule ID: %s"
|
||||
msgstr "Rule ID: %s"
|
||||
|
||||
msgid "Rule Level: %s"
|
||||
msgstr "Rule Level: %s"
|
||||
|
||||
msgid "Agent: %s (%s)"
|
||||
msgstr "Agent: %s (%s)"
|
||||
|
||||
msgid "Timestamp: %s"
|
||||
msgstr "Timestamp: %s"
|
||||
|
||||
msgid "Description: %s"
|
||||
msgstr "Description: %s"
|
||||
|
||||
msgid "Raw Alert Data:"
|
||||
msgstr "Raw Alert Data:"
|
||||
|
||||
# Cron tasks
|
||||
msgid "Synchronize Wazuh alerts"
|
||||
msgstr "Synchronize Wazuh alerts"
|
||||
|
||||
msgid "Cleanup old alerts"
|
||||
msgstr "Cleanup old alerts"
|
||||
|
||||
# Rights
|
||||
msgid "SIEM Wazuh Rights"
|
||||
msgstr "SIEM Wazuh Rights"
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr "Configuration"
|
||||
|
||||
msgid "Servers"
|
||||
msgstr "Servers"
|
||||
|
||||
msgid "Alerts"
|
||||
msgstr "Alerts"
|
||||
|
||||
# Error messages
|
||||
msgid "Access denied"
|
||||
msgstr "Access denied"
|
||||
|
||||
msgid "Invalid CSRF token"
|
||||
msgstr "Invalid CSRF token"
|
||||
|
||||
msgid "Invalid server ID"
|
||||
msgstr "Invalid server ID"
|
||||
|
||||
msgid "Server not found"
|
||||
msgstr "Server not found"
|
||||
|
||||
msgid "Invalid item parameters"
|
||||
msgstr "Invalid item parameters"
|
||||
|
||||
msgid "Invalid item type"
|
||||
msgstr "Invalid item type"
|
||||
|
||||
msgid "Item not found"
|
||||
msgstr "Item not found"
|
||||
|
||||
msgid "Unknown action"
|
||||
msgstr "Unknown action"
|
||||
|
||||
msgid "Request failed after maximum retries"
|
||||
msgstr "Request failed after maximum retries"
|
||||
|
||||
msgid "Indexer not configured"
|
||||
msgstr "Indexer not configured"
|
||||
|
||||
msgid "Authentication failed"
|
||||
msgstr "Authentication failed"
|
||||
|
||||
msgid "Status retrieved successfully"
|
||||
msgstr "Status retrieved successfully"
|
||||
|
||||
# Additional configuration
|
||||
msgid "Asset Mapping"
|
||||
msgstr "Asset Mapping"
|
||||
|
||||
msgid "Notifications"
|
||||
msgstr "Notifications"
|
||||
|
||||
msgid "Debug & Logs"
|
||||
msgstr "Debug & Logs"
|
||||
|
||||
msgid "Email Notifications"
|
||||
msgstr "Email Notifications"
|
||||
|
||||
msgid "Enable email notifications"
|
||||
msgstr "Enable email notifications"
|
||||
|
||||
msgid "Notification for critical alerts only"
|
||||
msgstr "Notification for critical alerts only"
|
||||
|
||||
msgid "Default notification recipients"
|
||||
msgstr "Default notification recipients"
|
||||
|
||||
msgid "Comma-separated email addresses"
|
||||
msgstr "Comma-separated email addresses"
|
||||
|
||||
msgid "Asset Detection Rules"
|
||||
msgstr "Asset Detection Rules"
|
||||
|
||||
msgid "Match computers by hostname"
|
||||
msgstr "Match computers by hostname"
|
||||
|
||||
msgid "Match network equipment by hostname"
|
||||
msgstr "Match network equipment by hostname"
|
||||
|
||||
msgid "Hostname patterns to ignore"
|
||||
msgstr "Hostname patterns to ignore"
|
||||
|
||||
msgid "One pattern per line. Use * as wildcard."
|
||||
msgstr "One pattern per line. Use * as wildcard."
|
||||
|
||||
msgid "Test Asset Mapping"
|
||||
msgstr "Test Asset Mapping"
|
||||
|
||||
msgid "Agent name or IP"
|
||||
msgstr "Agent name or IP"
|
||||
|
||||
msgid "Test"
|
||||
msgstr "Test"
|
||||
|
||||
msgid "Please enter an agent name or IP address"
|
||||
msgstr "Please enter an agent name or IP address"
|
||||
|
||||
msgid "Test failed"
|
||||
msgstr "Test failed"
|
||||
|
||||
msgid "Log API requests"
|
||||
msgstr "Log API requests"
|
||||
|
||||
msgid "Keep logs for (days)"
|
||||
msgstr "Keep logs for (days)"
|
||||
|
||||
msgid "Clear All Logs"
|
||||
msgstr "Clear All Logs"
|
||||
|
||||
msgid "Are you sure you want to clear all logs?"
|
||||
msgstr "Are you sure you want to clear all logs?"
|
||||
|
||||
msgid "Logs cleared successfully"
|
||||
msgstr "Logs cleared successfully"
|
||||
|
||||
msgid "Failed to clear logs"
|
||||
msgstr "Failed to clear logs"
|
||||
|
||||
msgid "Recent Logs"
|
||||
msgstr "Recent Logs"
|
||||
|
||||
msgid "Level"
|
||||
msgstr "Level"
|
||||
|
||||
msgid "Message"
|
||||
msgstr "Message"
|
||||
|
||||
msgid "System"
|
||||
msgstr "System"
|
||||
|
||||
msgid "No logs found"
|
||||
msgstr "No logs found"
|
0
locales/es_ES.mo
Normal file
0
locales/es_ES.mo
Normal file
0
locales/es_ES.po
Normal file
0
locales/es_ES.po
Normal file
0
locales/fr_FR.mo
Normal file
0
locales/fr_FR.mo
Normal file
543
locales/fr_FR.po
Normal file
543
locales/fr_FR.po
Normal file
@@ -0,0 +1,543 @@
|
||||
# SIEM-Wazuh Plugin for GLPI - French Translation
|
||||
# Copyright (C) 2024 SIEM-Wazuh Team
|
||||
# This file is distributed under the same license as GLPI.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: GLPI Plugin SIEM-Wazuh 1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-01-01 00:00+0000\n"
|
||||
"PO-Revision-Date: 2024-01-01 00:00+0000\n"
|
||||
"Last-Translator: SIEM-Wazuh Team\n"
|
||||
"Language-Team: French\n"
|
||||
"Language: fr_FR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
# Plugin information
|
||||
msgid "SIEM - Wazuh"
|
||||
msgstr "SIEM - Wazuh"
|
||||
|
||||
msgid "Wazuh Server"
|
||||
msgid_plural "Wazuh Servers"
|
||||
msgstr[0] "Serveur Wazuh"
|
||||
msgstr[1] "Serveurs Wazuh"
|
||||
|
||||
msgid "Wazuh Alert"
|
||||
msgid_plural "Wazuh Alerts"
|
||||
msgstr[0] "Alerte Wazuh"
|
||||
msgstr[1] "Alertes Wazuh"
|
||||
|
||||
msgid "SIEM Wazuh Configuration"
|
||||
msgstr "Configuration SIEM Wazuh"
|
||||
|
||||
# Server configuration
|
||||
msgid "Wazuh Server URL"
|
||||
msgstr "URL du serveur Wazuh"
|
||||
|
||||
msgid "Wazuh API Port"
|
||||
msgstr "Port API Wazuh"
|
||||
|
||||
msgid "Wazuh API Login"
|
||||
msgstr "Login API Wazuh"
|
||||
|
||||
msgid "Wazuh API Password"
|
||||
msgstr "Mot de passe API Wazuh"
|
||||
|
||||
msgid "Indexer Server URL"
|
||||
msgstr "URL du serveur Indexer"
|
||||
|
||||
msgid "Indexer API Port"
|
||||
msgstr "Port API Indexer"
|
||||
|
||||
msgid "Indexer API Login"
|
||||
msgstr "Login API Indexer"
|
||||
|
||||
msgid "Indexer API Password"
|
||||
msgstr "Mot de passe API Indexer"
|
||||
|
||||
msgid "Sync Interval (seconds)"
|
||||
msgstr "Intervalle de synchronisation (secondes)"
|
||||
|
||||
msgid "Last Sync"
|
||||
msgstr "Dernière synchronisation"
|
||||
|
||||
msgid "Default Ticket Type"
|
||||
msgstr "Type de ticket par défaut"
|
||||
|
||||
msgid "Default Ticket Category"
|
||||
msgstr "Catégorie de ticket par défaut"
|
||||
|
||||
msgid "Leave empty to keep current password"
|
||||
msgstr "Laisser vide pour conserver le mot de passe actuel"
|
||||
|
||||
# Alerts
|
||||
msgid "Alert ID"
|
||||
msgstr "ID d'alerte"
|
||||
|
||||
msgid "Rule ID"
|
||||
msgstr "ID de règle"
|
||||
|
||||
msgid "Rule Level"
|
||||
msgstr "Niveau de règle"
|
||||
|
||||
msgid "Rule Description"
|
||||
msgstr "Description de la règle"
|
||||
|
||||
msgid "Agent Name"
|
||||
msgstr "Nom de l'agent"
|
||||
|
||||
msgid "Agent IP"
|
||||
msgstr "IP de l'agent"
|
||||
|
||||
msgid "Alert Timestamp"
|
||||
msgstr "Horodatage de l'alerte"
|
||||
|
||||
msgid "Severity"
|
||||
msgstr "Sévérité"
|
||||
|
||||
msgid "Associated Computer"
|
||||
msgstr "Ordinateur associé"
|
||||
|
||||
msgid "Associated Network Equipment"
|
||||
msgstr "Équipement réseau associé"
|
||||
|
||||
msgid "Associated Ticket"
|
||||
msgstr "Ticket associé"
|
||||
|
||||
msgid "Raw Alert Data"
|
||||
msgstr "Données brutes de l'alerte"
|
||||
|
||||
# Status values
|
||||
msgid "New"
|
||||
msgstr "Nouveau"
|
||||
|
||||
msgid "Processed"
|
||||
msgstr "Traité"
|
||||
|
||||
msgid "Ignored"
|
||||
msgstr "Ignoré"
|
||||
|
||||
msgid "Ticket Created"
|
||||
msgstr "Ticket créé"
|
||||
|
||||
# Severity values
|
||||
msgid "Low"
|
||||
msgstr "Faible"
|
||||
|
||||
msgid "Medium"
|
||||
msgstr "Moyen"
|
||||
|
||||
msgid "High"
|
||||
msgstr "Élevé"
|
||||
|
||||
msgid "Critical"
|
||||
msgstr "Critique"
|
||||
|
||||
# Actions
|
||||
msgid "Test Connection"
|
||||
msgstr "Tester la connexion"
|
||||
|
||||
msgid "Sync Alerts"
|
||||
msgstr "Synchroniser les alertes"
|
||||
|
||||
msgid "Sync Now"
|
||||
msgstr "Synchroniser maintenant"
|
||||
|
||||
msgid "Export CSV"
|
||||
msgstr "Exporter CSV"
|
||||
|
||||
msgid "Mark as Processed"
|
||||
msgstr "Marquer comme traité"
|
||||
|
||||
msgid "Mark as Ignored"
|
||||
msgstr "Marquer comme ignoré"
|
||||
|
||||
msgid "Create Tickets"
|
||||
msgstr "Créer des tickets"
|
||||
|
||||
# Configuration
|
||||
msgid "General Configuration"
|
||||
msgstr "Configuration générale"
|
||||
|
||||
msgid "Enable synchronization"
|
||||
msgstr "Activer la synchronisation"
|
||||
|
||||
msgid "Auto-create tickets"
|
||||
msgstr "Création automatique de tickets"
|
||||
|
||||
msgid "Maximum alerts per sync"
|
||||
msgstr "Maximum d'alertes par synchronisation"
|
||||
|
||||
msgid "Alert retention (days)"
|
||||
msgstr "Rétention des alertes (jours)"
|
||||
|
||||
msgid "Minimum rule level"
|
||||
msgstr "Niveau de règle minimum"
|
||||
|
||||
msgid "Enable notifications"
|
||||
msgstr "Activer les notifications"
|
||||
|
||||
msgid "Default Ticket Settings"
|
||||
msgstr "Paramètres de ticket par défaut"
|
||||
|
||||
msgid "Default Priority"
|
||||
msgstr "Priorité par défaut"
|
||||
|
||||
msgid "Default Urgency"
|
||||
msgstr "Urgence par défaut"
|
||||
|
||||
msgid "Default Impact"
|
||||
msgstr "Impact par défaut"
|
||||
|
||||
msgid "Auto-assign tickets"
|
||||
msgstr "Attribution automatique des tickets"
|
||||
|
||||
msgid "Default Assignee"
|
||||
msgstr "Assigné par défaut"
|
||||
|
||||
msgid "Default Group"
|
||||
msgstr "Groupe par défaut"
|
||||
|
||||
msgid "Asset Mapping Configuration"
|
||||
msgstr "Configuration du mapping des assets"
|
||||
|
||||
msgid "Match by hostname"
|
||||
msgstr "Correspondance par nom d'hôte"
|
||||
|
||||
msgid "Match by IP address"
|
||||
msgstr "Correspondance par adresse IP"
|
||||
|
||||
msgid "Case sensitive matching"
|
||||
msgstr "Correspondance sensible à la casse"
|
||||
|
||||
msgid "Create unknown assets"
|
||||
msgstr "Créer les assets inconnus"
|
||||
|
||||
msgid "Debug Configuration"
|
||||
msgstr "Configuration de débogage"
|
||||
|
||||
msgid "Enable debug mode"
|
||||
msgstr "Activer le mode débogage"
|
||||
|
||||
msgid "Log level"
|
||||
msgstr "Niveau de log"
|
||||
|
||||
msgid "Keep debug logs (days)"
|
||||
msgstr "Conserver les logs de débogage (jours)"
|
||||
|
||||
msgid "Error only"
|
||||
msgstr "Erreurs seulement"
|
||||
|
||||
msgid "Warning and above"
|
||||
msgstr "Avertissements et plus"
|
||||
|
||||
msgid "Info and above"
|
||||
msgstr "Informations et plus"
|
||||
|
||||
msgid "All messages"
|
||||
msgstr "Tous les messages"
|
||||
|
||||
# Statistics
|
||||
msgid "Statistics"
|
||||
msgstr "Statistiques"
|
||||
|
||||
msgid "Total servers"
|
||||
msgstr "Total des serveurs"
|
||||
|
||||
msgid "Active servers"
|
||||
msgstr "Serveurs actifs"
|
||||
|
||||
msgid "Total alerts"
|
||||
msgstr "Total des alertes"
|
||||
|
||||
msgid "New alerts"
|
||||
msgstr "Nouvelles alertes"
|
||||
|
||||
msgid "Processed alerts"
|
||||
msgstr "Alertes traitées"
|
||||
|
||||
msgid "Tickets created"
|
||||
msgstr "Tickets créés"
|
||||
|
||||
msgid "%s alerts"
|
||||
msgstr "Alertes %s"
|
||||
|
||||
msgid "Last global sync"
|
||||
msgstr "Dernière synchronisation globale"
|
||||
|
||||
msgid "Never"
|
||||
msgstr "Jamais"
|
||||
|
||||
# Tab interface
|
||||
msgid "Wazuh Alerts"
|
||||
msgstr "Alertes Wazuh"
|
||||
|
||||
msgid "Alerts Summary"
|
||||
msgstr "Résumé des alertes"
|
||||
|
||||
msgid "Total"
|
||||
msgstr "Total"
|
||||
|
||||
msgid "Filters"
|
||||
msgstr "Filtres"
|
||||
|
||||
msgid "Date from"
|
||||
msgstr "Date de début"
|
||||
|
||||
msgid "Date to"
|
||||
msgstr "Date de fin"
|
||||
|
||||
msgid "Reset"
|
||||
msgstr "Réinitialiser"
|
||||
|
||||
msgid "Filter"
|
||||
msgstr "Filtrer"
|
||||
|
||||
msgid "No Wazuh alerts found for this item"
|
||||
msgstr "Aucune alerte Wazuh trouvée pour cet élément"
|
||||
|
||||
msgid "No alerts found"
|
||||
msgstr "Aucune alerte trouvée"
|
||||
|
||||
msgid "Mass Actions"
|
||||
msgstr "Actions de masse"
|
||||
|
||||
msgid "Sync new alerts"
|
||||
msgstr "Synchroniser les nouvelles alertes"
|
||||
|
||||
msgid "Export alerts"
|
||||
msgstr "Exporter les alertes"
|
||||
|
||||
# Messages
|
||||
msgid "Connection successful"
|
||||
msgstr "Connexion réussie"
|
||||
|
||||
msgid "Connection failed:"
|
||||
msgstr "Échec de la connexion :"
|
||||
|
||||
msgid "Authentication failed:"
|
||||
msgstr "Échec de l'authentification :"
|
||||
|
||||
msgid "Connection error:"
|
||||
msgstr "Erreur de connexion :"
|
||||
|
||||
msgid "Missing connection parameters"
|
||||
msgstr "Paramètres de connexion manquants"
|
||||
|
||||
msgid "Server is not active"
|
||||
msgstr "Le serveur n'est pas actif"
|
||||
|
||||
msgid "%d alerts processed"
|
||||
msgstr "%d alertes traitées"
|
||||
|
||||
msgid "Configuration updated successfully"
|
||||
msgstr "Configuration mise à jour avec succès"
|
||||
|
||||
msgid "Server activated"
|
||||
msgstr "Serveur activé"
|
||||
|
||||
msgid "Server deactivated"
|
||||
msgstr "Serveur désactivé"
|
||||
|
||||
msgid "Testing..."
|
||||
msgstr "Test en cours..."
|
||||
|
||||
msgid "Connection test failed"
|
||||
msgstr "Échec du test de connexion"
|
||||
|
||||
msgid "Synchronizing..."
|
||||
msgstr "Synchronisation en cours..."
|
||||
|
||||
msgid "Sync completed successfully"
|
||||
msgstr "Synchronisation terminée avec succès"
|
||||
|
||||
msgid "Sync failed:"
|
||||
msgstr "Échec de la synchronisation :"
|
||||
|
||||
msgid "An error occurred during sync"
|
||||
msgstr "Une erreur s'est produite lors de la synchronisation"
|
||||
|
||||
msgid "An error occurred during synchronization"
|
||||
msgstr "Une erreur s'est produite lors de la synchronisation"
|
||||
|
||||
msgid "Are you sure you want to change the status of this server?"
|
||||
msgstr "Êtes-vous sûr de vouloir changer le statut de ce serveur ?"
|
||||
|
||||
msgid "Are you sure you want to synchronize alerts from this server?"
|
||||
msgstr "Êtes-vous sûr de vouloir synchroniser les alertes de ce serveur ?"
|
||||
|
||||
msgid "Are you sure you want to sync alerts for this item?"
|
||||
msgstr "Êtes-vous sûr de vouloir synchroniser les alertes pour cet élément ?"
|
||||
|
||||
# Ticket content
|
||||
msgid "Wazuh Alert: %s"
|
||||
msgstr "Alerte Wazuh : %s"
|
||||
|
||||
msgid "Alert ID: %s"
|
||||
msgstr "ID d'alerte : %s"
|
||||
|
||||
msgid "Rule ID: %s"
|
||||
msgstr "ID de règle : %s"
|
||||
|
||||
msgid "Rule Level: %s"
|
||||
msgstr "Niveau de règle : %s"
|
||||
|
||||
msgid "Agent: %s (%s)"
|
||||
msgstr "Agent : %s (%s)"
|
||||
|
||||
msgid "Timestamp: %s"
|
||||
msgstr "Horodatage : %s"
|
||||
|
||||
msgid "Description: %s"
|
||||
msgstr "Description : %s"
|
||||
|
||||
msgid "Raw Alert Data:"
|
||||
msgstr "Données brutes de l'alerte :"
|
||||
|
||||
# Cron tasks
|
||||
msgid "Synchronize Wazuh alerts"
|
||||
msgstr "Synchroniser les alertes Wazuh"
|
||||
|
||||
msgid "Cleanup old alerts"
|
||||
msgstr "Nettoyer les anciennes alertes"
|
||||
|
||||
# Rights
|
||||
msgid "SIEM Wazuh Rights"
|
||||
msgstr "Droits SIEM Wazuh"
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr "Configuration"
|
||||
|
||||
msgid "Servers"
|
||||
msgstr "Serveurs"
|
||||
|
||||
msgid "Alerts"
|
||||
msgstr "Alertes"
|
||||
|
||||
# Error messages
|
||||
msgid "Access denied"
|
||||
msgstr "Accès refusé"
|
||||
|
||||
msgid "Invalid CSRF token"
|
||||
msgstr "Token CSRF invalide"
|
||||
|
||||
msgid "Invalid server ID"
|
||||
msgstr "ID de serveur invalide"
|
||||
|
||||
msgid "Server not found"
|
||||
msgstr "Serveur non trouvé"
|
||||
|
||||
msgid "Invalid item parameters"
|
||||
msgstr "Paramètres d'élément invalides"
|
||||
|
||||
msgid "Invalid item type"
|
||||
msgstr "Type d'élément invalide"
|
||||
|
||||
msgid "Item not found"
|
||||
msgstr "Élément non trouvé"
|
||||
|
||||
msgid "Unknown action"
|
||||
msgstr "Action inconnue"
|
||||
|
||||
msgid "Request failed after maximum retries"
|
||||
msgstr "Échec de la requête après le nombre maximum de tentatives"
|
||||
|
||||
msgid "Indexer not configured"
|
||||
msgstr "Indexer non configuré"
|
||||
|
||||
msgid "Authentication failed"
|
||||
msgstr "Échec de l'authentification"
|
||||
|
||||
msgid "Status retrieved successfully"
|
||||
msgstr "Statut récupéré avec succès"
|
||||
|
||||
# Additional configuration
|
||||
msgid "Asset Mapping"
|
||||
msgstr "Mapping des Assets"
|
||||
|
||||
msgid "Notifications"
|
||||
msgstr "Notifications"
|
||||
|
||||
msgid "Debug & Logs"
|
||||
msgstr "Debug et Logs"
|
||||
|
||||
msgid "Email Notifications"
|
||||
msgstr "Notifications par Email"
|
||||
|
||||
msgid "Enable email notifications"
|
||||
msgstr "Activer les notifications par email"
|
||||
|
||||
msgid "Notification for critical alerts only"
|
||||
msgstr "Notifications pour les alertes critiques uniquement"
|
||||
|
||||
msgid "Default notification recipients"
|
||||
msgstr "Destinataires par défaut des notifications"
|
||||
|
||||
msgid "Comma-separated email addresses"
|
||||
msgstr "Adresses email séparées par des virgules"
|
||||
|
||||
msgid "Asset Detection Rules"
|
||||
msgstr "Règles de détection des assets"
|
||||
|
||||
msgid "Match computers by hostname"
|
||||
msgstr "Correspondance ordinateurs par nom d'hôte"
|
||||
|
||||
msgid "Match network equipment by hostname"
|
||||
msgstr "Correspondance équipements réseau par nom d'hôte"
|
||||
|
||||
msgid "Hostname patterns to ignore"
|
||||
msgstr "Motifs de nom d'hôte à ignorer"
|
||||
|
||||
msgid "One pattern per line. Use * as wildcard."
|
||||
msgstr "Un motif par ligne. Utilisez * comme caractère générique."
|
||||
|
||||
msgid "Test Asset Mapping"
|
||||
msgstr "Tester le mapping des assets"
|
||||
|
||||
msgid "Agent name or IP"
|
||||
msgstr "Nom d'agent ou IP"
|
||||
|
||||
msgid "Test"
|
||||
msgstr "Tester"
|
||||
|
||||
msgid "Please enter an agent name or IP address"
|
||||
msgstr "Veuillez saisir un nom d'agent ou une adresse IP"
|
||||
|
||||
msgid "Test failed"
|
||||
msgstr "Échec du test"
|
||||
|
||||
msgid "Log API requests"
|
||||
msgstr "Journaliser les requêtes API"
|
||||
|
||||
msgid "Keep logs for (days)"
|
||||
msgstr "Conserver les logs pendant (jours)"
|
||||
|
||||
msgid "Clear All Logs"
|
||||
msgstr "Effacer tous les logs"
|
||||
|
||||
msgid "Are you sure you want to clear all logs?"
|
||||
msgstr "Êtes-vous sûr de vouloir effacer tous les logs ?"
|
||||
|
||||
msgid "Logs cleared successfully"
|
||||
msgstr "Logs effacés avec succès"
|
||||
|
||||
msgid "Failed to clear logs"
|
||||
msgstr "Échec de l'effacement des logs"
|
||||
|
||||
msgid "Recent Logs"
|
||||
msgstr "Logs récents"
|
||||
|
||||
msgid "Level"
|
||||
msgstr "Niveau"
|
||||
|
||||
msgid "Message"
|
||||
msgstr "Message"
|
||||
|
||||
msgid "System"
|
||||
msgstr "Système"
|
||||
|
||||
msgid "No logs found"
|
||||
msgstr "Aucun log trouvé"
|
0
locales/it_IT.mo
Normal file
0
locales/it_IT.mo
Normal file
0
locales/it_IT.po
Normal file
0
locales/it_IT.po
Normal file
0
locales/pl_PL.mo
Normal file
0
locales/pl_PL.mo
Normal file
0
locales/pl_PL.po
Normal file
0
locales/pl_PL.po
Normal file
0
locales/pt_BR.mo
Normal file
0
locales/pt_BR.mo
Normal file
0
locales/pt_BR.po
Normal file
0
locales/pt_BR.po
Normal file
235
setup.php
Normal file
235
setup.php
Normal file
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
/*
|
||||
* Plugin SIEM-Wazuh pour GLPI
|
||||
* Description: Intégration SIEM Wazuh avec GLPI
|
||||
* Version: 1.0.0
|
||||
*/
|
||||
|
||||
define('PLUGIN_SIEM_WAZUH_VERSION', '1.0.0');
|
||||
define('PLUGIN_SIEM_WAZUH_MIN_GLPI', '10.0.0');
|
||||
define('PLUGIN_SIEM_WAZUH_MAX_GLPI', '10.0.99');
|
||||
|
||||
/**
|
||||
* Plugin init function
|
||||
*/
|
||||
function plugin_init_siem_wazuh() {
|
||||
global $PLUGIN_HOOKS, $CFG_GLPI;
|
||||
|
||||
$PLUGIN_HOOKS['csrf_compliant']['siem-wazuh'] = true;
|
||||
|
||||
// Enregistrement du plugin
|
||||
Plugin::registerClass('PluginSiemWazuhServer', [
|
||||
'linkgroup' => 'admin',
|
||||
'linktext' => __('Wazuh Servers', 'siem-wazuh'),
|
||||
'icon' => 'fas fa-shield-alt'
|
||||
]);
|
||||
|
||||
Plugin::registerClass('PluginSiemWazuhConfig', [
|
||||
'linkgroup' => 'tools',
|
||||
'linktext' => __('SIEM Wazuh Configuration', 'siem-wazuh'),
|
||||
'icon' => 'fas fa-cogs'
|
||||
]);
|
||||
|
||||
Plugin::registerClass('PluginSiemWazuhAlert');
|
||||
|
||||
// Ajout des menus
|
||||
if (Session::haveRight('plugin_siem_wazuh_server', READ)) {
|
||||
$PLUGIN_HOOKS['menu_toadd']['siem-wazuh']['admin'] = 'PluginSiemWazuhServer';
|
||||
}
|
||||
|
||||
if (Session::haveRight('plugin_siem_wazuh_config', READ)) {
|
||||
$PLUGIN_HOOKS['menu_toadd']['siem-wazuh']['tools'] = 'PluginSiemWazuhConfig';
|
||||
}
|
||||
|
||||
// Ajout des onglets sur les éléments
|
||||
$PLUGIN_HOOKS['item_add_targets']['siem-wazuh'] = [
|
||||
'Computer' => ['PluginSiemWazuhTab'],
|
||||
'NetworkEquipment' => ['PluginSiemWazuhTab'],
|
||||
'Peripheral' => ['PluginSiemWazuhTab'],
|
||||
'Phone' => ['PluginSiemWazuhTab'],
|
||||
'Printer' => ['PluginSiemWazuhTab']
|
||||
];
|
||||
|
||||
// Hook pour l'affichage des onglets
|
||||
$PLUGIN_HOOKS['display_item']['siem-wazuh'] = 'plugin_siem_wazuh_display_item';
|
||||
|
||||
// Hook pour les actions automatiques (cron)
|
||||
$PLUGIN_HOOKS['cron']['siem-wazuh'] = 1;
|
||||
|
||||
// Hook pour les droits
|
||||
$PLUGIN_HOOKS['change_profile']['siem-wazuh'] = ['PluginSiemWazuhProfile', 'changeProfile'];
|
||||
$PLUGIN_HOOKS['init_profile']['siem-wazuh'] = ['PluginSiemWazuhProfile', 'initProfile'];
|
||||
|
||||
// CSS et JS
|
||||
$PLUGIN_HOOKS['add_css']['siem-wazuh'][] = 'css/style.css';
|
||||
$PLUGIN_HOOKS['add_javascript']['siem-wazuh'][] = 'js/wazuh.js';
|
||||
|
||||
// Import/Export
|
||||
$PLUGIN_HOOKS['import_item']['siem-wazuh'] = ['Computer', 'NetworkEquipment'];
|
||||
|
||||
// Notification
|
||||
$PLUGIN_HOOKS['item_get_events']['siem-wazuh'] = [
|
||||
'PluginSiemWazuhAlert' => ['PluginSiemWazuhAlert', 'getEvents']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin version function
|
||||
*/
|
||||
function plugin_version_siem_wazuh() {
|
||||
return [
|
||||
'name' => 'SIEM - Wazuh',
|
||||
'version' => PLUGIN_SIEM_WAZUH_VERSION,
|
||||
'author' => 'SIEM-Wazuh Team',
|
||||
'license' => 'GPLv2+',
|
||||
'homepage' => 'https://github.com/siem-wazuh/glpi-plugin',
|
||||
'requirements' => [
|
||||
'glpi' => [
|
||||
'min' => PLUGIN_SIEM_WAZUH_MIN_GLPI,
|
||||
'max' => PLUGIN_SIEM_WAZUH_MAX_GLPI,
|
||||
],
|
||||
'php' => [
|
||||
'min' => '7.4',
|
||||
],
|
||||
'params' => [
|
||||
'check_prerequisites' => true,
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check plugin prerequisites
|
||||
*/
|
||||
function plugin_siem_wazuh_check_prerequisites() {
|
||||
// Vérification de la version PHP
|
||||
if (version_compare(PHP_VERSION, '7.4', '<')) {
|
||||
echo "Ce plugin nécessite PHP 7.4 ou supérieur";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Vérification de la version GLPI
|
||||
if (!method_exists('Plugin', 'checkGlpiVersion')) {
|
||||
echo "Cette version de GLPI n'est pas supportée";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Plugin::checkGlpiVersion(PLUGIN_SIEM_WAZUH_MIN_GLPI, PLUGIN_SIEM_WAZUH_MAX_GLPI)) {
|
||||
echo "Ce plugin nécessite GLPI >= " . PLUGIN_SIEM_WAZUH_MIN_GLPI . " et < " . PLUGIN_SIEM_WAZUH_MAX_GLPI;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Vérification des extensions PHP nécessaires
|
||||
$required_extensions = ['curl', 'json', 'mbstring', 'openssl'];
|
||||
foreach ($required_extensions as $ext) {
|
||||
if (!extension_loaded($ext)) {
|
||||
echo "Extension PHP manquante: $ext";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check plugin configuration
|
||||
*/
|
||||
function plugin_siem_wazuh_check_config() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin display item hook
|
||||
*/
|
||||
function plugin_siem_wazuh_display_item($item) {
|
||||
if (in_array($item->getType(), ['Computer', 'NetworkEquipment', 'Peripheral', 'Phone', 'Printer'])) {
|
||||
if (Session::haveRight('plugin_siem_wazuh_alert', READ)) {
|
||||
$tab = new PluginSiemWazuhTab();
|
||||
$tab->showForItem($item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cron description
|
||||
*/
|
||||
function plugin_siem_wazuh_cron_description($name) {
|
||||
switch ($name) {
|
||||
case 'sync_alerts':
|
||||
return __('Synchronize Wazuh alerts', 'siem-wazuh');
|
||||
case 'cleanup_old_alerts':
|
||||
return __('Cleanup old alerts', 'siem-wazuh');
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute cron task
|
||||
*/
|
||||
function plugin_siem_wazuh_cron($name) {
|
||||
global $DB;
|
||||
|
||||
switch ($name) {
|
||||
case 'sync_alerts':
|
||||
return PluginSiemWazuhAlert::cronSyncAlerts();
|
||||
|
||||
case 'cleanup_old_alerts':
|
||||
return PluginSiemWazuhAlert::cronCleanupOldAlerts();
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get additional menu entries
|
||||
*/
|
||||
function plugin_siem_wazuh_get_additional_menu_entries($forcetab = '') {
|
||||
$entries = [];
|
||||
|
||||
if (Session::haveRight('plugin_siem_wazuh_server', READ)) {
|
||||
$entries['<img src="' . Plugin::getWebDir('siem-wazuh') . '/pics/wazuh-logo.png" width="16" height="16" alt=""> ' .
|
||||
__('Wazuh Servers', 'siem-wazuh')] = '/plugins/siem-wazuh/front/wazuhserver.php';
|
||||
}
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dropdown values
|
||||
*/
|
||||
function plugin_siem_wazuh_get_dropdown_values($post, $dropdown_name = '') {
|
||||
switch ($dropdown_name) {
|
||||
case 'PluginSiemWazuhServer':
|
||||
return PluginSiemWazuhServer::getDropdownValues($post);
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get search options
|
||||
*/
|
||||
function plugin_siem_wazuh_getAddSearchOptions($itemtype) {
|
||||
$sopt = [];
|
||||
|
||||
switch ($itemtype) {
|
||||
case 'Computer':
|
||||
case 'NetworkEquipment':
|
||||
$sopt[5150]['table'] = 'glpi_plugin_siem_wazuh_alerts';
|
||||
$sopt[5150]['field'] = 'id';
|
||||
$sopt[5150]['name'] = __('Wazuh Alerts', 'siem-wazuh');
|
||||
$sopt[5150]['forcegroupby'] = true;
|
||||
$sopt[5150]['usehaving'] = true;
|
||||
$sopt[5150]['datatype'] = 'count';
|
||||
$sopt[5150]['massiveaction'] = false;
|
||||
$sopt[5150]['joinparams'] = [
|
||||
'jointype' => 'itemtype_item',
|
||||
'specific_itemtype' => $itemtype
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
return $sopt;
|
||||
}
|
148
sql/install.sql
Normal file
148
sql/install.sql
Normal file
@@ -0,0 +1,148 @@
|
||||
-- Plugin SIEM-Wazuh - Installation script
|
||||
|
||||
-- Table des serveurs Wazuh
|
||||
CREATE TABLE IF NOT EXISTS `glpi_plugin_siem_wazuh_servers` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(255) NOT NULL COMMENT 'Nom de la connexion',
|
||||
`wazuh_url` VARCHAR(255) NOT NULL COMMENT 'URL du serveur Wazuh',
|
||||
`wazuh_port` INT(11) DEFAULT 55000 COMMENT 'Port API Wazuh',
|
||||
`wazuh_login` VARCHAR(255) NOT NULL COMMENT 'Login API Wazuh',
|
||||
`wazuh_password` TEXT NOT NULL COMMENT 'Mot de passe API Wazuh (crypté)',
|
||||
`indexer_url` VARCHAR(255) DEFAULT NULL COMMENT 'URL du serveur Indexer',
|
||||
`indexer_port` INT(11) DEFAULT 9200 COMMENT 'Port API Indexer',
|
||||
`indexer_login` VARCHAR(255) DEFAULT NULL COMMENT 'Login API Indexer',
|
||||
`indexer_password` TEXT DEFAULT NULL COMMENT 'Mot de passe API Indexer (crypté)',
|
||||
`sync_interval` INT(11) DEFAULT 300 COMMENT 'Intervalle de synchronisation en secondes',
|
||||
`is_active` TINYINT(1) DEFAULT 1 COMMENT 'Serveur actif',
|
||||
`last_sync` DATETIME DEFAULT NULL COMMENT 'Dernière synchronisation',
|
||||
`ticket_type` INT(11) DEFAULT 1 COMMENT 'Type de ticket par défaut',
|
||||
`ticket_category` INT(11) DEFAULT NULL COMMENT 'Catégorie de ticket par défaut',
|
||||
`date_creation` DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
`date_mod` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `is_active` (`is_active`),
|
||||
INDEX `last_sync` (`last_sync`),
|
||||
INDEX `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Table des alertes Wazuh
|
||||
CREATE TABLE IF NOT EXISTS `glpi_plugin_siem_wazuh_alerts` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`wazuh_server_id` INT(11) NOT NULL COMMENT 'ID du serveur Wazuh',
|
||||
`alert_id` VARCHAR(255) NOT NULL COMMENT 'ID unique de l\'alerte Wazuh',
|
||||
`rule_id` INT(11) DEFAULT NULL COMMENT 'ID de la règle Wazuh',
|
||||
`rule_level` INT(11) DEFAULT NULL COMMENT 'Niveau de la règle',
|
||||
`rule_description` TEXT DEFAULT NULL COMMENT 'Description de la règle',
|
||||
`agent_id` VARCHAR(255) DEFAULT NULL COMMENT 'ID de l\'agent Wazuh',
|
||||
`agent_name` VARCHAR(255) DEFAULT NULL COMMENT 'Nom de l\'agent Wazuh',
|
||||
`agent_ip` VARCHAR(45) DEFAULT NULL COMMENT 'IP de l\'agent',
|
||||
`timestamp` DATETIME NOT NULL COMMENT 'Timestamp de l\'alerte',
|
||||
`raw_data` LONGTEXT DEFAULT NULL COMMENT 'Données brutes de l\'alerte (JSON)',
|
||||
`computer_id` INT(11) DEFAULT NULL COMMENT 'ID de l\'ordinateur GLPI associé',
|
||||
`networkequipment_id` INT(11) DEFAULT NULL COMMENT 'ID de l\'équipement réseau GLPI associé',
|
||||
`peripheral_id` INT(11) DEFAULT NULL COMMENT 'ID du périphérique GLPI associé',
|
||||
`phone_id` INT(11) DEFAULT NULL COMMENT 'ID du téléphone GLPI associé',
|
||||
`printer_id` INT(11) DEFAULT NULL COMMENT 'ID de l\'imprimante GLPI associée',
|
||||
`ticket_id` INT(11) DEFAULT NULL COMMENT 'ID du ticket GLPI créé',
|
||||
`status` ENUM('new', 'processed', 'ignored', 'ticket_created') DEFAULT 'new',
|
||||
`severity` ENUM('low', 'medium', 'high', 'critical') DEFAULT 'medium',
|
||||
`date_creation` DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
`date_mod` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `unique_alert` (`wazuh_server_id`, `alert_id`),
|
||||
INDEX `agent_name` (`agent_name`),
|
||||
INDEX `agent_ip` (`agent_ip`),
|
||||
INDEX `status` (`status`),
|
||||
INDEX `severity` (`severity`),
|
||||
INDEX `timestamp` (`timestamp`),
|
||||
INDEX `rule_level` (`rule_level`),
|
||||
INDEX `computer_id` (`computer_id`),
|
||||
INDEX `networkequipment_id` (`networkequipment_id`),
|
||||
INDEX `peripheral_id` (`peripheral_id`),
|
||||
INDEX `phone_id` (`phone_id`),
|
||||
INDEX `printer_id` (`printer_id`),
|
||||
INDEX `ticket_id` (`ticket_id`),
|
||||
FOREIGN KEY (`wazuh_server_id`) REFERENCES `glpi_plugin_siem_wazuh_servers` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Table de configuration
|
||||
CREATE TABLE IF NOT EXISTS `glpi_plugin_siem_wazuh_config` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(255) NOT NULL COMMENT 'Nom du paramètre',
|
||||
`value` TEXT DEFAULT NULL COMMENT 'Valeur du paramètre',
|
||||
`context` VARCHAR(100) DEFAULT 'global' COMMENT 'Contexte du paramètre',
|
||||
`date_creation` DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
`date_mod` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name_context` (`name`, `context`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Table des profils/droits
|
||||
CREATE TABLE IF NOT EXISTS `glpi_plugin_siem_wazuh_profiles` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`profiles_id` INT(11) NOT NULL COMMENT 'ID du profil GLPI',
|
||||
`wazuh_config` CHAR(1) DEFAULT NULL COMMENT 'Droit configuration Wazuh',
|
||||
`wazuh_server` CHAR(1) DEFAULT NULL COMMENT 'Droit serveur Wazuh',
|
||||
`wazuh_alert` CHAR(1) DEFAULT NULL COMMENT 'Droit alerte Wazuh',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `profiles_id` (`profiles_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Table de logs
|
||||
CREATE TABLE IF NOT EXISTS `glpi_plugin_siem_wazuh_logs` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`wazuh_server_id` INT(11) DEFAULT NULL COMMENT 'ID du serveur Wazuh',
|
||||
`level` ENUM('debug', 'info', 'warning', 'error', 'critical') DEFAULT 'info',
|
||||
`message` TEXT NOT NULL,
|
||||
`context` JSON DEFAULT NULL COMMENT 'Contexte supplémentaire',
|
||||
`date_creation` DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `level` (`level`),
|
||||
INDEX `wazuh_server_id` (`wazuh_server_id`),
|
||||
INDEX `date_creation` (`date_creation`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Table des règles de correspondance
|
||||
CREATE TABLE IF NOT EXISTS `glpi_plugin_siem_wazuh_rules` (
|
||||
`id` INT(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(255) NOT NULL COMMENT 'Nom de la règle',
|
||||
`wazuh_rule_id` INT(11) DEFAULT NULL COMMENT 'ID de la règle Wazuh',
|
||||
`wazuh_rule_level` INT(11) DEFAULT NULL COMMENT 'Niveau de la règle Wazuh',
|
||||
`pattern_agent_name` VARCHAR(255) DEFAULT NULL COMMENT 'Pattern nom agent',
|
||||
`pattern_agent_ip` VARCHAR(255) DEFAULT NULL COMMENT 'Pattern IP agent',
|
||||
`ticket_type` INT(11) DEFAULT 1 COMMENT 'Type de ticket à créer',
|
||||
`ticket_category` INT(11) DEFAULT NULL COMMENT 'Catégorie de ticket',
|
||||
`ticket_priority` INT(11) DEFAULT 3 COMMENT 'Priorité du ticket',
|
||||
`ticket_urgency` INT(11) DEFAULT 3 COMMENT 'Urgence du ticket',
|
||||
`ticket_impact` INT(11) DEFAULT 3 COMMENT 'Impact du ticket',
|
||||
`auto_assign` TINYINT(1) DEFAULT 0 COMMENT 'Assignment automatique',
|
||||
`assigned_to` INT(11) DEFAULT NULL COMMENT 'Utilisateur assigné',
|
||||
`assigned_group` INT(11) DEFAULT NULL COMMENT 'Groupe assigné',
|
||||
`is_active` TINYINT(1) DEFAULT 1,
|
||||
`date_creation` DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
`date_mod` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
INDEX `wazuh_rule_id` (`wazuh_rule_id`),
|
||||
INDEX `wazuh_rule_level` (`wazuh_rule_level`),
|
||||
INDEX `is_active` (`is_active`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Insertion des données par défaut
|
||||
INSERT INTO `glpi_plugin_siem_wazuh_config` (`name`, `value`, `context`) VALUES
|
||||
('auto_create_ticket', '1', 'global'),
|
||||
('default_ticket_priority', '3', 'global'),
|
||||
('default_ticket_urgency', '3', 'global'),
|
||||
('default_ticket_impact', '3', 'global'),
|
||||
('alert_retention_days', '90', 'global'),
|
||||
('sync_enabled', '1', 'global'),
|
||||
('max_alerts_per_sync', '100', 'global'),
|
||||
('notification_enabled', '1', 'global'),
|
||||
('min_rule_level', '5', 'global'),
|
||||
('debug_mode', '0', 'global');
|
||||
|
||||
-- Insertion des règles par défaut
|
||||
INSERT INTO `glpi_plugin_siem_wazuh_rules` (`name`, `wazuh_rule_level`, `ticket_type`, `ticket_priority`, `is_active`) VALUES
|
||||
('Alertes critiques (niveau 12+)', 12, 1, 5, 1),
|
||||
('Alertes élevées (niveau 10-11)', 10, 1, 4, 1),
|
||||
('Alertes moyennes (niveau 7-9)', 7, 1, 3, 1),
|
||||
('Alertes faibles (niveau 5-6)', 5, 1, 2, 0);
|
46
sql/uninstall.sql
Normal file
46
sql/uninstall.sql
Normal file
@@ -0,0 +1,46 @@
|
||||
-- Plugin SIEM-Wazuh - Uninstallation script
|
||||
|
||||
-- Désactivation des contraintes de clés étrangères temporairement
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- Suppression des tables dans l'ordre inverse des dépendances
|
||||
DROP TABLE IF EXISTS `glpi_plugin_siem_wazuh_logs`;
|
||||
DROP TABLE IF EXISTS `glpi_plugin_siem_wazuh_rules`;
|
||||
DROP TABLE IF EXISTS `glpi_plugin_siem_wazuh_alerts`;
|
||||
DROP TABLE IF EXISTS `glpi_plugin_siem_wazuh_profiles`;
|
||||
DROP TABLE IF EXISTS `glpi_plugin_siem_wazuh_config`;
|
||||
DROP TABLE IF EXISTS `glpi_plugin_siem_wazuh_servers`;
|
||||
|
||||
-- Réactivation des contraintes de clés étrangères
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
|
||||
-- Suppression des droits du plugin
|
||||
DELETE FROM `glpi_profilerights` WHERE `name` LIKE 'plugin_siem_wazuh_%';
|
||||
|
||||
-- Suppression des tâches cron
|
||||
DELETE FROM `glpi_crontasks` WHERE `itemtype` LIKE 'PluginSiemWazuh%';
|
||||
|
||||
-- Suppression des notifications
|
||||
DELETE FROM `glpi_notifications` WHERE `itemtype` LIKE 'PluginSiemWazuh%';
|
||||
DELETE FROM `glpi_notificationtemplates` WHERE `itemtype` LIKE 'PluginSiemWazuh%';
|
||||
|
||||
-- Suppression des événements de log
|
||||
DELETE FROM `glpi_events` WHERE `type` = 'siem-wazuh';
|
||||
|
||||
-- Suppression des configurations dans la table des configurations générales
|
||||
DELETE FROM `glpi_configs` WHERE `name` LIKE 'plugin_siem_wazuh_%';
|
||||
|
||||
-- Suppression des préférences utilisateur liées au plugin
|
||||
DELETE FROM `glpi_users_configs` WHERE `name` LIKE 'plugin_siem_wazuh_%';
|
||||
|
||||
-- Nettoyage des liens avec les tickets (optionnel - conserve les tickets créés)
|
||||
-- UPDATE `glpi_tickets` SET `content` = REPLACE(`content`, '[WAZUH-ALERT]', '') WHERE `content` LIKE '%[WAZUH-ALERT]%';
|
||||
|
||||
-- Nettoyage des recherches sauvegardées liées au plugin
|
||||
DELETE FROM `glpi_savedsearches` WHERE `type` LIKE 'PluginSiemWazuh%';
|
||||
|
||||
-- Suppression des affichages personnalisés
|
||||
DELETE FROM `glpi_displaypreferences` WHERE `itemtype` LIKE 'PluginSiemWazuh%';
|
||||
|
||||
-- Suppression des options de recherche personnalisées
|
||||
DELETE FROM `glpi_searchoptions` WHERE `itemtype` LIKE 'PluginSiemWazuh%';
|
0
wazuhapi.class.php
Normal file
0
wazuhapi.class.php
Normal file
Reference in New Issue
Block a user