inotifywait ve inotifywatch ile Dosya Sistemi Olaylarını Gerçek Zamanlı İzleme

Bir production sunucusunda gece yarısı neden o dizine yazıldığını anlayamamadığın bir dosya beliriyor ve sabah geldiğinde zaten silinmiş oluyor. Log’lara bakıyorsun, bir şey yok. lsof açıyorsun, iş işten geçmiş. İşte tam bu noktada inotifywait seni kurtarır. Linux çekirdeğinin inotify API’sini doğrudan terminal üzerinden kullanmanı sağlayan bu araç, dosya sistemi olaylarını gerçek zamanlı olarak yakalamanın en temiz yoludur.

inotify Nedir, Neden Önemlidir

Linux çekirdeği 2.6.13 sürümünden itibaren inotify adında bir dosya sistemi bildirim mekanizması sunuyor. Klasik dnotify‘ın aksine inotify, dizin değil doğrudan dosya bazında izleme yapabiliyor. Polling yapmıyor, yani sürekli “değişti mi, değişmedi mi?” diye sormak yerine çekirdekten event bekliyor. Bu yaklaşım hem CPU açısından son derece verimli hem de gerçek zamanlı tepki süresi sunuyor.

inotify-tools paketi bu çekirdek özelliğini iki komut satırı aracına dönüştürüyor:

  • inotifywait: Belirtilen olayı bekler, olay gerçekleşince çıkar veya sürekli izleme modunda çalışır
  • inotifywatch: Belirli bir süre boyunca olayları sayar ve istatistik üretir

Debian/Ubuntu sistemlerde kurulum:

apt-get install inotify-tools

RHEL/CentOS/Rocky Linux sistemlerde:

dnf install inotify-tools
# veya eski sistemlerde
yum install inotify-tools

inotifywait Temel Kullanım

En basit kullanımda inotifywait bir olay gerçekleşene kadar bekler ve çıkar:

inotifywait /tmp/test.txt

Bu komut /tmp/test.txt dosyasına herhangi bir erişim veya değişiklik olduğunda terminale bildiri yazıp çıkacak. Ama gerçek gücü -m (monitor) parametresinde yatıyor:

inotifywait -m /var/log/nginx/

Bu şekilde çalıştırdığında sürekli izleme moduna giriyor, her olay gerçekleştiğinde satır satır çıktı üretiyor, Ctrl+C ile durdurana kadar çalışmaya devam ediyor.

Önemli Parametreler

  • -m / –monitor: Tek olay sonrası çıkmak yerine sürekli izleme modunda çalış
  • -r / –recursive: Alt dizinleri de dahil et, dikkatli kullan çünkü inode limitleri var
  • -q / –quiet: Başlangıç mesajlarını bastır, sadece olayları göster
  • -e / –event: Hangi olayları dinleyeceğini belirt
  • –format: Çıktı formatını özelleştir
  • –timefmt: Zaman formatını belirle
  • –exclude: Regex ile bazı dosyaları izleme dışı bırak
  • –excludei: Büyük/küçük harf duyarsız exclude
  • –timeout: Saniye cinsinden timeout süresi
  • –csv: Çıktıyı CSV formatında üret

İzlenebilir Olay Tipleri

  • access: Dosya okundu
  • modify: Dosya içeriği değişti
  • attrib: Metadata değişti (izinler, timestamps vs.)
  • close_write: Yazma için açılan dosya kapandı
  • close_nowrite: Salt okunur olarak açılan dosya kapandı
  • open: Dosya açıldı
  • moved_to: Bu dizine bir dosya taşındı
  • moved_from: Bu dizinden bir dosya taşındı
  • create: Dizinde yeni dosya/alt dizin oluşturuldu
  • delete: Dosya silindi
  • delete_self: İzlenen dosyanın kendisi silindi
  • unmount: Dosya sisteminin bağlı olduğu cihaz unmount edildi

Pratik Kullanım Senaryoları

