add the main file structure requirements

هذا الالتزام موجود في:
2026-07-26 22:46:15 +03:00
التزام 8bde42182e
20 ملفات معدلة مع 4613 إضافات و0 حذوفات

عرض الملف

@@ -0,0 +1,343 @@
/**
* SIEM Dashboard - JavaScript
* نظام مراقبة وتحليل السجلات الأمنية
*/
// ═══════════════════════════════════════════════════════════════
// بيانات تجريبية (في الإنتاج تأتي من API)
// ═══════════════════════════════════════════════════════════════
const sampleData = {
statistics: {
total_lines: 1250,
threats_detected: 15,
sources_analyzed: 3
},
summary: {
total_alerts: 15,
critical: 5,
high: 6,
medium: 4,
suspicious_ips_count: 4
},
alerts: [
{
id: "a1b2c3d4",
timestamp: "2026-07-26T14:05:01",
threat_type: "brute_force",
description: "محاولات تسجيل دخول فاشلة متكررة",
severity: "critical",
source_ip: "192.168.1.100",
details: { path: "/api/login", attempts: 15 }
},
{
id: "e5f6g7h8",
timestamp: "2026-07-26T14:10:02",
threat_type: "sql_injection",
description: "محاولة SQL Injection",
severity: "critical",
source_ip: "10.0.0.50",
details: { path: "/api/users?id=1' OR '1'='1" }
},
{
id: "i9j0k1l2",
timestamp: "2026-07-26T14:15:01",
threat_type: "xss_attempt",
description: "محاولة XSS",
severity: "high",
source_ip: "172.16.0.25",
details: { path: "/page?q=<script>alert('xss')</script>" }
},
{
id: "m3n4o5p6",
timestamp: "2026-07-26T14:15:02",
threat_type: "path_traversal",
description: "محاولة Path Traversal",
severity: "high",
source_ip: "172.16.0.25",
details: { path: "/../../etc/passwd" }
},
{
id: "q7r8s9t0",
timestamp: "2026-07-26T14:00:05",
threat_type: "ssh_brute_force",
description: "محاولات SSH فاشلة",
severity: "critical",
source_ip: "192.168.1.200",
details: { attempts: 5, usernames: ["admin", "root", "test"] }
},
{
id: "u1v2w3x4",
timestamp: "2026-07-26T14:10:01",
threat_type: "suspicious_user_agent",
description: "User-Agent مشبوه",
severity: "medium",
source_ip: "10.0.0.50",
details: { user_agent: "sqlmap/1.5" }
}
],
suspicious_ips: [
{
ip: "192.168.1.100",
threat_count: 15,
request_count: 120,
threats: ["brute_force", "auth_failure"],
last_seen: "2026-07-26T14:05:06"
},
{
ip: "10.0.0.50",
threat_count: 8,
request_count: 45,
threats: ["sql_injection", "suspicious_user_agent"],
last_seen: "2026-07-26T14:10:02"
},
{
ip: "172.16.0.25",
threat_count: 5,
request_count: 30,
threats: ["xss_attempt", "path_traversal"],
last_seen: "2026-07-26T14:15:02"
},
{
ip: "192.168.1.200",
threat_count: 5,
request_count: 25,
threats: ["ssh_brute_force"],
last_seen: "2026-07-26T14:00:05"
}
],
threat_counts: {
"SQL Injection": 3,
"Brute Force": 5,
"XSS": 2,
"Path Traversal": 2,
"SSH Attacks": 3
},
source_lines: {
nginx: 850,
auth: 200,
app: 200
}
};
// ═══════════════════════════════════════════════════════════════
// تحديث الإحصائيات
// ═══════════════════════════════════════════════════════════════
function updateStats(data) {
document.getElementById('criticalCount').textContent = data.summary.critical;
document.getElementById('highCount').textContent = data.summary.high;
document.getElementById('mediumCount').textContent = data.summary.medium;
document.getElementById('suspiciousIPs').textContent = data.summary.suspicious_ips_count;
// تحديث عدد الأسطر لكل مصدر
document.getElementById('nginxLines').textContent = data.source_lines.nginx.toLocaleString();
document.getElementById('authLines').textContent = data.source_lines.auth.toLocaleString();
document.getElementById('appLines').textContent = data.source_lines.app.toLocaleString();
}
// ═══════════════════════════════════════════════════════════════
// عرض التنبيهات
// ═══════════════════════════════════════════════════════════════
function renderAlerts(alerts, filter = 'all') {
const container = document.getElementById('alertsList');
container.innerHTML = '';
const filteredAlerts = filter === 'all'
? alerts
: alerts.filter(a => a.severity === filter);
if (filteredAlerts.length === 0) {
container.innerHTML = '<p style="text-align: center; color: var(--text-secondary); padding: 40px;">لا توجد تنبيهات</p>';
return;
}
filteredAlerts.forEach(alert => {
const icon = getAlertIcon(alert.severity);
const time = formatTime(alert.timestamp);
const alertHtml = `
<div class="alert-item ${alert.severity}">
<div class="alert-icon">${icon}</div>
<div class="alert-content">
<div class="alert-title">${alert.description}</div>
<div class="alert-details">
<strong>IP:</strong> ${alert.source_ip} |
<strong>النوع:</strong> ${alert.threat_type}
</div>
<div class="alert-time">${time}</div>
</div>
<span class="severity-badge ${alert.severity}">${getSeverityLabel(alert.severity)}</span>
</div>
`;
container.innerHTML += alertHtml;
});
}
function getAlertIcon(severity) {
const icons = {
critical: '🚨',
high: '⚠️',
medium: '📊',
low: ''
};
return icons[severity] || '📋';
}
function getSeverityLabel(severity) {
const labels = {
critical: 'حرج',
high: 'عالي',
medium: 'متوسط',
low: 'منخفض'
};
return labels[severity] || severity;
}
function formatTime(timestamp) {
const date = new Date(timestamp);
return date.toLocaleString('ar-SA', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
}
// ═══════════════════════════════════════════════════════════════
// عرض عناوين IP المشبوهة
// ═══════════════════════════════════════════════════════════════
function renderSuspiciousIPs(ips) {
const tbody = document.getElementById('ipsTableBody');
tbody.innerHTML = '';
ips.forEach(ip => {
const row = `
<tr>
<td><span class="ip-address">${ip.ip}</span></td>
<td><span class="threat-count">${ip.threat_count}</span></td>
<td>${ip.request_count}</td>
<td>${formatTime(ip.last_seen)}</td>
<td><button class="btn-block" onclick="blockIP('${ip.ip}')">حظر</button></td>
</tr>
`;
tbody.innerHTML += row;
});
}
function blockIP(ip) {
if (confirm(`هل تريد حظر عنوان IP: ${ip}؟`)) {
alert(`تم إرسال طلب حظر ${ip} إلى الخادم`);
// في الإنتاج: إرسال طلب API لحظر IP
}
}
// ═══════════════════════════════════════════════════════════════
// عرض الرسم البياني
// ═══════════════════════════════════════════════════════════════
function renderChart(threatCounts) {
const container = document.getElementById('threatChart');
container.innerHTML = '';
const maxCount = Math.max(...Object.values(threatCounts));
const colors = ['sql', 'brute', 'xss', 'traversal', 'sql'];
let colorIndex = 0;
for (const [threat, count] of Object.entries(threatCounts)) {
const percentage = (count / maxCount) * 100;
const color = colors[colorIndex % colors.length];
const barHtml = `
<div class="chart-bar">
<span class="chart-label">${threat}</span>
<div class="chart-bar-container">
<div class="chart-bar-fill ${color}" style="width: ${percentage}%">
${count}
</div>
</div>
</div>
`;
container.innerHTML += barHtml;
colorIndex++;
}
}
// ═══════════════════════════════════════════════════════════════
// تصفية التنبيهات
// ═══════════════════════════════════════════════════════════════
function setupFilters() {
const filterButtons = document.querySelectorAll('.filter-btn');
filterButtons.forEach(btn => {
btn.addEventListener('click', () => {
// إزالة active من جميع الأزرار
filterButtons.forEach(b => b.classList.remove('active'));
// إضافة active للزر المضغوط
btn.classList.add('active');
// تصفية التنبيهات
const filter = btn.dataset.filter;
renderAlerts(sampleData.alerts, filter);
});
});
}
// ═══════════════════════════════════════════════════════════════
// تحديث الوقت
// ═══════════════════════════════════════════════════════════════
function updateTime() {
const now = new Date();
const timeStr = now.toLocaleTimeString('ar-SA');
document.getElementById('lastUpdate').textContent = timeStr;
}
// ═══════════════════════════════════════════════════════════════
// جلب البيانات من API (للإنتاج)
// ═══════════════════════════════════════════════════════════════
async function fetchData() {
try {
// في الإنتاج: استبدل بـ API حقيقي
// const response = await fetch('/api/siem/report');
// const data = await response.json();
// استخدام البيانات التجريبية
return sampleData;
} catch (error) {
console.error('خطأ في جلب البيانات:', error);
return sampleData;
}
}
// ═══════════════════════════════════════════════════════════════
// التهيئة
// ═══════════════════════════════════════════════════════════════
async function init() {
const data = await fetchData();
updateStats(data);
renderAlerts(data.alerts);
renderSuspiciousIPs(data.suspicious_ips);
renderChart(data.threat_counts);
setupFilters();
updateTime();
// تحديث الوقت كل ثانية
setInterval(updateTime, 1000);
// تحديث البيانات كل 30 ثانية
setInterval(async () => {
const newData = await fetchData();
updateStats(newData);
// يمكن إضافة تحديث للتنبيهات أيضاً
}, 30000);
}
// بدء التطبيق
document.addEventListener('DOMContentLoaded', init);

عرض الملف

@@ -0,0 +1,145 @@
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SIEM Dashboard - لوحة تحكم الأمان</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<!-- Header -->
<header class="header">
<div class="header-content">
<h1>🛡️ SIEM Dashboard</h1>
<p>نظام مراقبة وتحليل السجلات الأمنية</p>
</div>
<div class="header-actions">
<span class="status-badge online">● متصل</span>
<span class="last-update">آخر تحديث: <span id="lastUpdate">--:--:--</span></span>
</div>
</header>
<!-- Stats Cards -->
<section class="stats-grid">
<div class="stat-card critical">
<div class="stat-icon">🚨</div>
<div class="stat-content">
<h3>تنبيهات حرجة</h3>
<p class="stat-number" id="criticalCount">0</p>
</div>
</div>
<div class="stat-card high">
<div class="stat-icon">⚠️</div>
<div class="stat-content">
<h3>تنبيهات عالية</h3>
<p class="stat-number" id="highCount">0</p>
</div>
</div>
<div class="stat-card medium">
<div class="stat-icon">📊</div>
<div class="stat-content">
<h3>تنبيهات متوسطة</h3>
<p class="stat-number" id="mediumCount">0</p>
</div>
</div>
<div class="stat-card info">
<div class="stat-icon">🔍</div>
<div class="stat-content">
<h3>IPs مشبوهة</h3>
<p class="stat-number" id="suspiciousIPs">0</p>
</div>
</div>
</section>
<!-- Main Content -->
<div class="main-content">
<!-- Alerts Section -->
<section class="alerts-section">
<div class="section-header">
<h2>📋 التنبيهات الأخيرة</h2>
<div class="filter-buttons">
<button class="filter-btn active" data-filter="all">الكل</button>
<button class="filter-btn" data-filter="critical">حرجة</button>
<button class="filter-btn" data-filter="high">عالية</button>
<button class="filter-btn" data-filter="medium">متوسطة</button>
</div>
</div>
<div class="alerts-list" id="alertsList">
<!-- Alerts will be inserted here -->
</div>
</section>
<!-- Suspicious IPs Section -->
<section class="ips-section">
<div class="section-header">
<h2>🔴 عناوين IP المشبوهة</h2>
</div>
<div class="ips-table-container">
<table class="ips-table">
<thead>
<tr>
<th>عنوان IP</th>
<th>عدد التهديدات</th>
<th>عدد الطلبات</th>
<th>آخر نشاط</th>
<th>الإجراء</th>
</tr>
</thead>
<tbody id="ipsTableBody">
<!-- IPs will be inserted here -->
</tbody>
</table>
</div>
</section>
</div>
<!-- Threat Types Chart -->
<section class="chart-section">
<div class="section-header">
<h2>📈 أنواع التهديدات</h2>
</div>
<div class="chart-container">
<div class="bar-chart" id="threatChart">
<!-- Chart bars will be inserted here -->
</div>
</div>
</section>
<!-- Log Sources Status -->
<section class="sources-section">
<div class="section-header">
<h2>📂 حالة مصادر السجلات</h2>
</div>
<div class="sources-grid">
<div class="source-card active">
<div class="source-icon">🌐</div>
<h4>Nginx Logs</h4>
<p class="source-status">● نشط</p>
<p class="source-lines"><span id="nginxLines">0</span> سطر</p>
</div>
<div class="source-card active">
<div class="source-icon">🔐</div>
<h4>Auth Logs</h4>
<p class="source-status">● نشط</p>
<p class="source-lines"><span id="authLines">0</span> سطر</p>
</div>
<div class="source-card active">
<div class="source-icon">📱</div>
<h4>App Logs</h4>
<p class="source-status">● نشط</p>
<p class="source-lines"><span id="appLines">0</span> سطر</p>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<p>SIEM Dashboard - نظام مراقبة أمني لمنصة غيمة</p>
<p>© 2026 - جميع الحقوق محفوظة</p>
</footer>
</div>
<script src="app.js"></script>
</body>
</html>

عرض الملف

@@ -0,0 +1,498 @@
/* ═══════════════════════════════════════════════════════════════
SIEM Dashboard - Styles
نظام مراقبة وتحليل السجلات الأمنية
═══════════════════════════════════════════════════════════════ */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-primary: #0f172a;
--bg-secondary: #1e293b;
--bg-card: #334155;
--text-primary: #f8fafc;
--text-secondary: #94a3b8;
--accent-blue: #3b82f6;
--accent-green: #22c55e;
--accent-yellow: #eab308;
--accent-red: #ef4444;
--accent-orange: #f97316;
--border-color: #475569;
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3);
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
min-height: 100vh;
line-height: 1.6;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
}
/* ═══════════════════════════════════════════════════════════════
Header
═══════════════════════════════════════════════════════════════ */
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 30px;
background: var(--bg-secondary);
border-radius: 12px;
margin-bottom: 24px;
box-shadow: var(--shadow);
}
.header h1 {
font-size: 1.8rem;
margin-bottom: 4px;
}
.header p {
color: var(--text-secondary);
font-size: 0.9rem;
}
.header-actions {
display: flex;
align-items: center;
gap: 20px;
}
.status-badge {
padding: 6px 12px;
border-radius: 20px;
font-size: 0.85rem;
font-weight: 500;
}
.status-badge.online {
background: rgba(34, 197, 94, 0.2);
color: var(--accent-green);
}
.last-update {
color: var(--text-secondary);
font-size: 0.85rem;
}
/* ═══════════════════════════════════════════════════════════════
Stats Cards
═══════════════════════════════════════════════════════════════ */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 20px;
margin-bottom: 24px;
}
.stat-card {
display: flex;
align-items: center;
gap: 16px;
padding: 24px;
background: var(--bg-secondary);
border-radius: 12px;
border-right: 4px solid;
box-shadow: var(--shadow);
transition: transform 0.2s;
}
.stat-card:hover {
transform: translateY(-2px);
}
.stat-card.critical {
border-color: var(--accent-red);
}
.stat-card.high {
border-color: var(--accent-orange);
}
.stat-card.medium {
border-color: var(--accent-yellow);
}
.stat-card.info {
border-color: var(--accent-blue);
}
.stat-icon {
font-size: 2.5rem;
}
.stat-content h3 {
font-size: 0.9rem;
color: var(--text-secondary);
margin-bottom: 4px;
}
.stat-number {
font-size: 2rem;
font-weight: 700;
}
.stat-card.critical .stat-number { color: var(--accent-red); }
.stat-card.high .stat-number { color: var(--accent-orange); }
.stat-card.medium .stat-number { color: var(--accent-yellow); }
.stat-card.info .stat-number { color: var(--accent-blue); }
/* ═══════════════════════════════════════════════════════════════
Main Content
═══════════════════════════════════════════════════════════════ */
.main-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
margin-bottom: 24px;
}
@media (max-width: 1024px) {
.main-content {
grid-template-columns: 1fr;
}
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.section-header h2 {
font-size: 1.2rem;
}
/* ═══════════════════════════════════════════════════════════════
Alerts Section
═══════════════════════════════════════════════════════════════ */
.alerts-section {
background: var(--bg-secondary);
border-radius: 12px;
padding: 24px;
box-shadow: var(--shadow);
}
.filter-buttons {
display: flex;
gap: 8px;
}
.filter-btn {
padding: 6px 14px;
border: 1px solid var(--border-color);
background: transparent;
color: var(--text-secondary);
border-radius: 6px;
cursor: pointer;
font-size: 0.85rem;
transition: all 0.2s;
}
.filter-btn:hover,
.filter-btn.active {
background: var(--accent-blue);
border-color: var(--accent-blue);
color: white;
}
.alerts-list {
max-height: 400px;
overflow-y: auto;
}
.alert-item {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 16px;
background: var(--bg-card);
border-radius: 8px;
margin-bottom: 12px;
border-right: 3px solid;
}
.alert-item.critical { border-color: var(--accent-red); }
.alert-item.high { border-color: var(--accent-orange); }
.alert-item.medium { border-color: var(--accent-yellow); }
.alert-icon {
font-size: 1.5rem;
}
.alert-content {
flex: 1;
}
.alert-title {
font-weight: 600;
margin-bottom: 4px;
}
.alert-details {
font-size: 0.85rem;
color: var(--text-secondary);
}
.alert-time {
font-size: 0.75rem;
color: var(--text-secondary);
}
.severity-badge {
padding: 4px 10px;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
}
.severity-badge.critical {
background: rgba(239, 68, 68, 0.2);
color: var(--accent-red);
}
.severity-badge.high {
background: rgba(249, 115, 22, 0.2);
color: var(--accent-orange);
}
.severity-badge.medium {
background: rgba(234, 179, 8, 0.2);
color: var(--accent-yellow);
}
/* ═══════════════════════════════════════════════════════════════
IPs Section
═══════════════════════════════════════════════════════════════ */
.ips-section {
background: var(--bg-secondary);
border-radius: 12px;
padding: 24px;
box-shadow: var(--shadow);
}
.ips-table-container {
overflow-x: auto;
}
.ips-table {
width: 100%;
border-collapse: collapse;
}
.ips-table th,
.ips-table td {
padding: 12px;
text-align: right;
border-bottom: 1px solid var(--border-color);
}
.ips-table th {
color: var(--text-secondary);
font-weight: 500;
font-size: 0.85rem;
}
.ips-table td {
font-size: 0.9rem;
}
.ips-table tr:hover {
background: var(--bg-card);
}
.ip-address {
font-family: monospace;
color: var(--accent-red);
}
.threat-count {
display: inline-block;
padding: 4px 10px;
background: rgba(239, 68, 68, 0.2);
color: var(--accent-red);
border-radius: 4px;
font-weight: 600;
}
.btn-block {
padding: 6px 12px;
background: var(--accent-red);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 0.8rem;
transition: opacity 0.2s;
}
.btn-block:hover {
opacity: 0.8;
}
/* ═══════════════════════════════════════════════════════════════
Chart Section
═══════════════════════════════════════════════════════════════ */
.chart-section {
background: var(--bg-secondary);
border-radius: 12px;
padding: 24px;
margin-bottom: 24px;
box-shadow: var(--shadow);
}
.chart-container {
padding: 20px 0;
}
.bar-chart {
display: flex;
flex-direction: column;
gap: 16px;
}
.chart-bar {
display: flex;
align-items: center;
gap: 12px;
}
.chart-label {
width: 150px;
font-size: 0.9rem;
text-align: left;
}
.chart-bar-container {
flex: 1;
height: 30px;
background: var(--bg-card);
border-radius: 4px;
overflow: hidden;
}
.chart-bar-fill {
height: 100%;
border-radius: 4px;
transition: width 0.5s ease;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 10px;
font-size: 0.85rem;
font-weight: 600;
}
.chart-bar-fill.sql { background: var(--accent-red); }
.chart-bar-fill.xss { background: var(--accent-orange); }
.chart-bar-fill.brute { background: var(--accent-yellow); }
.chart-bar-fill.traversal { background: var(--accent-blue); }
/* ═══════════════════════════════════════════════════════════════
Sources Section
═══════════════════════════════════════════════════════════════ */
.sources-section {
background: var(--bg-secondary);
border-radius: 12px;
padding: 24px;
margin-bottom: 24px;
box-shadow: var(--shadow);
}
.sources-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
}
.source-card {
background: var(--bg-card);
border-radius: 8px;
padding: 20px;
text-align: center;
}
.source-icon {
font-size: 2rem;
margin-bottom: 8px;
}
.source-card h4 {
margin-bottom: 8px;
}
.source-status {
font-size: 0.85rem;
color: var(--accent-green);
margin-bottom: 4px;
}
.source-lines {
font-size: 0.9rem;
color: var(--text-secondary);
}
/* ═══════════════════════════════════════════════════════════════
Footer
═══════════════════════════════════════════════════════════════ */
.footer {
text-align: center;
padding: 24px;
color: var(--text-secondary);
font-size: 0.85rem;
}
/* ═══════════════════════════════════════════════════════════════
Scrollbar
═══════════════════════════════════════════════════════════════ */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-card);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: var(--border-color);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-secondary);
}
/* ═══════════════════════════════════════════════════════════════
Animations
═══════════════════════════════════════════════════════════════ */
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.alert-item.new {
animation: pulse 1s ease-in-out 3;
}

