fanotify ve inotifywait ile Dosya Sistemi Olaylarını İzleme ve Tetikleyici Oluşturma

Üretim ortamında bir şeyler ters gittiğinde, çoğu zaman ilk soru şu oluyor: “Bu dosya ne zaman değişti, kim değiştirdi?” Audit log’larına bakıyorsun, uygulama loglarını karıştırıyorsun ama gerçek zamanlı izleme yoksa cevabı bulmak saatler alıyor. İşte tam bu noktada Linux’un dosya sistemi olay altyapısı devreye giriyor. inotify ve fanotify mekanizmaları, kernel seviyesinde dosya sistemi olaylarını yakalamana ve bunlara anında tepki vermene olanak tanıyor. Bu yazıda hem teorik altyapıyı hem de gerçek dünya senaryolarında nasıl kullandığımı paylaşacağım.

Kernel Tarafında Ne Oluyor?

Linux kernel’i, dosya sistemi olaylarını iki farklı arayüz üzerinden kullanıcı alanına taşır: inotify ve fanotify. İkisi de farklı amaçlar için tasarlanmış.

inotify, 2005 yılında kernel 2.6.13 ile geldi. Belirli dosya ve dizinleri izlemek için kullanılır, hafif ve kullanımı kolaydır. Ancak bir sınırlaması var: izleme yaptığın dizinin alt dizinlerini özyinelemeli olarak izleyemez, bunu manuel olarak halletmen gerekir.

fanotify ise çok daha güçlü. 2009’da kernel 2.6.36 ile tanıtıldı ama asıl yetenekleri sonraki versiyonlarda geldi. Mount point seviyesinde izleme yapabilir, dosya erişimini engelleyebilir (permission event), işlem kimliğini raporlayabilir. Yani bir antivirüs, bir DLP (Data Loss Prevention) sistemi ya da gerçek zamanlı backup çözümü yazmak istiyorsan fanotify senin arkadaşın.

inotifywait ile Başlamak

Araç kurulumu basit:

# Debian/Ubuntu
apt install inotify-tools

# RHEL/CentOS/Rocky
dnf install inotify-tools

# Arch
pacman -S inotify-tools

inotify-tools paketi sana iki komut veriyor: inotifywait (tek seferlik veya sürekli izleme) ve inotifywatch (istatistik toplama). Biz çoğunlukla inotifywait kullanacağız.

Temel kullanım:

inotifywait -m /etc/nginx/conf.d/

Bu komut /etc/nginx/conf.d/ dizinini izlemeye başlar ve her olay geldiğinde ekrana basar. -m (monitor) olmadan tek olay bekler ve çıkar.

Olay Tipleri

İzleyebileceğin başlıca olaylar şunlar:

  • CREATE: Dosya veya dizin oluşturuldu
  • MODIFY: Dosya içeriği değişti
  • CLOSE_WRITE: Yazma amaçlı açılan dosya kapatıldı
  • DELETE: Dosya veya dizin silindi
  • MOVED_FROM / MOVED_TO: Dosya taşındı
  • ACCESS: Dosyaya erişildi (okundu)
  • ATTRIB: Metadata değişti (izin, timestamp vs.)
  • OPEN: Dosya açıldı

Genellikle CLOSE_WRITE olayı, MODIFY‘dan daha güvenilir bir trigger noktası. Çünkü MODIFY bir dosyaya yazıldıkça defalarca tetiklenebilir; CLOSE_WRITE ise yazma işlemi bittikten sonra tetiklenir.

İlk Gerçek Senaryo: Nginx Config Değişince Reload Et

Production’da çalışan bir Nginx sunucunda, config dosyası değiştiğinde otomatik olarak nginx -s reload çalıştırmak istiyorsun. Bunu basit bir shell script ile yapabilirsin:

#!/bin/bash
# /usr/local/bin/nginx-config-watcher.sh

WATCH_DIR="/etc/nginx"
LOG_FILE="/var/log/nginx-config-watcher.log"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

log "Nginx config watcher başlatıldı. İzlenen dizin: $WATCH_DIR"

inotifywait -m -r -e close_write,create,delete 
    --format '%T %w %f %e' 
    --timefmt '%Y-%m-%d %H:%M:%S' 
    "$WATCH_DIR" | while read -r date time dir file event; do
    
    # Sadece .conf uzantılı dosyaları işle
    if [[ "$file" == *.conf ]]; then
        log "Değişiklik algılandı: $dir$file ($event)"
        
        # Config test et
        if nginx -t 2>/dev/null; then
            log "Config testi başarılı, reload yapılıyor..."
            nginx -s reload
            log "Nginx reload tamamlandı."
        else
            log "HATA: Config testi başarısız! Reload yapılmadı."
            nginx -t 2>&1 | tee -a "$LOG_FILE"
        fi
    fi