Senaryo 1: Yetkisiz Değişiklik Tespiti

Production’da /etc/passwd veya /etc/sudoers gibi kritik dosyalara kim ne zaman dokunuyor? Bunu izlemek için:

inotifywait -m -e modify,attrib,moved_to,moved_from,create,delete 
  --format '%T %w %f %e' 
  --timefmt '%Y-%m-%d %H:%M:%S' 
  /etc/passwd /etc/shadow /etc/sudoers /etc/crontab 
  >> /var/log/critical-file-watch.log 2>&1 &

Bu komutu bir systemd servisine dönüştürmek daha sağlıklı olur, ama hızlı test için arka plana almak işe yarıyor.

Senaryo 2: Deploy Sonrası Dosya Değişiklik İzleme

Uygulama deploy ettikten sonra gerçekten değişmesini beklediğin dosyaların değişip değişmediğini doğrulamak için:

inotifywait -r -m -e close_write 
  --format '%T %w%f' 
  --timefmt '%H:%M:%S' 
  /var/www/html/

close_write olayını tercih ediyorum çünkü modify çok fazla gürültü üretiyor. Büyük bir dosyayı yazarken modify defalarca tetikleniyor, oysa close_write dosya tamamen yazılıp kapatıldığında bir kez tetikleniyor.

Senaryo 3: Upload Dizinini İşleyen Script

Bir dizine dosya yüklendiğinde otomatik olarak işlem başlatmak yaygın bir ihtiyaç. Basit ama işlevsel bir örnek:

#!/bin/bash

WATCH_DIR="/data/uploads"
PROCESS_SCRIPT="/usr/local/bin/process-upload.sh"
LOG_FILE="/var/log/upload-watcher.log"

echo "$(date): Upload watcher başlatıldı, izlenen dizin: $WATCH_DIR" >> "$LOG_FILE"

inotifywait -m -r -e close_write --format '%w%f' "$WATCH_DIR" | while read FILE_PATH; do
    echo "$(date): Yeni dosya tespit edildi: $FILE_PATH" >> "$LOG_FILE"
    
    # Dosyanın gerçekten var olduğunu kontrol et
    if [ -f "$FILE_PATH" ]; then
        "$PROCESS_SCRIPT" "$FILE_PATH" >> "$LOG_FILE" 2>&1
        echo "$(date): İşlem tamamlandı: $FILE_PATH" >> "$LOG_FILE"
    fi
done

Bu pattern, klasik cron-based polling’e göre çok daha verimli. 30 saniyede bir dizini kontrol etmek yerine, dosya gerçekten geldiğinde anında haberdar oluyorsun.

Senaryo 4: Config Dosyası Değişikliğinde Servis Yeniden Başlatma

#!/bin/bash

CONFIG_FILE="/etc/nginx/nginx.conf"
CONFIG_DIR="/etc/nginx/conf.d"

inotifywait -m -e close_write,moved_to "$CONFIG_FILE" "$CONFIG_DIR" | while read DIR EVENT FILE; do
    echo "$(date): Config değişikliği tespit edildi: $DIR$FILE ($EVENT)"
    
    # Nginx config'ini test et
    if nginx -t 2>/dev/null; then
        echo "$(date): Config geçerli, nginx yeniden yükleniyor..."
        systemctl reload nginx
        echo "$(date): nginx reload başarılı"
    else
        echo "$(date): UYARI: Config hatası var, reload iptal edildi!"
        nginx -t  # Hata detayını göster
    fi
done

Bu script özellikle CI/CD pipeline’larında veya Ansible ile config deployment yapılan ortamlarda çok işe yarıyor.

inotifywatch ile İstatistik Toplama

inotifywatch anlık tepki vermek yerine belirli bir süre boyunca olayları sayar ve sona erdiğinde istatistik üretir. Hangi dosyaların en çok erişildiğini veya değiştirildiğini anlamak için idealdir.