عرض الملف

@@ -0,0 +1,386 @@
# دليل نشر نظام SIEM على غيمة
## استخدام Block Storage للسجلات
---
## نظرة عامة على البنية
```
┌─────────────────────────────────────────────────────────────┐
│ غيمة Cloud │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Nginx │ │ App │ │ Auth │ │
│ │ Logs │ │ Logs │ │ Logs │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └────────────────┼────────────────┘ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ Block Storage │ │
│ │ (السجلات المركزية) │ │
│ └───────────┬───────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ SIEM Container │ │
│ │ - log_analyzer.py │ │
│ │ - Dashboard │ │
│ └───────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
---
## الخطوة 1: إنشاء Block Storage على غيمة
### من لوحة تحكم غيمة:
1. اذهب إلى **التخزين****Block Storage**
2. اضغط **إنشاء Volume جديد**
3. الإعدادات:
- **الاسم**: `siem-logs`
- **الحجم**: 50GB (أو حسب الحاجة)
- **المنطقة**: نفس منطقة التطبيق
- **النوع**: SSD (للأداء العالي)
### أو باستخدام CLI:
```bash
# تثبيت Ghaymah CLI
npm install -g ghaymah-cli
# تسجيل الدخول
ghaymah login
# إنشاء Block Storage
ghaymah storage create \
--name siem-logs \
--size 50 \
--type ssd \
--region me-riyadh-1
```
---
## الخطوة 2: ربط Block Storage بالتطبيق
### في ملف docker-compose.yml:
```yaml
version: '3.8'
services:
siem:
build: .
container_name: siem-analyzer
volumes:
# ربط Block Storage
- /mnt/siem-logs:/var/log/siem
# ربط سجلات التطبيقات
- nginx-logs:/var/log/nginx:ro
- app-logs:/var/log/app:ro
- auth-logs:/var/log/auth:ro
ports:
- "8080:8080"
environment:
- LOG_PATH=/var/log/siem
- RETENTION_DAYS=90
restart: always
dashboard:
build: ./dashboard
container_name: siem-dashboard
ports:
- "3000:80"
depends_on:
- siem
restart: always
volumes:
nginx-logs:
external: true
app-logs:
external: true
auth-logs:
external: true
```
### في Kubernetes (إذا كنت تستخدمه):
```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: siem-logs-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
storageClassName: ghaymah-block-storage
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: siem-analyzer
spec:
replicas: 1
selector:
matchLabels:
app: siem
template:
metadata:
labels:
app: siem
spec:
containers:
- name: siem
image: siem-analyzer:latest
volumeMounts:
- name: logs-storage
mountPath: /var/log/siem
- name: nginx-logs
mountPath: /var/log/nginx
readOnly: true
volumes:
- name: logs-storage
persistentVolumeClaim:
claimName: siem-logs-pvc
- name: nginx-logs
hostPath:
path: /var/log/nginx
```
---
## الخطوة 3: إعداد جمع السجلات
### تكوين Nginx لإرسال السجلات:
```nginx
# /etc/nginx/nginx.conf
http {
# تنسيق السجلات للـ SIEM
log_format siem_format '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time';
access_log /var/log/nginx/access.log siem_format;
error_log /var/log/nginx/error.log warn;
}
```
### تكوين التطبيق لإرسال السجلات:
```javascript
// Node.js - Winston Logger
const winston = require('winston');
const logger = winston.createLogger({
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({
filename: '/var/log/app/application.log',
maxsize: 100 * 1024 * 1024, // 100MB
maxFiles: 10
})
]
});
// تسجيل مع IP
app.use((req, res, next) => {
logger.info({
ip: req.ip,
method: req.method,
path: req.path,
userAgent: req.get('User-Agent')
});
next();
});
```
---
## الخطوة 4: Dockerfile للنظام
```dockerfile
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
# تثبيت التبعيات
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# نسخ الكود
COPY log_analyzer.py .
COPY dashboard/ ./dashboard/
# إنشاء مجلدات السجلات
RUN mkdir -p /var/log/siem /var/log/nginx /var/log/app /var/log/auth
# تشغيل المحلل
CMD ["python", "log_analyzer.py", "--daemon"]
```
```txt
# requirements.txt
schedule==1.2.0
flask==3.0.0
redis==5.0.0
```
---
## الخطوة 5: النشر على غيمة
### باستخدام Git (النشر التلقائي):
```bash
# 1. إنشاء مشروع على غيمة
ghaymah project create siem-system
# 2. ربط Git
git remote add ghaymah https://git.ghaymah.systems/username/siem-system.git
# 3. النشر
git push ghaymah main
```
### أو باستخدام Docker:
```bash
# 1. بناء الصورة
docker build -t siem-analyzer:latest .
# 2. رفع الصورة لـ Ghaymah Registry
docker tag siem-analyzer:latest registry.ghaymah.systems/username/siem-analyzer:latest
docker push registry.ghaymah.systems/username/siem-analyzer:latest
# 3. النشر
ghaymah deploy --image registry.ghaymah.systems/username/siem-analyzer:latest
```
---
## الخطوة 6: إعداد التنبيهات
### تكوين Webhook للتنبيهات:
```python
# في log_analyzer.py - إضافة إرسال التنبيهات
import requests
def send_alert(alert):
"""إرسال تنبيه عبر Webhook"""
webhook_url = os.environ.get('ALERT_WEBHOOK_URL')
if not webhook_url:
return
payload = {
"text": f"🚨 تنبيه أمني: {alert['description']}",
"severity": alert['severity'],
"ip": alert['source_ip'],
"timestamp": alert['timestamp']
}
try:
requests.post(webhook_url, json=payload, timeout=5)
except Exception as e:
print(f"فشل إرسال التنبيه: {e}")
```
### متغيرات البيئة المطلوبة:
```bash
# في لوحة تحكم غيمة → إعدادات التطبيق → متغيرات البيئة
ALERT_WEBHOOK_URL=https://hooks.slack.com/services/xxx/yyy/zzz
LOG_RETENTION_DAYS=90
ANALYSIS_INTERVAL=300 # كل 5 دقائق
REDIS_URL=redis://redis:6379
```
---
## الخطوة 7: إعداد النسخ الاحتياطي
### تفعيل النسخ الاحتياطي التلقائي:
```bash
# من CLI
ghaymah storage backup enable \
--volume siem-logs \
--schedule daily \
--retention 30
```
### أو من لوحة التحكم:
1. اذهب إلى **التخزين****siem-logs**
2. اضغط **النسخ الاحتياطي**
3. فعّل **النسخ التلقائي**
4. اختر **يومي** مع الاحتفاظ لـ 30 يوم
---
## الخطوة 8: المراقبة والصيانة
### مراقبة استخدام التخزين:
```bash
# فحص استخدام Block Storage
ghaymah storage stats siem-logs
# تنظيف السجلات القديمة (أكثر من 90 يوم)
find /var/log/siem -type f -mtime +90 -delete
```
### Cron Job للتنظيف التلقائي:
```bash
# إضافة في crontab
0 2 * * * find /var/log/siem -type f -mtime +90 -delete
0 3 * * * python /app/log_analyzer.py --analyze --export
```
---
## ملخص الأوامر
```bash
# إنشاء المشروع
ghaymah project create siem-system
# إنشاء Block Storage
ghaymah storage create --name siem-logs --size 50
# ربط التخزين
ghaymah storage attach siem-logs --to siem-system
# النشر
git push ghaymah main
# مراقبة السجلات
ghaymah logs siem-system --follow
# فحص الحالة
ghaymah status siem-system
```
---
## روابط مفيدة
- [وثائق غيمة - Block Storage](https://docs.ghaymah.systems/storage)
- [وثائق غيمة - النشر](https://docs.ghaymah.systems/deploy)
- [أمثلة على GitHub](https://github.com/ghaymah/examples)

عرض الملف

@@ -0,0 +1,440 @@
#!/usr/bin/env python3
"""
SIEM مبسط - نظام جمع وتحليل السجلات
يجمع ويحلل logs من 3 مصادر ويكتشف الأنماط المشبوهة
المصادر:
1. Nginx Access Logs
2. Auth/SSH Logs
3. Application Logs
"""
import re
import json
import os
from datetime import datetime, timedelta
from collections import defaultdict
from typing import Dict, List, Tuple
import hashlib
# ═══════════════════════════════════════════════════════════════
# إعدادات المصادر
# ═══════════════════════════════════════════════════════════════
LOG_SOURCES = {
'nginx': '/var/log/nginx/access.log',
'auth': '/var/log/auth.log',
'app': '/var/log/app/application.log'
}
# للتجربة - ملفات محلية
SAMPLE_LOG_SOURCES = {
'nginx': 'sample_logs/nginx_access.log',
'auth': 'sample_logs/auth.log',
'app': 'sample_logs/app.log'
}
# ═══════════════════════════════════════════════════════════════
# أنماط الكشف عن التهديدات
# ═══════════════════════════════════════════════════════════════
THREAT_PATTERNS = {
'brute_force': {
'description': 'محاولات تسجيل دخول فاشلة متكررة',
'threshold': 5,
'window_minutes': 5,
'severity': 'critical'
},
'sql_injection': {
'description': 'محاولة SQL Injection',
'patterns': [
r"(\%27)|(\')|(\-\-)|(\%23)|(#)",
r"((\%3D)|(=))[^\n]*((\%27)|(\')|(\-\-)|(\%3B)|(;))",
r"\w*((\%27)|(\'))((\%6F)|o|(\%4F))((\%72)|r|(\%52))",
r"union.*select",
r"select.*from",
r"insert.*into",
r"drop.*table"
],
'severity': 'critical'
},
'xss_attempt': {
'description': 'محاولة XSS',
'patterns': [
r"<script[^>]*>",
r"javascript:",
r"on\w+\s*=",
r"<iframe",
r"<object"
],
'severity': 'high'
},
'path_traversal': {
'description': 'محاولة Path Traversal',
'patterns': [
r"\.\./",
r"\.\.\\",
r"%2e%2e%2f",
r"%2e%2e/"
],
'severity': 'high'
},
'suspicious_user_agent': {
'description': 'User-Agent مشبوه',
'patterns': [
r"nikto",
r"sqlmap",
r"nmap",
r"masscan",
r"dirbuster",
r"gobuster",
r"wfuzz"
],
'severity': 'medium'
},
'ssh_brute_force': {
'description': 'محاولات SSH فاشلة',
'threshold': 3,
'window_minutes': 5,
'severity': 'critical'
}
}
# ═══════════════════════════════════════════════════════════════
# فئة تحليل السجلات
# ═══════════════════════════════════════════════════════════════
class LogAnalyzer:
def __init__(self, use_sample=True):
self.sources = SAMPLE_LOG_SOURCES if use_sample else LOG_SOURCES
self.alerts = []
self.suspicious_ips = defaultdict(lambda: {
'count': 0,
'first_seen': None,
'last_seen': None,
'threats': []
})
self.stats = {
'total_lines': 0,
'threats_detected': 0,
'sources_analyzed': 0
}
def parse_nginx_log(self, line: str) -> Dict:
"""تحليل سطر من Nginx access log"""
pattern = r'(\d+\.\d+\.\d+\.\d+) - - \[([^\]]+)\] "(\w+) ([^"]+)" (\d+) (\d+) "([^"]*)" "([^"]*)"'
match = re.match(pattern, line)
if match:
return {
'ip': match.group(1),
'timestamp': match.group(2),
'method': match.group(3),
'path': match.group(4),
'status': int(match.group(5)),
'size': int(match.group(6)),
'referer': match.group(7),
'user_agent': match.group(8),
'source': 'nginx'
}
return None
def parse_auth_log(self, line: str) -> Dict:
"""تحليل سطر من Auth log"""
# Failed password
failed_pattern = r'(\w+\s+\d+\s+[\d:]+).*Failed password for (?:invalid user )?(\w+) from (\d+\.\d+\.\d+\.\d+)'
match = re.search(failed_pattern, line)
if match:
return {
'timestamp': match.group(1),
'username': match.group(2),
'ip': match.group(3),
'event': 'failed_login',
'source': 'auth'
}
# Accepted password
success_pattern = r'(\w+\s+\d+\s+[\d:]+).*Accepted password for (\w+) from (\d+\.\d+\.\d+\.\d+)'
match = re.search(success_pattern, line)
if match:
return {
'timestamp': match.group(1),
'username': match.group(2),
'ip': match.group(3),
'event': 'successful_login',
'source': 'auth'
}
return None
def parse_app_log(self, line: str) -> Dict:
"""تحليل سطر من Application log"""
pattern = r'\[(\d{4}-\d{2}-\d{2} [\d:]+)\] \[(\w+)\] \[(\d+\.\d+\.\d+\.\d+)\] (.+)'
match = re.match(pattern, line)
if match:
return {
'timestamp': match.group(1),
'level': match.group(2),
'ip': match.group(3),
'message': match.group(4),
'source': 'app'
}
return None
def detect_pattern_threat(self, log_entry: Dict, threat_type: str) -> bool:
"""كشف التهديدات بناءً على الأنماط"""
threat = THREAT_PATTERNS.get(threat_type)
if not threat or 'patterns' not in threat:
return False
# البحث في الحقول المناسبة
search_fields = ['path', 'message', 'user_agent', 'referer']
for field in search_fields:
if field in log_entry:
for pattern in threat['patterns']:
if re.search(pattern, str(log_entry[field]), re.IGNORECASE):
return True
return False
def analyze_log_entry(self, entry: Dict):
"""تحليل سجل واحد للكشف عن التهديدات"""
if not entry:
return
ip = entry.get('ip', 'unknown')
# تحديث معلومات IP
self.suspicious_ips[ip]['count'] += 1
if not self.suspicious_ips[ip]['first_seen']:
self.suspicious_ips[ip]['first_seen'] = entry.get('timestamp')
self.suspicious_ips[ip]['last_seen'] = entry.get('timestamp')
# كشف SQL Injection
if self.detect_pattern_threat(entry, 'sql_injection'):
self.add_alert('sql_injection', entry)
# كشف XSS
if self.detect_pattern_threat(entry, 'xss_attempt'):
self.add_alert('xss_attempt', entry)
# كشف Path Traversal
if self.detect_pattern_threat(entry, 'path_traversal'):
self.add_alert('path_traversal', entry)
# كشف User-Agent مشبوه
if self.detect_pattern_threat(entry, 'suspicious_user_agent'):
self.add_alert('suspicious_user_agent', entry)
# كشف محاولات تسجيل دخول فاشلة
if entry.get('event') == 'failed_login':
self.suspicious_ips[ip]['threats'].append('failed_login')
if len([t for t in self.suspicious_ips[ip]['threats'] if t == 'failed_login']) >= 5:
self.add_alert('brute_force', entry)
# كشف أكواد HTTP مشبوهة
status = entry.get('status')
if status:
if status == 401 or status == 403:
self.suspicious_ips[ip]['threats'].append('auth_failure')
elif status >= 500:
self.suspicious_ips[ip]['threats'].append('server_error')
def add_alert(self, threat_type: str, entry: Dict):
"""إضافة تنبيه جديد"""
threat_info = THREAT_PATTERNS.get(threat_type, {})
alert = {
'id': hashlib.md5(f"{threat_type}{entry.get('ip')}{datetime.now()}".encode()).hexdigest()[:8],
'timestamp': datetime.now().isoformat(),
'threat_type': threat_type,
'description': threat_info.get('description', threat_type),
'severity': threat_info.get('severity', 'medium'),
'source_ip': entry.get('ip', 'unknown'),
'details': entry,
'status': 'new'
}
self.alerts.append(alert)
self.stats['threats_detected'] += 1
def analyze_file(self, source_name: str, filepath: str):
"""تحليل ملف سجلات"""
if not os.path.exists(filepath):
print(f"⚠️ الملف غير موجود: {filepath}")
return
parser = {
'nginx': self.parse_nginx_log,
'auth': self.parse_auth_log,
'app': self.parse_app_log
}.get(source_name)
if not parser:
print(f"⚠️ لا يوجد محلل لـ: {source_name}")
return
print(f"📂 تحليل {source_name}: {filepath}")
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
for line in f:
self.stats['total_lines'] += 1
entry = parser(line.strip())
if entry:
self.analyze_log_entry(entry)
self.stats['sources_analyzed'] += 1
def analyze_all(self):
"""تحليل جميع المصادر"""
print("=" * 60)
print("🔍 بدء تحليل السجلات - SIEM مبسط")
print("=" * 60)
for source_name, filepath in self.sources.items():
self.analyze_file(source_name, filepath)
print("\n" + "=" * 60)
print("📊 نتائج التحليل")
print("=" * 60)
def get_suspicious_ips(self, min_threats: int = 3) -> List[Dict]:
"""الحصول على قائمة IPs المشبوهة"""
suspicious = []
for ip, data in self.suspicious_ips.items():
threat_count = len(data['threats'])
if threat_count >= min_threats:
suspicious.append({
'ip': ip,
'request_count': data['count'],
'threat_count': threat_count,
'threats': list(set(data['threats'])),
'first_seen': data['first_seen'],
'last_seen': data['last_seen']
})
return sorted(suspicious, key=lambda x: x['threat_count'], reverse=True)
def get_alerts_by_severity(self) -> Dict[str, List]:
"""تجميع التنبيهات حسب الخطورة"""
by_severity = defaultdict(list)
for alert in self.alerts:
by_severity[alert['severity']].append(alert)
return dict(by_severity)
def generate_report(self) -> Dict:
"""إنشاء تقرير شامل"""
return {
'generated_at': datetime.now().isoformat(),
'statistics': self.stats,
'alerts': self.alerts,
'alerts_by_severity': self.get_alerts_by_severity(),
'suspicious_ips': self.get_suspicious_ips(),
'summary': {
'total_alerts': len(self.alerts),
'critical': len([a for a in self.alerts if a['severity'] == 'critical']),
'high': len([a for a in self.alerts if a['severity'] == 'high']),
'medium': len([a for a in self.alerts if a['severity'] == 'medium']),
'suspicious_ips_count': len(self.get_suspicious_ips())
}
}
def print_summary(self):
"""طباعة ملخص النتائج"""
report = self.generate_report()
print(f"\n📈 الإحصائيات:")
print(f" - إجمالي الأسطر المحللة: {report['statistics']['total_lines']}")
print(f" - المصادر المحللة: {report['statistics']['sources_analyzed']}")
print(f" - التهديدات المكتشفة: {report['statistics']['threats_detected']}")
print(f"\n🚨 التنبيهات حسب الخطورة:")
print(f" - حرجة (Critical): {report['summary']['critical']}")
print(f" - عالية (High): {report['summary']['high']}")
print(f" - متوسطة (Medium): {report['summary']['medium']}")
print(f"\n🔴 عناوين IP المشبوهة ({report['summary']['suspicious_ips_count']}):")
for ip_data in report['suspicious_ips'][:10]:
print(f" - {ip_data['ip']}: {ip_data['threat_count']} تهديدات, {ip_data['request_count']} طلبات")
if self.alerts:
print(f"\n⚠️ آخر 5 تنبيهات:")
for alert in self.alerts[-5:]:
print(f" [{alert['severity'].upper()}] {alert['description']} من {alert['source_ip']}")
def export_json(self, filepath: str):
"""تصدير التقرير كـ JSON"""
report = self.generate_report()
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(report, f, ensure_ascii=False, indent=2)
print(f"\n💾 تم حفظ التقرير: {filepath}")
# ═══════════════════════════════════════════════════════════════
# إنشاء ملفات سجلات تجريبية
# ═══════════════════════════════════════════════════════════════
def create_sample_logs():
"""إنشاء ملفات سجلات تجريبية للاختبار"""
os.makedirs('sample_logs', exist_ok=True)
# Nginx sample logs
nginx_logs = """192.168.1.100 - - [26/Jul/2026:14:05:01 +0000] "POST /api/login HTTP/1.1" 401 45 "-" "Mozilla/5.0"
192.168.1.100 - - [26/Jul/2026:14:05:02 +0000] "POST /api/login HTTP/1.1" 401 45 "-" "Mozilla/5.0"
192.168.1.100 - - [26/Jul/2026:14:05:03 +0000] "POST /api/login HTTP/1.1" 401 45 "-" "Mozilla/5.0"
192.168.1.100 - - [26/Jul/2026:14:05:04 +0000] "POST /api/login HTTP/1.1" 401 45 "-" "Mozilla/5.0"
192.168.1.100 - - [26/Jul/2026:14:05:05 +0000] "POST /api/login HTTP/1.1" 401 45 "-" "Mozilla/5.0"
192.168.1.100 - - [26/Jul/2026:14:05:06 +0000] "POST /api/login HTTP/1.1" 200 512 "-" "Mozilla/5.0"
10.0.0.50 - - [26/Jul/2026:14:10:01 +0000] "GET /api/users?id=1' OR '1'='1 HTTP/1.1" 400 120 "-" "sqlmap/1.5"
10.0.0.50 - - [26/Jul/2026:14:10:02 +0000] "GET /api/users?id=1 UNION SELECT * FROM users HTTP/1.1" 400 120 "-" "sqlmap/1.5"
172.16.0.25 - - [26/Jul/2026:14:15:01 +0000] "GET /page?q=<script>alert('xss')</script> HTTP/1.1" 400 80 "-" "Mozilla/5.0"
172.16.0.25 - - [26/Jul/2026:14:15:02 +0000] "GET /../../etc/passwd HTTP/1.1" 403 50 "-" "Mozilla/5.0"
8.8.8.8 - - [26/Jul/2026:14:20:01 +0000] "GET / HTTP/1.1" 200 1024 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
8.8.8.8 - - [26/Jul/2026:14:20:02 +0000] "GET /about HTTP/1.1" 200 2048 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
"""
# Auth sample logs
auth_logs = """Jul 26 14:00:01 server sshd[1234]: Failed password for invalid user admin from 192.168.1.200 port 22 ssh2
Jul 26 14:00:02 server sshd[1234]: Failed password for invalid user admin from 192.168.1.200 port 22 ssh2
Jul 26 14:00:03 server sshd[1234]: Failed password for invalid user root from 192.168.1.200 port 22 ssh2
Jul 26 14:00:04 server sshd[1234]: Failed password for invalid user test from 192.168.1.200 port 22 ssh2
Jul 26 14:00:05 server sshd[1234]: Failed password for invalid user user from 192.168.1.200 port 22 ssh2
Jul 26 14:05:01 server sshd[1235]: Accepted password for developer from 10.0.0.10 port 22 ssh2
"""
# App sample logs
app_logs = """[2026-07-26 14:00:01] [ERROR] [192.168.1.100] Login failed for user: admin
[2026-07-26 14:00:02] [ERROR] [192.168.1.100] Login failed for user: admin
[2026-07-26 14:00:03] [WARN] [10.0.0.50] Suspicious query detected: SELECT * FROM users
[2026-07-26 14:00:04] [INFO] [8.8.8.8] User logged in successfully
[2026-07-26 14:00:05] [ERROR] [172.16.0.25] Invalid input: <script>alert(1)</script>
"""
with open('sample_logs/nginx_access.log', 'w') as f:
f.write(nginx_logs)
with open('sample_logs/auth.log', 'w') as f:
f.write(auth_logs)
with open('sample_logs/app.log', 'w') as f:
f.write(app_logs)
print("✅ تم إنشاء ملفات السجلات التجريبية في sample_logs/")
# ═══════════════════════════════════════════════════════════════
# التشغيل الرئيسي
# ═══════════════════════════════════════════════════════════════
if __name__ == '__main__':
import sys
# إنشاء ملفات تجريبية إذا لم تكن موجودة
if not os.path.exists('sample_logs'):
create_sample_logs()
# تحليل السجلات
analyzer = LogAnalyzer(use_sample=True)
analyzer.analyze_all()
analyzer.print_summary()
# تصدير التقرير
analyzer.export_json('siem_report.json')
print("\n" + "=" * 60)
print("✅ اكتمل التحليل - راجع siem_report.json للتفاصيل")
print("=" * 60)