Cuma Sabahı 08:47: dc-01 Erişilemiyor Uyarısı
Cuma sabahı 08:47'de ShamashAi Dashboard'unda kırmızı bir uyarı belirir: "dc-01.prod.local - TCP:3389 erişilemiyor (timeout 3000ms)". Sistem yöneticisi Mehmet, hemen Device Health paneline bakar. Son 10 dakikada dc-01 sunucusuna 6 ardışık TCP probe gönderilmiş, tümü başarısız. Latency değerleri sıfır, status 'fail', message: "Connection refused or timeout". Aynı anda
events tablosunda 14 farklı
BRUTE_FORCE_DETECTED, 3
M365_RISKY_SIGNIN kaydı var — bu kritik güvenlik olayları.
Mehmet, şükür eder ki
reachability log'ları events tablosunu şişirtmemiş. Eğer her 2 dakikada bir 50 cihaza gönderilen ICMP ping ve TCP check'ler events tablosunda dursaydı, günde onlarca bin kayıt birikir, sorgular yavaşlar, asıl tehditleri görmek zorlaşırdı. ShamashAi bu sorunu tasarım aşamasında çözdü:
reachability_logs adlı ayrı bir tabloya yazarak
hot-path performansını korudu ve
event_type semantiğini bozmadı.
Bu yazıda ShamashAi'nin reachability probe sisteminin mimari kararlarını, tablo yapısını, retention politikasını ve kullanım senaryolarını teknik detaylarıyla açıklıyoruz.
Neden Ayrı Tablo? Event Tablosunu Kirletme Riski
Events Tablosunun Amacı: Güvenlik Olayları
ShamashAi'nin merkezi
dbo.events tablosu, güvenlik olaylarını saklar. Her satır bir
event_type (BRUTE_FORCE_DETECTED, AUTH_FAIL_USER, KNOWN_BAD_IP, BEHAVIORAL_ANOMALY, CERT_EXPIRING, SOAR_ACTION, BACKUP_FAILED vb.) taşır. Bu tablonun sorguları, SIEM'in çekirdeğini oluşturur:
- Gerçek zamanlı korelasyon kuralları (örn. "10 dakikada 5'ten fazla AUTH_FAIL_USER aynı IP'den → BRUTE_FORCE_DETECTED tetikle")
- Incident gruplandırma (dbo.incident_groups ile ilişkilendirme)
- AI/ML model besleme (POST /ai/investigate-event endpoint'i event_type'lara bakarak pattern arar)
- Compliance raporları (GET /compliance/evidence, KVKK Madde 12 kanıtları)
Events tablosu hot-path'tir: saniyede onlarca yeni kayıt, yüzlerce okuma. Index'ler (event_type, timestamp, source_device_id) bu hızı sağlar.
Health Check Log'ları Farklı Bir Kategori
Reachability probe'ları (TCP port check, ICMP ping) ise
operational telemetry'dir — cihazların erişilebilirlik durumunu izler, güvenlik tehdidi değildir. Özellikleri:
- Yüksek hacim: Eğer 200 cihaza 2 dakikada bir probe gönderilirse günde ~144.000 kayıt.
- Farklı semantik: "status: ok/warn/fail/unknown", "latency_ms" gibi alanlar event_type semantiğine uymaz.
- Farklı retention: Health check log'ları daha kısa süre saklanabilir (genelde 30 gün yeter), events ise compliance gereklilikleri nedeniyle 90+ gün tutulur.
- Farklı okuma patern: Reachability log'ları genelde Device Detail sayfasında veya "son 1 saat erişilemeyen cihazlar" sorgusuyla okunur; correlation engine'e girmez.
Events tablosuna reachability kayıtları yazılsaydı:
1.
Index şişmesi: event_type index'ine anlamsız "HEALTH_CHECK" değerleri binerce kez eklenir.
2.
Query yavaşlaması: "Son 1 saatte BRUTE_FORCE_DETECTED olayları" sorgusu, 7200 health check kaydını da taramak zorunda kalır (WHERE event_type = 'BRUTE_FORCE_DETECTED' filtresinde bile index scan pahalılaşır).
3.
Karışık semantik: Correlation kuralları "event_type IN ('BRUTE_FORCE_DETECTED', 'AUTH_FAIL_USER')" yazarken sürekli "AND event_type NOT LIKE '%HEALTH%'" eklemek zorunda kalır.
4.
Retention karmaşası: Events 90 gün tutulurken health check'leri 30 günde silmek için ekstra script gerekir.
ShamashAi Reachability_Logs Tablosu: Yapı ve Alanlar
ShamashAi,
dbo.reachability_logs tablosunu şu şemayla tasarladı:
sql
CREATE TABLE dbo.reachability_logs (
id BIGINT IDENTITY PRIMARY KEY,
timestamp DATETIME2(3) NOT NULL,
source VARCHAR(255) NOT NULL, -- device hostname veya IP
check_type VARCHAR(50) NOT NULL, -- 'TCP', 'ICMP'
target VARCHAR(255), -- hedef IP/hostname (TCP için port:3389 vs.)
status VARCHAR(20) NOT NULL, -- 'ok', 'warn', 'fail', 'unknown'
latency_ms INT, -- ping/response süresi (ms)
message NVARCHAR(1000), -- "Connection refused", "Timeout 3000ms" vb.
INDEX idx_timestamp (timestamp DESC),
INDEX idx_source_status (source, status, timestamp DESC)
);
Alan Açıklamaları
- check_type: 'TCP' (belirli porta bağlanma denemeleri, örn. 3389/RDP, 22/SSH) veya 'ICMP' (ping).
-
ok: Probe başarılı, cihaz erişilebilir.
-
warn: Yavaş yanıt (örn. latency_ms > 500) ama erişilebilir.
-
fail: Timeout, connection refused, host unreachable.
-
unknown: Probe gönderildi ama sonuç belirsiz (nadiren, network hatası).
- latency_ms: Round-trip süresi. ICMP için ping latency, TCP için SYN-ACK süresi.
- message: Hata mesajı (örn. "EHOSTUNREACH", "Timeout 5000ms") veya başarı notu ("Response in 12ms").
Örnek kayıt:
{
"timestamp": "2026-08-12T08:47:23.456Z",
"source": "dc-01.prod.local",
"check_type": "TCP",
"target": "dc-01.prod.local:3389",
"status": "fail",
"latency_ms": null,
"message": "Connection refused or timeout (3000ms)"
}
API ve UI Entegrasyonu
GET /reachability/logs Endpoint
ShamashAi API, reachability log'larını sorgulamak için
GET /reachability/logs endpoint'ini sunar:
Request (Node.js Fastify route örneği):
javascript
// routes/reachability.js
module.exports = async function (fastify, opts) {
fastify.get('/reachability/logs', {
schema: {
querystring: {
type: 'object',
properties: {
source: { type: 'string' }, // device hostname/IP
status: { type: 'string', enum: ['ok', 'warn', 'fail', 'unknown'] },
q: { type: 'string' }, // free text search (message içinde)
start: { type: 'string', format: 'date-time' },
end: { type: 'string', format: 'date-time' },
limit: { type: 'integer', default: 100, maximum: 1000 }
}
}
},
preHandler: fastify.authenticate // JWT check
}, async (request, reply) => {
const { source, status, q, start, end, limit } = request.query;
const db = fastify.mssql.pool;
let query = 'SELECT TOP (@limit) * FROM dbo.reachability_logs WHERE 1=1';
const params = { limit };
if (source) {
query += ' AND source = @source';
params.source = source;
}
if (status) {
query += ' AND status = @status';
params.status = status;
}
if (q) {
query += ' AND message LIKE @q';
params.q =
%${q}%;
}
if (start) {
query += ' AND timestamp >= @start';
params.start = start;
}
if (end) {
query += ' AND timestamp <= @end';
params.end = end;
}
query += ' ORDER BY timestamp DESC';
const result = await db.request()
.input('limit', fastify.mssql.Int, params.limit)
.input('source', fastify.mssql.VarChar, params.source)
.input('status', fastify.mssql.VarChar, params.status)
.input('q', fastify.mssql.NVarChar, params.q)
.input('start', fastify.mssql.DateTime2, params.start)
.input('end', fastify.mssql.DateTime2, params.end)
.query(query);
return { logs: result.recordset, count: result.recordset.length };
});
};
Response:
{
"logs": [
{
"id": 9823471,
"timestamp": "2026-08-12T08:47:23.456Z",
"source": "dc-01.prod.local",
"check_type": "TCP",
"target": "dc-01.prod.local:3389",
"status": "fail",
"latency_ms": null,
"message": "Connection refused or timeout (3000ms)"
},
{
"id": 9823470,
"timestamp": "2026-08-12T08:45:11.234Z",
"source": "dc-01.prod.local",
"check_type": "ICMP",
"target": "dc-01.prod.local",
"status": "ok",
"latency_ms": 12,
"message": "Response in 12ms"
}
],
"count": 2
}
Filtreler: source, status, free text
ShamashAi dokümantasyonu şu filtreleri açıkça tanımlar:
- source: Device hostname veya IP (örn. "dc-01.prod.local" veya "10.20.30.40").
- status: ok/warn/fail/unknown enum değeri.
- q (free text): message alanında arama. Örneğin
q=timeout tüm timeout içeren kayıtları döner.
- start / end: ISO 8601 zaman aralığı (örn. "2026-08-12T00:00:00Z").
Bu filtreler, Device Detail sayfasında veya Reachability UI'da "Son 1 saat erişilemeyen cihazlar" sorgusunda kullanılır.
DeviceHealthConnector Feed Mekanizması
ShamashAi .NET 8 agent'ı içinde
DeviceHealthConnector modülü, reachability_logs tablosunu besler. Tasarım:
1.
Device Registry:
dbo.devices tablosundaki her cihaz için
health_check_enabled flag'i true ise, probe ayarları (check_type, interval_seconds, target_port) kaydedilir.
2.
Probe Scheduler: Agent, her
interval_seconds (default 120) saniyede bir TCP veya ICMP probe gönderir.
3.
Result Writer: Probe sonucu (status, latency_ms, message)
reachability_logs tablosuna INSERT edilir —
dbo.events tablosuna değil.
Örnek .NET 8 DeviceHealthConnector kodu (basitleştirilmiş):
csharp
// Services/DeviceHealthConnector.cs
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using Microsoft.Data.SqlClient;
public class DeviceHealthConnector : BackgroundService
{
private readonly IConfiguration _config;
private readonly ILogger<DeviceHealthConnector> _logger;
public DeviceHealthConnector(IConfiguration config, ILogger<DeviceHealthConnector> logger)
{
_config = config;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var connectionString = _config.GetConnectionString("ShamashDb");
var intervalMs = _config.GetValue<int>("HealthCheck:IntervalSeconds", 120) * 1000;
while (!stoppingToken.IsCancellationRequested)
{
await RunProbes(connectionString);
await Task.Delay(intervalMs, stoppingToken);
}
}
private async Task RunProbes(string connStr)
{
using var conn = new SqlConnection(connStr);
await conn.OpenAsync();
// dbo.devices tablosundan health_check_enabled=true cihazları çek
var cmd = new SqlCommand(
"SELECT hostname, ip_address, health_check_type, health_check_port FROM dbo.devices WHERE health_check_enabled=1",
conn);
using var reader = await cmd.ExecuteReaderAsync();
var tasks = new List<Task>();
while (await reader.ReadAsync())
{
var hostname = reader.GetString(0);
var ip = reader.GetString(1);
var checkType = reader.GetString(2); // "TCP" veya "ICMP"
var port = reader.IsDBNull(3) ? 0 : reader.GetInt32(3);
tasks.Add(ProbeDevice(connStr, hostname, ip, checkType, port));
}
await Task.WhenAll(tasks);
}
private async Task ProbeDevice(string connStr, string hostname, string ip, string checkType, int port)
{
string status, message;
int? latencyMs = null;
var sw = Stopwatch.StartNew();
try
{
if (checkType == "ICMP")
{
using var ping = new Ping();
var reply = await ping.SendPingAsync(ip, 3000);
if (reply.Status == IPStatus.Success)
{
status = "ok";
latencyMs = (int)reply.RoundtripTime;
message = $"Response in {latencyMs}ms";
}
else
{
status = "fail";
message = $"ICMP failed: {reply.Status}";
}
}
else // TCP
{
using var client = new TcpClient();
await client.ConnectAsync(ip, port).WaitAsync(TimeSpan.FromSeconds(3));
sw.Stop();
status = "ok";
latencyMs = (int)sw.ElapsedMilliseconds;
message = $"TCP {port} open, latency {latencyMs}ms";
}
}
catch (Exception ex)
{
sw.Stop();
status = "fail";
message = ex.Message.Length > 900 ? ex.Message.Substring(0, 900) : ex.Message;
}
// reachability_logs tablosuna yaz
await WriteLog(connStr, hostname, checkType, $"{ip}:{port}", status, latencyMs, message);
}
private async Task WriteLog(string connStr, string source, string checkType, string target, string status, int? latencyMs, string message)
{
using var conn = new SqlConnection(connStr);
await conn.OpenAsync();
var cmd = new SqlCommand(
"INSERT INTO dbo.reachability_logs (timestamp, source, check_type, target, status, latency_ms, message) " +
"VALUES (SYSDATETIME(), @source, @checkType, @target, @status, @latencyMs, @message)",
conn);
cmd.Parameters.AddWithValue("@source", source);
cmd.Parameters.AddWithValue("@checkType", checkType);
cmd.Parameters.AddWithValue("@target", target);
cmd.Parameters.AddWithValue("@status", status);
cmd.Parameters.AddWithValue("@latencyMs", (object?)latencyMs ?? DBNull.Value);
cmd.Parameters.AddWithValue("@message", message);
await cmd.ExecuteNonQueryAsync();
_logger.LogDebug("Reachability log written: {source} {status}", source, status);
}
}
Bu kod,
dbo.events tablosuna hiç dokunmaz — sadece
reachability_logs yazar.
Retention Politikası: 30 Gün vs. 90 Gün
ShamashAi dokümantasyonu açıkça şunu belirtir:
- reachability_logs retention: 30 gün (default). Health check log'ları, cihaz erişilebilirlik trendlerini görmek için kısa vadeli kullanılır.
- dbo.events retention: 90 gün (KVKK Madde 12 uyum, ISO 27001:2022 Annex A kanıt saklama gerekliliği).
Neden bu fark?
1.
Hacim farkı: 200 cihaza 2 dakikada bir probe = günde ~144.000 kayıt. 30 günde 4.3M satır. 90 güne çıkarsa 13M satır — disk ve index maliyeti artar.
2.
Compliance gerekliliği yok: Reachability log'ları güvenlik tehdidi değil, operasyonel veridir. KVKK raporlarına girmez.
3.
Trend analizi yeterli: Bir cihazın son 30 gündeki uptime/downtime trendi, kapasite planlaması için yeterli.
Retention job (SQL Server Agent veya Hangfire ile):
sql
-- Her gece 02:00'de çalışan job
DELETE FROM dbo.reachability_logs
WHERE timestamp < DATEADD(day, -30, GETDATE());
Events tablosunda ise:
sql
DELETE FROM dbo.events
WHERE timestamp < DATEADD(day, -90, GETDATE());
Bu iki ayrı retention policy, tablolar ayrı olduğu için kolayca uygulanır.
Reachability Sayfası: Ayrı UI
ShamashAi Next.js web uygulamasında
Reachability adlı ayrı bir sayfa vardır:
- Son 24 saat/7 gün/30 gün filtresi.
- Status bazlı renk kodlu tablo (ok=yeşil, warn=sarı, fail=kırmızı).
- Free text arama (message alanında).
- Device Detail sayfasından linkleme: Her cihazın "Health Check History" butonu,
/reachability?source=dc-01.prod.local açar.
- Events sayfasından ayrılık: Events sayfası (dbo.events) event_type, severity, incident_id gösterir. Reachability sayfası ise check_type, latency_ms, status gösterir — karışmaz.
Örnek Next.js component:
typescript
// pages/reachability.tsx
import { useQuery } from '@tanstack/react-query';
import { useState } from 'react';
const ReachabilityPage = () => {
const [filters, setFilters] = useState({ status: '', source: '', q: '' });
const { data, isLoading } = useQuery(['reachability', filters], async () => {
const params = new URLSearchParams(
Object.entries(filters).filter(([_, v]) => v !== '')
);
const res = await fetch(
/api/reachability/logs?${params}, {
headers: { Authorization:
Bearer ${localStorage.getItem('token')} }
});
return res.json();
});
if (isLoading) return <div>Yükleniyor...</div>;
return (
<div className="p-6">
<h1 className="text-2xl font-bold mb-4">Reachability Logs</h1>
<div className="flex gap-4 mb-4">
<input
placeholder="Source (hostname/IP)"
value={filters.source}
onChange={e => setFilters({ ...filters, source: e.target.value })}
className="border p-2"
/>
<select
value={filters.status}
onChange={e => setFilters({ ...filters, status: e.target.value })}
className="border p-2"
>
<option value="">Tüm durumlar</option>
<option value="ok">OK</option>
<option value="warn">Warn</option>
<option value="fail">Fail</option>
</select>
<input
placeholder="Message'da ara (free text)"
value={filters.q}
onChange={e => setFilters({ ...filters, q: e.target.value })}
className="border p-2"
/>
</div>
<table className="w-full border">
<thead>
<tr className="bg-gray-100">
<th className="p-2">Timestamp</th>
<th>Source</th>
<th>Check Type</th>
<th>Target</th>
<th>Status</th>
<th>Latency (ms)</th>
<th>Message</th>
</tr>
</thead>
<tbody>
{data?.logs.map((log: any) => (
<tr key={log.id} className={log.status === 'fail' ? 'bg-red-50' : ''}>
<td className="p-2">{new Date(log.timestamp).toLocaleString('tr-TR')}</td>
<td>{log.source}</td>
<td>{log.check_type}</td>
<td>{log.target}</td>
<td>
<span className={`px-2 py-1 rounded ${
log.status === 'ok' ? 'bg-green-100 text-green-800' :
log.status === 'warn' ? 'bg-yellow-100 text-yellow-800' :
'bg-red-100 text-red-800'
}`}>
{log.status.toUpperCase()}
</span>
</td>
<td>{log.latency_ms ?? '-'}</td>
<td className="text-sm">{log.message}</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default ReachabilityPage;
Performans Kazanımları: Somut Metrikler
200 cihazlı bir ortamda, 2 dakikalık probe interval varsayımıyla:
- Günlük reachability kayıt: ~144.000 (200 cihaz × 720 probe/gün).
- Günlük security event: ~500-2000 (brute force, auth fail, anomaly vb.).
Eğer reachability kayıtları
events tablosunda tutulunsaydı:
- Events tablosu büyüklüğü: 90 günde 13M reachability + 90K security = 13.09M satır.
- Query: "Son 1 saatte BRUTE_FORCE_DETECTED" → WHERE event_type = 'BRUTE_FORCE_DETECTED' AND timestamp > DATEADD(hour, -1, GETDATE()) index scan'i 1 saatte biriken 6000 reachability kaydını da tarar (index selectivity düşer).
- Index boyutu: event_type index'i 13M satıra yayılır, cache miss riski artar.
Ayrı tablo ile:
- Events tablosu: 90 günde sadece 90K satır (güvenlik olayları).
- Reachability_logs: 30 günde 4.3M satır, ama events query'lerine hiç dokunmaz.
- Query "Son 1 saatte BRUTE_FORCE_DETECTED" sadece 90K satırlık tablodan okur — hız kazancı ~100x.
- Index boyutu: event_type index'i 90K satır, CPU cache'e sığar.
Disk kullanımı:
- Events (90 gün, 90K satır, ~500 byte/satır): ~45 MB.
- Reachability_logs (30 gün, 4.3M satır, ~200 byte/satır): ~860 MB.
- Toplam: ~905 MB. Eğer reachability events'te tutulunsaydı (90 gün): 13.09M × 400 byte = ~5.2 GB. Disk tasarrufu: ~4.3 GB.
Kullanım Senaryoları
1. Cihaz Downtime Analizi
SOC analisti, "dc-05 sunucusu neden dün gece 03:00-04:00 arası erişilemedi?" sorusunu araştırır:
bash
curl -H "Authorization: Bearer $TOKEN" \
"https://shamashai.local/api/reachability/logs?source=dc-05.prod.local&start=2026-08-11T03:00:00Z&end=2026-08-11T04:00:00Z"
Dönüş:
{
"logs": [
{ "timestamp": "2026-08-11T03:12:45Z", "status": "fail", "message": "Timeout 3000ms" },
{ "timestamp": "2026-08-11T03:14:50Z", "status": "fail", "message": "Timeout 3000ms" },
{ "timestamp": "2026-08-11T03:58:12Z", "status": "ok", "latency_ms": 18 }
]
}
Sonuç: 03:12-03:58 arası erişilememiş, 03:58'de normale dönmüş. Bu veriler
dbo.events tablosunu kirletmemiş.
2. Yavaş Yanıt Veren Cihazları Bulma
"Latency > 200ms olan tüm cihazlar" sorgusu:
sql
SELECT source, AVG(latency_ms) AS avg_latency, COUNT(*) AS probe_count
FROM dbo.reachability_logs
WHERE timestamp > DATEADD(day, -7, GETDATE()) AND status = 'ok'
GROUP BY source
HAVING AVG(latency_ms) > 200
ORDER BY avg_latency DESC;
Bu sorgu,
events tablosuna dokunmadan reachability_logs'dan çeker.
3. Device Detail Sayfasında Health History
Kullanıcı Device Detail sayfasında "dc-01" seçer, "Health History" butonu
/reachability?source=dc-01.prod.local&start=2026-08-05T00:00:00Z açar. Son 7 gündeki probe sonuçları grafik olarak gösterilir (ok/fail dağılımı).
Limitler ve Dürüst Notlar
1.
Probe interval konfigürasyonu UI'da yok: Şu an probe interval (default 120 saniye)
dbo.devices tablosunda elle set edilir veya deployment sırasında config'de tanımlanır. ShamashAi Admin UI'da "Device Settings > Health Check Interval" sayfası henüz yok; bu pilot sürecinde manuel yapılandırılır.
2.
Reachability log'ları correlation engine'e girmez:
dbo.reachability_logs tablosu SIEM correlation kurallarına beslenmez. Yani "cihaz 10 dakikadır erişilemiyor → otomatik incident oluştur" kuralı ShamashAi v1.0'da yok. Bu, roadmap'te "Operational Event Integration" başlığı altında.
3.
Free text arama performansı:
q parametresi
message alanında LIKE sorgusu yapar — index'siz full-text search. 4M+ satırda yavaş olabilir. Eğer sık kullanılırsa SQL Server Full-Text Index eklenebilir, ancak varsayılan kurulumda yok.
4.
Alerting reachability'de sınırlı: ShamashAi, reachability
status=fail için basit e-posta bildirimi gönderebilir (dbo.devices tablosunda
alert_on_fail flag'i varsa), ancak SOAR entegrasyonu (POST /soar/block gibi) reachability olaylarına uygulanmaz — sadece güvenlik olaylarına (dbo.events).
5.
Multi-site probe yok: Eğer firmanın İstanbul ve Ankara ofisi varsa,
dbo.sites tablosu var ama reachability probe'ları şu an merkezi agent'tan çıkar. Yani Ankara'daki cihaza İstanbul agent'ı probe atar — WAN latency'si gerçek lokal erişilebilirliği yansıtmayabilir. Multi-site probe agent mimarisi roadmap'te.
Sonuç: Ayrı Tablo, Temiz Mimari, Hızlı Sorgular
ShamashAi'nin
reachability_logs tablosunu
dbo.events'ten ayırması, mimari bir temizlik ve performans optimizasyonudur. Health check log'ları güvenlik olaylarını kirletmez, hot-path sorguları yavaşlatmaz, retention politikaları karışmaz.
GET /reachability/logs endpoint'i ve ayrı UI, operasyonel telemetry'yi SIEM semantiğinden izole eder.
200 cihazlı bir ortamda, events tablosu 90K satır, reachability_logs 4.3M satır tutarak
disk 4+ GB tasarruf, query hızı
~100x artış sağlar. DeviceHealthConnector .NET 8 modülü, TCP ve ICMP probe sonuçlarını doğru tabloya yazar.
Pilot programı 30 gün ücretsiz: shamashai.com.tr/iletisim