# 60 saniye boyunca /var/log dizinini izle
inotifywatch -v -r -t 60 /var/log/

Çıktı şuna benzer bir şey üretir:

Establishing watches...
Setting up watch(es) on /var/log/
OK, /var/log/ is now being watched.
Total of 47 watches.
Finished establishing watches, now collecting statistics.
Will listen for events for 60 seconds.
total  access  modify  close_write  filename
523    180     98      245          /var/log/nginx/access.log
87     0       87      0            /var/log/syslog
34     0       34      0            /var/log/auth.log

Bu çıktıdan hangi log dosyasının en aktif olduğunu, erişim ve yazma oranlarını hemen görebiliyorsun.

inotifywatch Parametreleri

  • -v / –verbose: Başlangıç bilgilerini göster
  • -z / –zero: Sıfır sayımlı olayları da raporla
  • -r / –recursive: Alt dizinleri dahil et
  • -t / –timeout: İzleme süresi (saniye)
  • -e / –event: Hangi olayları say
  • -a / –ascending: Belirtilen sütuna göre artan sırala
  • -d / –descending: Azalan sırala

Format Özelleştirme

--format parametresi oldukça güçlü. Kullanabileceğin format değişkenleri:

  • %w: İzlenen dizinin yolu (trailing slash dahil)
  • %f: Olayı tetikleyen dosyanın adı (sadece dosya adı, tam yol değil)
  • %e: Olay tipi
  • %T: --timefmt ile belirlenen zaman formatı

Detaylı loglama için güzel bir format örneği:

inotifywait -m -r 
  -e create,delete,modify,moved_from,moved_to 
  --format '{"time":"%T","dir":"%w","file":"%f","event":"%e"}' 
  --timefmt '%Y-%m-%dT%H:%M:%S' 
  /important/directory/ | tee /var/log/fs-events.jsonl

Bu komut JSON Lines formatında çıktı üretiyor. Splunk, Elasticsearch veya herhangi bir log aggregation sistemine beslemek için idealdir. tee ile hem terminalde görüyor hem de dosyaya yazıyorsun.

Sınırlamalar ve Dikkat Edilmesi Gerekenler

inotify Watch Limitleri

Sistemdeki varsayılan inotify watch limiti genellikle düşük gelir. Özellikle -r ile büyük dizin ağaçlarını izlemeye çalışınca “inotify watch limit reached” hatası alırsın:

# Mevcut limiti kontrol et
cat /proc/sys/fs/inotify/max_user_watches

# Geçici olarak artır
echo 524288 > /proc/sys/fs/inotify/max_user_watches

# Kalıcı olarak artır
echo 'fs.inotify.max_user_watches=524288' >> /etc/sysctl.conf
sysctl -p

Her izlenen dosya/dizin bir watch harcıyor. 100.000 dosyalı bir dizini -r ile izlemeye kalkışırsan buna göre limit ayarlamalısın.

Network Dosya Sistemlerinde Çalışmaz

inotify yalnızca yerel dosya sistemleriyle çalışır. NFS, CIFS/SMB veya diğer ağ tabanlı dosya sistemlerinde inotify olayları tetiklenmez veya güvenilmez çalışır. Bu durumda alternatif olarak fswatch veya polling tabanlı çözümlere bakmak gerekir.

/proc ve /sys Dizinleri

Bu sanal dosya sistemlerinde inotify beklendiği gibi çalışmaz. Kernel internal değişiklikleri inotify üzerinden izlenemez.

Systemd Servisine Dönüştürme

Gerçek dünyada bu izleme scriptlerini manuel çalıştırmak yerine systemd servisi olarak kurmak şart. Basit bir örnek:

# /etc/systemd/system/file-watcher.service dosyasını oluştur

[Unit]
Description=Kritik Dosya Değişiklik İzleyici
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/file-watcher.sh
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
User=root

[Install]
WantedBy=multi-user.target

Ardından:

systemctl daemon-reload
systemctl enable file-watcher.service
systemctl start file-watcher.service
journalctl -u file-watcher.service -f

Restart=always ve RestartSec=5 kombinasyonu önemli. Eğer izlenen dosya silindiyse veya bir hata olduysa servis 5 saniye sonra yeniden başlayacak.

Gerçek Dünya: Zararlı Yazılım Tespiti

Bir keresinde shared hosting ortamında PHP dosyalarına izinsiz ekleme yapan bir saldırı sürecini tam olarak inotify ile yakaladım. Bütün web dizinlerini izleyecek şekilde aşağıdaki gibi bir komut çalıştırdım:

inotifywait -m -r 
  -e create,modify,moved_to 
  --format '%T %w%f %e %X' 
  --timefmt '%Y-%m-%d %H:%M:%S' 
  /var/www/ 
  --exclude '.*.(log|tmp|cache)$' 
  >> /var/log/webdir-watch.log 2>&1

--exclude ile log, tmp ve cache dosyalarını dışladım, yoksa çıktı okunaksız hale geliyor. %X format specifier’ı aslında inotifywait’te yok, burada PID gibi ek bilgi için kendi script wrapper’ını yazman gerekiyor. Ama bu komut sayesinde tam olarak hangi IP’den gelen hangi web isteğinin ardından hangi PHP dosyasının değiştiğini ilişkilendirebildim.

Web sunucusu log’larıyla timestamp karşılaştırması yaparak saldırıyı anladık ve birkaç saat içinde kapattık. inotify olmadan bu tespiti yapmak saatler değil günler sürebilirdi.

inotifywait ile Basit Bir Dosya Senkronizasyonu

rsync + inotifywait kombinasyonu, inotifywait’in en yaygın kullanım şekillerinden biri:

#!/bin/bash

SOURCE="/data/source/"
DEST="/data/backup/"
RSYNC_OPTS="-az --delete --checksum"

echo "Değişiklik izleme başlatıldı: $SOURCE"

inotifywait -m -r -e close_write,create,delete,moved_from,moved_to 
  --format '%e %w%f' 
  "$SOURCE" | while read EVENT FILE; do
    
    echo "$(date +%H:%M:%S) Değişiklik: $EVENT -> $FILE"
    
    # Burst'leri önlemek için kısa bir bekleme
    sleep 1
    
    rsync $RSYNC_OPTS "$SOURCE" "$DEST"
    echo "$(date +%H:%M:%S) Senkronizasyon tamamlandı"
done

Bu scriptin bir zayıflığı var: çok hızlı değişiklikler gelirse her biri için rsync çalışıyor. Bunu önlemek için event’leri biriktirip toplu işlemek (debouncing) gerekir. Ama basit kullanım senaryoları için fazlasıyla yeterli.

Sonuç

inotifywait ve inotifywatch, bir Linux sistem yöneticisinin toolbox’ında olmazsa olmaz araçlar. Polling’e göre çok daha verimli, geliştirmesi kolay ve son derece esnek. Güvenlik izleme, otomatik deployment tepkileri, dosya işleme pipeline’ları veya sadece “bu dosyaya ne zaman dokunuluyor” sorusuna cevap bulmak için birebir.

Öte yandan bu araçlar herşeyin çözümü değil. Büyük çaplı, kurumsal seviye dosya sistemi izleme için Auditd veya OSSEC/Wazuh gibi araçlara bakmak gerekir. inotify, kernel audit subsystem’ın aksine process bilgisi (hangi PID, hangi kullanıcı) doğrudan vermiyor. Bu eksikliği auditctl ile tamamlamak mümkün ama o ayrı bir yazının konusu.

Başlangıç noktası olarak en değerli bulduğum kombinasyon: inotifywait -m -e close_write --format '%T %w%f' --timefmt '%H:%M:%S' ve bunu bir pipe ile işleyen basit bir while döngüsü. Bu temel üzerine her şeyi inşa edebilirsin.

Bir yanıt yazın

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