Log Tabanlı Uyarı Sistemi Kurulumu

Sunuculardan akan log verisi, çoğu zaman bir felaketin habercisini gizli tutar. Disk dolmaya başlar, uygulama hatalar fırlatır, birisi sisteme brute-force dener; ama siz sabah kahvenizi içerken bunların hiçbirinden haberiniz olmaz. İşte bu yüzden pasif log izleme yetmez. Logları aktif olarak analiz eden, anlamlı olayları yakalayan ve sizi anında uyaran bir sistem kurmak, modern bir sysadmin’in temel sorumluluklarından biridir.

Bu yazıda sıfırdan işe yarar bir log tabanlı uyarı sistemi kuracağız. Rsyslog, Logwatch, Fail2Ban, Loki + Grafana ve özel Bash betikleri kullanarak gerçek dünya senaryolarına dayalı bir altyapı oluşturacağız.

Neden Log Tabanlı Uyarı?

Metrik tabanlı izleme araçları (Prometheus, Zabbix gibi) CPU kullanımını, bellek doluluk oranını, disk I/O’sunu takip eder. Bunlar harika araçlar. Ancak bazı kritik olaylar sayısal metriklerde iz bırakmaz ya da bıraktığında iş işten geçmiş olur.

Örneğin:

  • /var/log/auth.log içinde Failed password satırları birikiyorsa biri sisteme girmeye çalışıyor demektir
  • Uygulama logunda NullPointerException sayısı artıyorsa deployment bozuk gitmiştir
  • kern.log içinde Out of memory: Kill process görüyorsanız OOM killer çalışmış, bir servis düşmüştür
  • Nginx access logunda 502 oranı yükseliyorsa backend sağlıksızdır

Bunların hiçbiri bir CPU grafiğine direkt yansımaz. Ama hepsi loglarda açık açık yazar.

Sistem Mimarisine Genel Bakış

Kuracağımız sistemin temel katmanları şöyle:

  • Log Toplama: Rsyslog ile merkezi log yönlendirme
  • Kural Tabanlı Uyarı: Bash + cron ile özel log analizi
  • Otomatik Engelleme: Fail2Ban ile brute-force koruması
  • Görsel İzleme ve Uyarı: Loki + Grafana stack’i
  • Bildirim Katmanı: E-posta, Slack webhook ve Telegram bot entegrasyonu

Ortam olarak Ubuntu 22.04 / Debian tabanlı sistemleri baz alıyoruz, ancak çoğu konfigürasyon RHEL/CentOS’ta da minimal değişiklikle çalışır.

Rsyslog ile Merkezi Log Toplama

Birden fazla sunucunuz varsa logları tek noktada toplamak, uyarı sistemini çok daha yönetilebilir kılar. Rsyslog bu iş için hala en güvenilir araçlardan biridir.

Sunucu Tarafı Konfigürasyonu

Log toplama sunucusunda (logserver) şu konfigürasyonu yapıyoruz:

# /etc/rsyslog.conf dosyasına ekle veya /etc/rsyslog.d/server.conf oluştur

# UDP ve TCP dinleme aktif et
module(load="imudp")
input(type="imudp" port="514")

module(load="imtcp")
input(type="imtcp" port="514")

# Gelen logları kaynak IP'ye göre ayrı dosyalara yaz
$template RemoteLogs,"/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log"
*.* ?RemoteLogs
& ~
# Dizin izinlerini ayarla
mkdir -p /var/log/remote
chown syslog:adm /var/log/remote
chmod 755 /var/log/remote

# Rsyslog'u yeniden başlat
systemctl restart rsyslog

# Firewall kuralı ekle
ufw allow 514/tcp
ufw allow 514/udp

İstemci Tarafı Konfigürasyonu

Log gönderecek sunucularda ise şunu yapıyoruz:

# /etc/rsyslog.d/remote.conf
# Tüm logları merkezi sunucuya ilet
# @ UDP, @@ TCP demektir
*.* @@logserver.sirketim.com:514

# Bağlantı kopuksa kuyruğa al
$ActionQueueFileName queue
$ActionQueueMaxDiskSpace 1g
$ActionQueueSaveOnShutdown on
$ActionQueueType LinkedList
$ActionResumeRetryCount -1

Artık tüm sunucuların logları tek bir yerde toplanıyor. Uyarı sistemimizi bu merkezi sunucu üzerinde çalıştıracağız.

Bash ile Özel Log İzleme Betiği

