first sync

This commit is contained in:
2025-08-27 21:17:28 +02:00
parent 0ce51dbe03
commit 247efa5d8f
35 changed files with 7725 additions and 2 deletions

706
front/wazuhalert.php Normal file
View 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
View 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 "&nbsp;<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
View 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
View 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();