done

Bu scripti systemd servis olarak çalıştırmak için:

# /etc/systemd/system/nginx-config-watcher.service
[Unit]
Description=Nginx Config File Watcher
After=nginx.service

[Service]
Type=simple
ExecStart=/usr/local/bin/nginx-config-watcher.sh
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now nginx-config-watcher

Debounce Problemi ve Çözümü

Yukarıdaki basit örnekte kritik bir sorun var: bir metin editörü dosyayı kaydettiğinde birden fazla olay tetikleyebilir. Vim mesela geçici dosya oluşturur, siler, yeniden yazar. Bu durumda nginx -s reload saniyeler içinde 3-4 kez çalışabilir.

Bunu çözmek için debounce mantığı uygulaman gerekiyor:

#!/bin/bash
# Debounce'lu nginx watcher

WATCH_DIR="/etc/nginx"
DEBOUNCE_SECONDS=2
LAST_RELOAD=0

inotifywait -m -r -e close_write,create,delete 
    --format '%w%f' 
    "$WATCH_DIR" | while read -r filepath; do
    
    [[ "$filepath" != *.conf ]] && continue
    
    NOW=$(date +%s)
    DIFF=$((NOW - LAST_RELOAD))
    
    if [ "$DIFF" -ge "$DEBOUNCE_SECONDS" ]; then
        LAST_RELOAD=$NOW
        echo "[$(date)] $filepath değişti, reload tetikleniyor..."
        nginx -t && nginx -s reload
    else
        echo "[$(date)] Debounce aktif, atlanıyor: $filepath"
    fi
done

Özyinelemeli İzleme ve inotify Limitleri

-r parametresi ile özyinelemeli izleme yapabilirsin, ama burada önemli bir nokta var. Kernel’in izin verdiği maksimum inotify izleyici sayısı varsayılan olarak genellikle 8192’dir:

cat /proc/sys/fs/inotify/max_user_watches
# 8192

# Büyük dizin ağaçları için artır
echo 524288 >> /etc/sysctl.conf
sysctl -p

Büyük bir Git repository’sini ya da Node.js projelerini izlemeye çalışıyorsan node_modules klasörü tek başına bu limiti patlatabilir. Bunu -e ile olay filtresi, --exclude ile de dizin/dosya filtresi kullanarak kontrol altına alabilirsin:

inotifywait -m -r 
    --exclude '(.git|node_modules|.swp$|~$)' 
    -e close_write,create,delete 
    /var/www/myapp/

fanotify ile Daha Güçlü İzleme

fanotify doğrudan shell’den kullanmak için hazır araç sayısı inotify‘a göre daha az. fatrace en bilinen kullanıcı alanı aracı:

# Debian/Ubuntu
apt install fatrace

# Çalıştır (root gerektirir)
fatrace

fatrace çıktısı şöyle görünür:

vim(12543): W /home/user/notes.txt
bash(8821): R /etc/profile
nginx(1024): O /etc/nginx/nginx.conf

Format: process(pid): event path şeklinde. Olaylar R (read), W (write), O (open), C (close), D (directory) olarak gösteriliyor.

Belirli bir mount point’i izlemek için:

# Sadece /var/www altındaki yazma olaylarını göster
fatrace --current-mount -f W --mount /var/www

# Belirli bir process'i izle
fatrace | grep "nginx"

# Çıktıyı dosyaya yaz ve aynı zamanda ekranda göster
fatrace 2>/dev/null | tee /var/log/fatrace.log

Python ile fanotify Kullanımı

fanotify‘ın gerçek gücü C veya Python ile programatik kullanımında. Aşağıda python-inotify yerine doğrudan fanotify syscall’ını kullanan minimalist bir örnek var:

#!/usr/bin/env python3
"""
fanotify ile dosya erişim izleme
Gereksinimler: pip install cffi (veya ctypes kullanımı)
Root yetkisi gerektirir
"""

import os
import struct
import ctypes
import ctypes.util

# fanotify sabitleri
FAN_CLASS_NOTIF = 0x00000000
FAN_CLOEXEC = 0x00000001
FAN_NONBLOCK = 0x00000002

FAN_ACCESS = 0x00000001
FAN_MODIFY = 0x00000002
FAN_CLOSE_WRITE = 0x00000008
FAN_OPEN = 0x00000020

FAN_MARK_ADD = 0x00000001
FAN_MARK_MOUNT = 0x00000010

O_RDONLY = 0
AT_FDCWD = -100

libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)

def fanotify_init(flags, event_f_flags):
    return libc.syscall(300, flags, event_f_flags)  # __NR_fanotify_init

def fanotify_mark(fanotify_fd, flags, mask, dirfd, path):
    path_bytes = path.encode()
    return libc.syscall(301, fanotify_fd, flags, mask, 
                        ctypes.c_longlong(mask >> 32),
                        dirfd, path_bytes)

if os.geteuid() != 0:
    print("Bu script root yetkisi gerektirir!")
    exit(1)

fd = fanotify_init(FAN_CLASS_NOTIF | FAN_CLOEXEC, O_RDONLY)
if fd < 0:
    print(f"fanotify_init başarısız: errno={ctypes.get_errno()}")
    exit(1)

print(f"fanotify fd: {fd}, izleme başlıyor...")
# Gerçek implementasyon için python-fanotify kütüphanesi önerilir

Pratikte python-fanotify veya inotify_simple kütüphanelerini kullanmak çok daha verimli.

Gerçek Senaryo: Güvenlik Tetikleyicisi

Bir production sunucunda /etc/passwd, /etc/shadow veya /etc/sudoers dosyaları değiştiğinde anında uyarı almak kritik. İşte bunun için basit ama etkili bir script:

#!/bin/bash
# /usr/local/bin/security-file-monitor.sh
# Kritik sistem dosyalarını izle ve alert gönder

ALERT_EMAIL="[email protected]"
SLACK_WEBHOOK="https://hooks.slack.com/services/XXXX/YYYY/ZZZZ"
HOSTNAME=$(hostname -f)

CRITICAL_FILES=(
    "/etc/passwd"
    "/etc/shadow"
    "/etc/sudoers"
    "/etc/ssh/sshd_config"
    "/root/.ssh/authorized_keys"
)

send_alert() {
    local file="$1"
    local event="$2"
    local user
    user=$(who am i 2>/dev/null | awk '{print $1}' || echo "unknown")
    
    local message="[UYARI] $HOSTNAME üzerinde kritik dosya değişikliği!
Dosya: $file
Olay: $event
Zaman: $(date '+%Y-%m-%d %H:%M:%S')
Aktif kullanıcı: $user"
    
    # Mail gönder
    echo "$message" | mail -s "[GÜVENLİK UYARISI] Kritik dosya değişikliği - $HOSTNAME" "$ALERT_EMAIL"
    
    # Slack webhook (curl ile)
    curl -s -X POST "$SLACK_WEBHOOK" 
        -H 'Content-type: application/json' 
        --data "{"text":"$message"}" > /dev/null
    
    # Sistem loguna yaz
    logger -t security-monitor -p auth.warning "$message"
    
    echo "$message"
}

# inotifywait ile izleme başlat
inotifywait -m 
    --format '%w %e' 
    -e modify,attrib,close_write,moved_to,create,delete 
    "${CRITICAL_FILES[@]}" | while read -r filepath event; do
    send_alert "$filepath" "$event"
done

inotifywatch ile İstatistik Toplama

Bir sistemde hangi dosyaların en çok değiştiğini, hangi dizinlerin en aktif olduğunu bulmak için inotifywatch kullanabilirsin:

# 60 saniye boyunca /var/log altındaki olayları say
inotifywatch -r -t 60 -l 10 /var/log/

# Çıktı örneği:
# Dosya                          modify  close_write  open
# /var/log/syslog                142     71           89
# /var/log/auth.log              23      12           15
# /var/log/nginx/access.log      891     445          512

Bu bilgi, disk I/O darboğazlarını tespit etmekte veya beklenmedik dosya aktivitesini yakalamak için oldukça işe yarıyor.

Gelişmiş Kullanım: İzleme + Otomatik Yedekleme

Bir dizini izleyip değişen dosyaları otomatik olarak yedekleyen, üstelik değişiklik geçmişini tutan bir sistem:

#!/bin/bash
# Dosya değişikliği tetiklemeli artımlı yedekleme

SOURCE_DIR="/var/www/html"
BACKUP_BASE="/backup/web-incremental"
LOCK_FILE="/tmp/backup-trigger.lock"

mkdir -p "$BACKUP_BASE"

do_backup() {
    local changed_file="$1"
    
    # Lock kontrolü (paralel çalışmayı engelle)
    if [ -f "$LOCK_FILE" ]; then
        echo "Yedekleme zaten çalışıyor, atlanıyor..."
        return
    fi
    
    touch "$LOCK_FILE"
    
    TIMESTAMP=$(date '+%Y%m%d_%H%M%S')
    BACKUP_DIR="$BACKUP_BASE/$TIMESTAMP"
    
    echo "[$(date)] Tetiklendi: $changed_file -> Yedekleme: $BACKUP_DIR"
    
    # rsync ile sadece değişenleri kopyala, link-dest ile disk tasarrufu
    rsync -a --link-dest="$BACKUP_BASE/latest" 
        "$SOURCE_DIR/" "$BACKUP_DIR/" 2>/dev/null
    
    # latest symlink'i güncelle
    ln -sfn "$BACKUP_DIR" "$BACKUP_BASE/latest"
    
    # 7 günden eski yedekleri temizle
    find "$BACKUP_BASE" -maxdepth 1 -type d -mtime +7 -exec rm -rf {} + 2>/dev/null
    
    rm -f "$LOCK_FILE"
    echo "[$(date)] Yedekleme tamamlandı: $BACKUP_DIR"
}

inotifywait -m -r 
    --exclude '(.swp$|.swx$|~$|.git)' 
    -e close_write,create,delete,moved_to 
    --format '%w%f' 
    "$SOURCE_DIR" | while read -r filepath; do
    do_backup "$filepath"
done

Sistemde Kaç İzleyici Kullandığını Görmek

Monitoring kurulumların büyüdükçe inotify limitlerini takip etmek önemli:

# Mevcut kullanılan izleyici sayısını göster (process bazlı)
for pid in /proc/[0-9]*/fd; do
    count=$(ls -la "$pid" 2>/dev/null | grep -c inotify)
    if [ "$count" -gt 0 ]; then
        proc=$(cat "/proc/${pid%/fd}/comm" 2>/dev/null)
        echo "$proc (${pid%/fd#*/proc/}): $count inotify fd"
    fi
done

# Toplam kullanılan watch sayısı
cat /proc/sys/fs/inotify/max_user_watches
# Ne kadar kullanıldığını görmek için:
find /proc/*/fdinfo -name '*' -exec grep -l inotify {} ; 2>/dev/null | wc -l

Pratik İpuçları ve Yaygın Hatalar

Yıllar içinde bu araçlarla uğraşırken öğrendiğim birkaç şey:

  • NFS ve network dosya sistemleri üzerinde inotify çalışmaz. NFS mount’larını izlemek istiyorsan sunucu tarafında izleme yapman gerekiyor.
  • Symbolic link’leri inotify takip etmez. Symlink’in kendisini değil, hedef dosyayı izlemelisin.
  • CLOSE_WRITE > MODIFY: Tetikleyici scripti için neredeyse her zaman CLOSE_WRITE kullan. Editörler dosyayı yazarken MODIFY defalarca tetiklenir.
  • Çok sayıda dizin izlerken --fromfile parametresiyle izlenecek yolları dosyadan okuyabilirsin, komut satırı sınırını aşmazsın.
  • fanotify için production sistemlerde FAN_CLASS_CONTENT veya FAN_CLASS_PRE_CONTENT ile permission event kullanacaksan kernel 5.1+ gerekiyor, bunu deployment öncesi doğrula.
  • Script’lerin başına set -euo pipefail koy, beklenmedik hata durumlarında script sessizce devam etmesin.

Sonuç

inotify ve fanotify, Linux’un sağladığı en kullanışlı kernel arayüzlerinden ikisi. Basit bir shell scriptiyle config reload otomasyonu yapabilir, Python ile özel bir dosya izleme daemon’ı yazabilir ya da güvenlik monitoring altyapının temelini kurabilirsin.

inotifywait günlük sysadmin işlerinde hızlıca sonuç almanı sağlar; hafif, kurması kolay, shell script’lere entegre edilmesi basit. fanotify ise daha karmaşık gereksinimlerde, özellikle işlem kimliği takibi ve erişim engelleme istediğinde devreye giriyor.

Şunu da söyleyeyim: bu araçları production’a almadan önce mutlaka yük testlerinden geçir. Yoğun yazma trafiği olan sistemlerde inotify event queue dolabilir ve olaylar kaybedilebilir. max_queued_events parametresini (/proc/sys/fs/inotify/max_queued_events) ihtiyaca göre ayarlamayı unutma. Gerçek zamanlı dosya sistemi izleme güçlü bir araç, ama kendi olay döngüsünde bir darboğaz yaratmaması için dikkatli tasarlanması gerekiyor.

Bir yanıt yazın

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