Hazır araçlara geçmeden önce, log izlemenin temelini anlayalım. Basit ama etkili bir izleme betiği şu mantıkla çalışır: düzenli aralıklarla logu tara, belirlenen kalıpları bul, eşik aşıldıysa uyar.

#!/bin/bash
# /usr/local/bin/log-monitor.sh
# Log tabanlı uyarı betiği

# Konfigürasyon
LOG_FILE="/var/log/nginx/error.log"
ALERT_EMAIL="[email protected]"
THRESHOLD=10
CHECK_INTERVAL=300  # 5 dakika (saniye cinsinden)
STATE_FILE="/tmp/log-monitor-state"

# Son kontrol zamanını al
if [ -f "$STATE_FILE" ]; then
    LAST_CHECK=$(cat "$STATE_FILE")
else
    LAST_CHECK=$(date -d "5 minutes ago" +%s)
fi

CURRENT_TIME=$(date +%s)

# Son kontrol zamanından bu yana yeni hataları say
ERROR_COUNT=$(awk -v last="$LAST_CHECK" '
{
    # Log satırından epoch zamanını parse et (nginx format)
    # Nginx format: 2024/01/15 14:23:01 [error] ...
    cmd = "date -d ""$1" "$2"" +%s 2>/dev/null"
    cmd | getline epoch
    close(cmd)
    if (epoch > last && /[error]/) count++
}
END { print count+0 }
' "$LOG_FILE")

echo "Tespit edilen hata sayisi: $ERROR_COUNT"

# Eşik kontrolü
if [ "$ERROR_COUNT" -gt "$THRESHOLD" ]; then
    HOSTNAME=$(hostname -f)
    MESSAGE="UYARI: $HOSTNAME sunucusunda son 5 dakikada $ERROR_COUNT Nginx hatasi tespit edildi!"
    
    # E-posta gönder
    echo "$MESSAGE" | mail -s "[ALARM] Nginx Hata Esigi Asildi - $HOSTNAME" "$ALERT_EMAIL"
    
    # Syslog'a yaz
    logger -p local0.warning -t log-monitor "$MESSAGE"
    
    echo "$(date): Uyari gonderildi - $ERROR_COUNT hata" >> /var/log/log-monitor.log
fi

# Durum dosyasını güncelle
echo "$CURRENT_TIME" > "$STATE_FILE"

Bu betiği cron ile çalıştırıyoruz:

# Crontab'a ekle: crontab -e
*/5 * * * * /usr/local/bin/log-monitor.sh >> /var/log/log-monitor.log 2>&1

Fail2Ban ile Brute-Force Uyarısı

Fail2Ban sadece IP engellemez, aynı zamanda mükemmel bir log tabanlı uyarı aracıdır. Birisi SSH’a brute-force yaparken hem engeller hem sizi haberdar eder.

# Fail2Ban kurulumu
apt install fail2ban -y

# /etc/fail2ban/jail.local dosyası oluştur
# /etc/fail2ban/jail.local

[DEFAULT]
# Genel ayarlar
bantime  = 3600
findtime = 600
maxretry = 5
destemail = [email protected]
sendername = Fail2Ban-Alert
mta = sendmail
action = %(action_mwl)s

[sshd]
enabled = true
port    = ssh
logpath = %(sshd_log)s
maxretry = 3
bantime = 7200

[nginx-http-auth]
enabled  = true
filter   = nginx-http-auth
logpath  = /var/log/nginx/error.log
maxretry = 6

[nginx-limit-req]
enabled  = true
filter   = nginx-limit-req
action   = iptables-multiport[name=ReqLimit, port="http,https", protocol=tcp]
logpath  = /var/log/nginx/error.log
maxretry = 10
findtime = 60
bantime  = 7200

Fail2Ban’ın e-posta ile Slack bildirimi göndermesi için özel bir action dosyası oluşturalım:

# /etc/fail2ban/action.d/slack-notify.conf

[Definition]
actionstart = 
actionstop  = 
actioncheck = 

actionban = curl -s -X POST 
    -H 'Content-type: application/json' 
    --data '{"text":"*[FAIL2BAN UYARI]* `<ip>` adresi `<name>` servisine brute-force nedeniyle engellendi. Sunucu: '"$(hostname)"'"}' 
    https://hooks.slack.com/services/XXXXX/XXXXX/XXXXX

actionunban = curl -s -X POST 
    -H 'Content-type: application/json' 
    --data '{"text":"*[FAIL2BAN]* `<ip>` engeli kaldirildi. Servis: `<name>`"}' 
    https://hooks.slack.com/services/XXXXX/XXXXX/XXXXX

[Init]
name = default

Grafana Loki ile Gelişmiş Log Uyarısı

Loki, Prometheus’un log dünyasındaki kardeşidir. Logları indekslemek yerine etiketler kullanır, bu da onu son derece hafif ve hızlı yapar. Grafana ile entegre olduğunda görsel bir uyarı sistemi elde edersiniz.

Loki Kurulumu

# Loki binary indir
cd /tmp
wget https://github.com/grafana/loki/releases/download/v2.9.0/loki-linux-amd64.zip
unzip loki-linux-amd64.zip
mv loki-linux-amd64 /usr/local/bin/loki
chmod +x /usr/local/bin/loki

# Promtail (log agent) indir
wget https://github.com/grafana/loki/releases/download/v2.9.0/promtail-linux-amd64.zip
unzip promtail-linux-amd64.zip
mv promtail-linux-amd64 /usr/local/bin/promtail
chmod +x /usr/local/bin/promtail

# Konfigürasyon dizini oluştur
mkdir -p /etc/loki /etc/promtail /var/lib/loki
# /etc/loki/loki-config.yaml

auth_enabled: false

server:
  http_listen_port: 3100
  grpc_listen_port: 9096

common:
  path_prefix: /var/lib/loki
  storage:
    filesystem:
      chunks_directory: /var/lib/loki/chunks
      rules_directory: /var/lib/loki/rules
  replication_factor: 1
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: inmemory

schema_config:
  configs:
    - from: 2020-10-24
      store: boltdb-shipper
      object_store: filesystem
      schema: v11
      index:
        prefix: index_
        period: 24h

ruler:
  alertmanager_url: http://localhost:9093

# Alert kuralları dizini
ruler:
  storage:
    type: local
    local:
      directory: /var/lib/loki/rules
  rule_path: /var/lib/loki/rules-temp
  alertmanager_url: http://localhost:9093
  ring:
    kvstore:
      store: inmemory
  enable_api: true

Promtail Konfigürasyonu

Promtail, log dosyalarını okuyup Loki’ye gönderen agent’tır:

# /etc/promtail/promtail-config.yaml

server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://localhost:3100/loki/api/v1/push

scrape_configs:
  - job_name: system
    static_configs:
      - targets:
          - localhost
        labels:
          job: varlogs
          host: __HOSTNAME__
          __path__: /var/log/*log

  - job_name: nginx
    static_configs:
      - targets:
          - localhost
        labels:
          job: nginx
          host: __HOSTNAME__
          __path__: /var/log/nginx/*.log
    pipeline_stages:
      - regex:
          expression: '(?P<status_code>d{3}) d+'
      - labels:
          status_code:

  - job_name: auth
    static_configs:
      - targets:
          - localhost
        labels:
          job: auth
          host: __HOSTNAME__
          __path__: /var/log/auth.log

Loki Uyarı Kuralları

LogQL ile uyarı kuralları tanımlayabiliriz:

# /var/lib/loki/rules/default/rules.yaml

groups:
  - name: log-alerts
    rules:
      # SSH brute force uyarisi
      - alert: SSHBruteForce
        expr: |
          sum(count_over_time({job="auth"} |= "Failed password" [5m])) > 20
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "SSH Brute Force Saldirisi"
          description: "Son 5 dakikada 20'den fazla basarisiz SSH girisi tespit edildi."

      # Nginx 5xx hatalari
      - alert: NginxHighErrorRate
        expr: |
          sum(rate({job="nginx", filename=~".*access.*"} |= "" 5" [5m])) > 10
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "Nginx Yuksek 5xx Hata Orani"
          description: "Nginx sunucusunda 5xx hata orani dakikada 10'u gecti."

      # OOM Killer uyarisi
      - alert: OOMKillerTriggered
        expr: |
          count_over_time({job="varlogs"} |= "Out of memory: Kill process" [10m]) > 0
        for: 0m
        labels:
          severity: critical
        annotations:
          summary: "OOM Killer Devreye Girdi"
          description: "Sunucuda bellek yetersizligi nedeniyle bir proses olduruldu."

Telegram Bot ile Anlık Bildirim

E-posta bildirimlerini kaçırabilirsiniz, ama Telegram bildirimi telefona anında düşer. Üstelik kurmak çok kolay.

Önce @BotFather’dan bir bot oluşturun, token alın ve chat ID’nizi öğrenin. Ardından:

#!/bin/bash
# /usr/local/bin/telegram-alert.sh
# Kullanim: telegram-alert.sh "Mesaj metni" [severity]

BOT_TOKEN="1234567890:ABCDEFGHIJKLMNOP"
CHAT_ID="-100123456789"  # Grup ID'si veya bireysel chat ID
SEVERITY=${2:-"INFO"}
HOSTNAME=$(hostname -f)
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

# Severity'e gore emoji belirle
case "$SEVERITY" in
    "CRITICAL") EMOJI="🔴" ;;
    "WARNING")  EMOJI="🟡" ;;
    "INFO")     EMOJI="🟢" ;;
    *)          EMOJI="⚪" ;;
esac

MESSAGE="${EMOJI} *[${SEVERITY}]* ${TIMESTAMP}
*Sunucu:* `${HOSTNAME}`
*Mesaj:* ${1}"

curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" 
    -d chat_id="${CHAT_ID}" 
    -d parse_mode="Markdown" 
    -d text="${MESSAGE}" 
    > /dev/null 2>&1

# Cikis kodu kontrol
if [ $? -eq 0 ]; then
    logger -p local0.info -t telegram-alert "Bildirim gonderildi: $1"
else
    logger -p local0.error -t telegram-alert "Bildirim gonderilemedi!"
    exit 1
fi

Bu betiği log izleme sistemimizle birleştirelim. Kritik bir hata yakalandığında hem e-posta hem Telegram bildirimi gönderelim:

#!/bin/bash
# /usr/local/bin/critical-log-watcher.sh
# Kritik log desenlerini gercek zamanli izle

WATCH_PATTERNS=(
    "CRITICAL"
    "Out of memory"
    "disk quota exceeded"
    "Connection refused"
    "Segmentation fault"
    "kernel: BUG"
    "Authentication failure"
)

LOG_DIR="/var/log/remote"
ALERT_COOLDOWN=300  # Ayni uyari icin 5 dakika bekleme suresi
COOLDOWN_DIR="/tmp/alert-cooldown"
mkdir -p "$COOLDOWN_DIR"

send_alert() {
    local pattern="$1"
    local log_line="$2"
    local log_file="$3"
    
    # Cooldown kontrolu (ayni pattern icin spam onle)
    local cooldown_file="$COOLDOWN_DIR/$(echo "$pattern" | md5sum | cut -d' ' -f1)"
    
    if [ -f "$cooldown_file" ]; then
        local last_alert=$(cat "$cooldown_file")
        local now=$(date +%s)
        if [ $((now - last_alert)) -lt $ALERT_COOLDOWN ]; then
            return 0  # Cooldown suresi dolmamis, uyari gonderme
        fi
    fi
    
    # Uyari gonder
    local message="Log eslesme: '$pattern'
Dosya: $log_file
Satir: $log_line"
    
    /usr/local/bin/telegram-alert.sh "$message" "CRITICAL"
    echo "$log_line" | mail -s "[KRITIK] Log Alarmi: $pattern" [email protected]
    
    # Cooldown zamanini guncelle
    date +%s > "$cooldown_file"
    
    logger -p local0.crit -t critical-log-watcher "Alert gonderildi: $pattern"
}

# Tail ile gercek zamanli izleme
tail -F "$LOG_DIR"/*/*.log 2>/dev/null | while read -r line; do
    for pattern in "${WATCH_PATTERNS[@]}"; do
        if echo "$line" | grep -qi "$pattern"; then
            # Hangi dosyadan geldigini bul
            send_alert "$pattern" "$line" "stream"
            break
        fi
    done
done

Logwatch ile Günlük Özet Raporları

Anlık uyarıların yanı sıra, her sabah bir özet raporu almak için Logwatch mükemmel bir araçtır:

# Logwatch kurulumu
apt install logwatch -y

# Gunluk rapor icin cron ayari
# /etc/cron.daily/00logwatch dosyasini duzenle
/usr/sbin/logwatch 
    --output mail 
    --mailto [email protected] 
    --detail high 
    --range yesterday 
    --service All 
    --format html

Logwatch’a özel bir servis tanımı ekleyelim. Nginx için özel bir analiz modülü:

# /etc/logwatch/scripts/services/nginx-custom

#!/usr/bin/perl
use strict;
use warnings;

my %status_codes;
my %top_ips;
my $total_requests = 0;
my $error_count = 0;

while (defined(my $line = <STDIN>)) {
    chomp $line;
    
    # Access log parse
    if ($line =~ /^(S+) .* "(S+) (S+) S+" (d{3})/) {
        my ($ip, $method, $uri, $status) = ($1, $2, $3, $4);
        $status_codes{$status}++;
        $top_ips{$ip}++;
        $total_requests++;
        $error_count++ if $status >= 500;
    }
}

print "Toplam istek: $total_requestsn";
print "5xx hata sayisi: $error_countnn";
print "HTTP Durum Kodlari:n";
foreach my $code (sort keys %status_codes) {
    printf "  %s: %dn", $code, $status_codes{$code};
}

print "nEn cok istek yapan IP'ler (ilk 10):n";
my $count = 0;
foreach my $ip (sort { $top_ips{$b} <=> $top_ips{$a} } keys %top_ips) {
    last if $count++ >= 10;
    printf "  %-20s: %d istekn", $ip, $top_ips{$ip};
}

Gerçek Dünya Senaryosu: E-Ticaret Sunucusunda Kriz Yönetimi

Bir e-ticaret şirketinde çalıştığınızı düşünün. Cuma gecesi yoğun satış sezonu başlamış. Sistemler normal görünüyor ama aşağıdaki log satırları birikmekte:

[error] 1234#1234: *45678 upstream timed out (110: Connection timed out)
php-fpm: ERROR: [pool www] child 12345 exited with code 255
mysql: InnoDB: page_cleaner: 1000ms intended loop took 4521ms

Log tabanlı uyarı sisteminiz çalışıyor olsaydı:

  • Nginx upstream timeout sayısı eşiği geçtiğinde (dakikada 5’ten fazla) ilk uyarı gelir
  • PHP-FPM child çöküşleri tespit edilir, ikinci uyarı tetiklenir
  • MySQL InnoDB yavaşlama uyarısı üçüncü sinyali verir

Bunların hepsi aynı anda geldiğinde “veritabanı darboğazı var, PHP-FPM havuzu bitmiş” tanısını 2 dakikada koyarsınız. Sistemsiz bir ortamda bunu anlamak 30-45 dakika sürebilir.

Bu senaryoya özel bir uyarı kuralı:

# /etc/loki/rules/ecommerce-alerts.yaml

groups:
  - name: ecommerce
    rules:
      - alert: UpstreamTimeout
        expr: |
          sum(count_over_time({job="nginx"} |= "upstream timed out" [3m])) > 15
        for: 1m
        labels:
          severity: critical
          team: backend
        annotations:
          summary: "Backend upstream timeout artisi"
          description: "Son 3 dakikada 15'ten fazla upstream timeout. Backend saglik kontrolu yapilmali."
          runbook: "https://wiki.sirketim.com/runbooks/upstream-timeout"

Sonuç

Log tabanlı uyarı sistemi kurmak tek seferlik bir iş değil, sürekli iyileştirilen bir yapıdır. Başlangıçta çok fazla uyarı alabilirsiniz; bu normaldir. Eşik değerlerini sisteminizin normal davranışına göre kalibre edin.

Önemli birkaç nokta:

  • Alarm yorgunluğuna dikkat edin. Çok fazla uyarı, önemli olanların göz ardı edilmesine neden olur. Her uyarı aksiyona dönüşmeli.
  • Cooldown mekanizması şart. Aynı sorun için dakikada 50 bildirim almak yerine 5 dakikada bir özet alın.
  • Runbook bağlantısı ekleyin. Her uyarıya “bu durumda ne yapılır” sorusunun cevabını veren bir wiki linki ekleyin.
  • Log rotasyonunu ihmal etmeyin. İzlediğiniz log dosyaları rotate olduğunda tail -F ve Promtail bunu otomatik yakalar, ama bazı araçlar dosya tanımlayıcısını kaybeder. Test edin.
  • Merkezi log sunucusunu da izleyin. Log sunucunuz çökerse hiçbir şeyden haberiniz olmaz. Meta izleme, yani izleme sistemini izlemek, kritik öneme sahiptir.

Kurduğunuz sistem ne kadar karmaşık olursa olsun, amacı basit kalmalı: doğru kişiye, doğru zamanda, aksiyona dönüştürülebilir bilgi ulaştırmak. Bu sağlandığında, gece 3’te patlayan bir kriz yerine sabah kahvenizle birlikte “dün gece şu uyarı geldi, çözüldü” raporunu okursunuz.

Similar Posts

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir