Tüm sistemler çalışıyor 6 offshore bölge No-KYC ödeme
Hands-on Saha kılavuzu

VPS'teki sunucu günlükleri: tutmamaya karar verdiğiniz veri

Kutuyu offshore'a taşıdınız, Monero ile ödediniz ve her şeyi bir tünelin arkasına aldınız. Sonra nginx bir satır yazdı. Sunucunuzun yanıtladığı her istek, kimin nereden ne istediğine dair tarihli bir kayıt bırakır; standart bir imajda bu kayıt /var/log'da iki hafta, fail2ban veritabanınızda bir ay, yedekleriniz ise ne kadar saklıyorsa — ki bu genellikle ikisinden de uzundur — o kadar hayatta kalır. Bu rehber ağ yerine makineye bakar: kendi sunucunuzun ne kaydettiğine karar vermek, adresi satır oluşturulduktan sonra temizlemek yerine oluşturulmadan önce kısaltmak ve elinizde tutmak istemeyeceğiniz bir ziyaretçi geçmişi biriktirmeden bir saldırganı engellemeye ve bir 500 hatasında hata ayıklamaya yetecek kadarını saklamak.

2026-09-15 tarihinde güncellendi · 15 dk okuma · Filo operasyonları
Bu sayfada
  1. Yanıtladığınız istek, artık elinizde tuttuğunuz bir kayıttır
  2. Altı günlük, ve insanları ele veren iki tanesi
  3. Yazma anında kısaltın, rotasyonda değil
  4. Kısaltmanın bozduğu şey, ve bir günlüğün gerçekte yaptığı üç iş
  5. journald, auth.log, ve size geri götüren iz
  6. Saklama politikanız, yedeklerinizin dediği şeydir
  7. Kontrol etmediğiniz kopyalar
  8. Tasarım gereği minimizasyon, ihbar üzerine silme değil
  9. Adım adım
SP·01

Yanıtladığınız istek, artık elinizde tuttuğunuz bir kayıttır

Bu seride yer alan diğer her şey dışa bakar: kaynağı gizlemek, diski şifrelemek, yargı bölgesini seçmek, başkasının sizin hakkınızda günlük tutmasını engellemek. Bu rehber ise doğrudan makinenin kendisine bakar, çünkü istekleri yanıtlayan bir sunucu bunları kimin yaptığını kaydeder — varsayılan olarak, aynı anda birden fazla yerde, hiç kimsenin seçmediği bir saklama süresiyle.

Bir standart imajın devreye alındıktan iki hafta sonra elinde ne tuttuğuna bakın. /var/log/nginx/access.log istek başına bir satır tutar: adres, zaman damgası, path, referrer, user-agent — her gün döndürülür ve on dört gün saklanır. /var/log/auth.log her SSH oturumunu kaydeder — sizinkilerin her birinin kaynak adresi de dahil. Journal aynı olayların bir kopyasını, artı servislerinizin stderr'e yazdığı her şeyi tutar. Docker çalışıyorsa, her konteynerin kutudan çıktığı haliyle hiçbir boyut sınırı olmayan bir JSON günlük dosyası vardır. fail2ban çalışıyorsa, yasakladığı her adresin bir günlüğünü ve aynı şeyi söyleyen bir SQLite veritabanını tutar; ikisindeki adresler de eksiksizdir.

Bunların hiçbiri kötü niyetli değildir ve çoğu, bir şeyler ters gittikten sonraki yaklaşık bir saat boyunca gerçekten kullanışlıdır. Sorun biçimdedir: tam doğrulukla, uzun süre, bir karardan çok bir kaza sonucu saklanır. Bu dosyaların her biri hakkında sorulmaya değer soru "bu hassas mı?" değil, "üç hafta önce yazdığım satırla gerçekte ne yapardım?"dır. Satırların ezici çoğunluğu için, sunucuların ezici çoğunluğunda cevap hiçbir şeydir ve asla okumayacağınız bir kayıt salt bir yüktür — bir güvenlik ihlaline karşı, makineden daha uzun ömürlü bir yedeğe karşı, er ya da geç onu talep edecek herkese karşı.

Bu düzeltmenin resmî adı veri minimizasyonudur ve veri korumadaki en az tartışmalı fikirdir: iş için gerekeni toplayın, iş gerektirdiği sürece saklayın, sonra durun. Aşağıdakiler bunu, gerçekten işlettiğiniz bir kutuya uygular — devreye alma sonrası ilk saat sertleştirme geçişini zaten tamamladığınızı ve makinede korumaya değer bir şeyin bulunduğunu varsayarak.

SP·02

Altı günlük, ve insanları ele veren iki tanesi

Herhangi bir şeyi değiştirmeden önce envanteri bilin. Bir web hizmeti çalıştıran küçük bir Debian ya da Ubuntu VPS, tipik olarak altı akış yazar ve bunlar insanların beklediğinden daha fazla örtüşür — aynı olay genellikle üç farklı saklama süresine sahip üç dosyaya birden düşer.

nginx erişim günlüğü, ziyaretçilerinizi ele veren günlüktür. İstek başına bir satır: istemci adresi, tam path, referrer ve tek başına zayıf bir parmak izi oluşturacak kadar ayrıntılı bir user-agent dizesi. nginx hata günlüğü ise insanların unuttuğu günlüktür: her üst akış zaman aşımında, her 403'te, her bozuk istekte client: 203.0.113.9 gibi bir satır kaydeder — ve erişim günlüğünün aksine, biçimi sabittir ve şablonlanamaz.

auth günlüğü — Debian ailesinde /var/log/auth.log, RHEL ailesinde /var/log/securesizi ele verir. Kabul edilen her publickey satırı, kaynak adresinizi ve oturumu açan anahtarın parmak izini taşır. Bir ayını okuyan biri, hangi ağlardan yönettiğinizi, hangi saatlerde çalıştığınızı ve kaç farklı anahtara sahip olduğunuzu öğrenir. Anonim sahipli bir kutuda bu dosya, çoğu zaman ziyaretçilerinizin ürettiği her şeyden daha fazla şey ele verir.

Journal, yukarıdakilerin çoğunun bir kopyasını, artı her birimin stdout ve stderr çıktısını tutar; varsayılan bir kurulumda, en eski kayıtları silmeye başlamadan önce dosya sisteminin %10'una, en fazla 4 GB'a kadar büyümesine izin verilir. 40 GB ya da daha büyük her diskte bu tavan tam 4 GB'tır, bu da küçük bir sunucunun ürettiği hacimde aylarca demektir.

Uygulama günlükleri joker karttır. Hata ayıklama modundaki bir framework, sorgu dizeleri dahil tam URL'leri günlüğe kaydeder ve sorgu dizeleri genellikle oturum token'larını, parola sıfırlama bağlantılarını ve arama terimlerini taşır. PHP-FPM, nginx ayarlarınızın hiç dokunmadığı bir dosyadan, nginx'in zaten yazdığı istek satırını tekrarlayan kendi erişim günlüğünü yazacak şekilde yapılandırılabilir.

Konteyner günlükleri ise sessiz olandır. Docker'ın varsayılan json-file sürücüsü, siz yapılandırmadıkça hiç rotasyona girmez; bu yüzden /var/lib/docker/containers/*/*-json.log, konteynerin oluşturulduğundan beri söylediği her şeyi tutar. "14 günlük" bir saklama politikasının aslında on bir ayı elinde tuttuğunu keşfetmenin yaygın bir yoludur bu — ve Docker'ın ufw'nin arkasına yazdığı güvenlik duvarı kurallarıyla aynı sürpriz sınıfındandır.

Altısından ikisi, bilerek en aza indirilmeye değer, insanlara ait tanımlayıcılar taşır: nginx erişim günlüğü (ziyaretçileriniz) ve auth günlüğü (siz). Geri kalanların çoğu yalnızca bir boyut sınırına ve daha kısa bir saate ihtiyaç duyar.

SP·03

Yazma anında kısaltın, rotasyonda değil

İçgüdüsel eğilim, normal şekilde günlük tutmaya devam edip temizliği sonraya bırakmaktır — dünün dosyasını adresler silinmiş halde yeniden yazan, her gece çalışan bir cron. Bunu kurmayın. Bir temizleme işi, ham adreslerin bir güne kadar diskte gerçekten var olduğu anlamına gelir; o gün boyunca yedekleme çalıştırmanız tarafından anlık görüntüsü alınmış, belki barındırma sağlayıcınızın blok düzeyindeki anlık görüntüsü tarafından kopyalanmış ve dosya sisteminin eski bloklara ne yaptıysa onun içinde bırakılmış olurlar. Daha kötüsü, bu iş hareketli bir parçadır: disk dolduğu hafta sessizce başarısız olur ve dünün dosyasının hâlâ eksiksiz olduğunu size hiçbir şey söylemez.

Gerçekten güvenebileceğiniz tek azaltma, satır yazılmadan önce gerçekleşendir. nginx'te bu, günlük anında değerlendirilen ve adresi yeni bir değişkene yeniden yazan bir map bloğudur; ardından gelen log_format, $remote_addr yerine o yeni değişkeni kullanır. Tam adres hiçbir zaman diske yazılmaz. Sonradan temizlenecek hiçbir şey, zamanlanacak hiçbir şey ve izlemediğiniz gün yanlış gidecek hiçbir şey yoktur.

# /etc/nginx/conf.d/00-privacy-log.conf  --  http context, loaded before the sites
map $remote_addr $ip_trunc {
    # IPv4: keep the /24, zero the host part
    ~(?<v4>\d+\.\d+\.\d+)\.     "${v4}.0";
    # IPv6: keep the first two groups, drop the rest
    ~(?<v6>[^:]+:[^:]+):          "${v6}::";
    # anything the two patterns cannot parse -- including compressed forms
    # like ::1 -- falls through here, i.e. fails closed rather than open
    default                       "0.0.0.0";
}

log_format privacy '$ip_trunc - - [$time_local] "$request" $status '
                   '$body_bytes_sent "$http_referer" "$http_user_agent" '
                   'rid=$request_id rt=$request_time';

Üç ayrıntı bunun gerçekten işleyip işlemeyeceğini belirler. Birincisi, map, http bağlamında yaşamalıdır — bir server bloğunun içinde nginx başlamayı reddeder. Bunu, alfabetik sırada erken gelen bir adla conf.d/ dizinine bırakmak, emin olmanın en basit yoludur.

İkincisi, Cloudflare'ın ya da başka bir uç katmanın arkasındaysanız, $remote_addr'ın ne içerdiğini kontrol edin. realip modülü, istek işlemenin erken bir aşamasında onu gerçek istemci adresiyle değiştirir ve bağlanan adresi $realip_remote_addr'da tutar. Bu sıralama sizin lehinizedir: map günlük anında çalışır, dolayısıyla uç katmanı değil gerçek ziyaretçiyi kısaltır. Ama bu aynı zamanda şu anlama gelir: realip yapılandırılmış değilse, Cloudflare'ın adresini kısaltıp hiçbir şey öğrenmiyor olursunuz — gerçek adres ise bir başlıkta oturur.

Üçüncüsü, format dizesinin geri kalanını denetleyin. $remote_addr'ı kısaltmak, satır hâlâ "$http_x_forwarded_for" ile bitiyorsa ya da $http_cf_connecting_ip taşıyorsa tam olarak hiçbir işe yaramaz — ve pek çok standart ve kontrol paneli formatı bunlardan birini içerir. Tam adres, istek başlıklarındadır; yalnızca onu taşıyan her başlığı formatın dışında bırakırsanız dosyanın dışında kalır.

Onun yerine geleni not edin: nginx'in istek başına ürettiği rastgele, 32 karakterlik bir hex dize olan $request_id. Bir sonraki bölümün ekseni de budur.

SP·04

Kısaltmanın bozduğu şey, ve bir günlüğün gerçekte yaptığı üç iş

Birileri her zaman anonimleştirilmiş günlüklerin işe yaramaz olduğuna itiraz eder ve yarı haklıdırlar — çünkü "günlükler" tek bir dosya adını giyen üç ilgisiz iştir ve bunlardan yalnızca biri adrese ihtiyaç duyar.

Birinci iş: şu anda sizi bombalayan kim varsa engellemek. Bu, tam adrese ihtiyaç duyar ve buna saniyeler içinde ihtiyaç duyar. Yarın ihtiyaç duymaz. fail2ban bunun olağan aracıdır ve kısaltılmış bir dosyayla gerçekten çalışamaz — 203.0.113.0'ı engellemek masum bir host'u engeller ve saldırganı bağlı bırakır. Ama bu iş, bir gün var olan bir dosyayla ya da hiç dosya kullanmayarak karşılanır: nginx'in kendi limit_req ve limit_conn'u durumlarını paylaşımlı bellekte tutar, fail2ban'ın yoklama aralığı yerine mikrosaniyeler içinde işlem yapar ve diske hiçbir şey yazmaz.

İkinci iş: o isteğin neden 500 döndürdüğünü çözmek. Bunun için gereken kimlik değil, ilişkilendirmedir. nginx'ten uygulamaya kadar taşınan bir istek kimliği, erişim satırını, üst akış hatasını ve uygulamanın stack trace'ini tek bir olayla ilişkilendirir — ki adrese uzandığınızda gerçekte yapmaya çalıştığınız şey de zaten budur. Pratikte bu kimlik daha iyidir: oturum ortasında adresi değişen mobil ağdaki bir istemciye rağmen geçerliliğini korur ve dört ziyaretçi tek bir CGNAT adresini paylaştığında bayatlamaz.

Üçüncü iş: trafiği zaman içinde anlamak. Hacim, durum kodu dağılımı, hangi path'lerin yoğun olduğu, crawler'ın kontrolden çıkıp çıkmadığı. Kısaltılmış bir adres burada gayet yeterlidir ve kısaltmadan sağ çıkan /24, bir ağın isteklerinizin %40'ından sorumlu olduğunu görmeye yeter.

Yani tasarım "daha az günlük tut" değil, saate göre ayırdır: bir gün yaşayan ve engelleme araçlarını besleyen tam doğruluklu bir akış, bir de istatistik istediğiniz kadar yaşayan kısaltılmış bir akış. İkisi de nginx tarafından aynı anda yazılır, dolayısıyla aralarında bir işleme adımı ve yanlış şeyin niyetlenenden daha uzun süre diskte kalacağı bir pencere yoktur.

# inside the server block, or in a snippet included by it
access_log  /var/log/nginx/access.log       privacy;   # truncated, keep for weeks
access_log  /var/log/nginx/security.log     secip;     # full address, keep for a day

# static assets are pure noise in a privacy log -- drop them entirely
location ~* \.(?:css|js|svg|png|jpe?g|webp|woff2?|ico)$ {
    access_log off;
    expires 30d;
}

secip formatı, diğerinin yanında tek satırlık bir şeydir — $remote_addr, zaman damgası, istek ve durum, başka hiçbir şey yok. Kutu üzerinde eksiksiz bir ziyaretçi adresinin oturmasına izin verilen tek dosyadır bu, bu da onun saklama süresini altı dosya boyunca akıl yürütmeniz gereken bir özellik değil, tek bir yerde alınan tek bir karar hâline getirir.

SP·05

journald, auth.log, ve size geri götüren iz

Ziyaretçi gizliliği, hakkında çok yazılan kısımdır. Yöneticinin izi ise, bütün amacı adınızın ona bağlı olmaması olan bir kutuda asıl önemli olan kısımdır ve neredeyse tamamen iki yerdedir.

/var/log/auth.log, açtığınız her oturum için kaynak adresiniz ve anahtar parmak izinizle birlikte bir Accepted publickey satırı kaydeder. Bir ay boyunca bu, çalışma alışkanlıklarınızın bir çizelgesi ve kullandığınız ağların bir listesidir. Her zaman aynı tünel üzerinden bağlanıyorsanız, bu tekrar eden tek bir adrestir ve nispeten sıkıcıdır. Nerede bulunursanız oradan bağlanıyorsanız, bu bir seyahat geçmişidir.

Journal aynı olayları, artı birimlerinizin yazdırdığı her şeyi tutar ve varsayılanları cömerttir: SystemMaxUse= dosya sisteminin %10'udur ve MaxRetentionSec= ayarlanmamıştır — bu da hiçbir zaman sınırı olmadığı, yalnızca bir boyut sınırı olduğu anlamına gelir. Sakin bir sunucuda bu kombinasyon ayları saklar.

Burada gerçek bir takas var ve bunun elin sallanmasıyla değil, açıkça söylenmesi gerekir. Sizi tarif eden günlükler, birinin nasıl içeri girdiğini de size söyleyen günlüklerle aynısıdır. Storage=volatile ayarlayın, journal yalnızca RAM'de yaşar ve yeniden başlatmada kaybolur — gerçekten özel, ve başlatmadığınız bir süreci bulduğunuz sabah gerçekten işe yaramaz, çünkü saldırganınızın ilk yeniden başlatması kanıtı silmiştir. Çoğu insan için makul orta yol, sert bir üst sınıra ve kısa bir saate sahip kalıcı depolamadır: bir hafta içinde fark ettiğiniz bir olayı araştırmaya yetecek kadar uzun, dosyanın bir günlük defteri olmayacağı kadar kısa.

İnsanları yakalayan iki uygulama notu. Debian ve Ubuntu imajları, rsyslog'un kurulu olup olmadığı konusunda farklılık gösterir; kutunuzda /var/log/auth.log varsa rsyslog onu yazıyordur ve journald'ın sınırları o dosyayı hiç yönetmez — bu logrotate'in işidir. Ve ForwardToSyslog=, journal'dan rsyslog'u besleyen ayardır; dolayısıyla ikisine de sahip bir kutuda bunu kapatmak, her şeyin iki farklı saklama politikası altında iki kopyasını tutmanızı önler.

SP·06

Saklama politikanız, yedeklerinizin dediği şeydir

Bu, yukarıdaki tüm özenli çalışmayı boşa çıkaran şeydir ve aramadıkça görünmezdir.

Diyelim ki logrotate nginx günlüklerini on dört gün saklıyor ve bundan memnunsunuz. Şimdi kurmakta haklı olduğunuz uzak konum yedeğini ekleyin: günlük bir Borg ya da restic çalıştırması, yedi günlük, dört haftalık ve altı aylık arşivlerden oluşan bir saklama süresiyle. Bu arşivlerin her biri, çalıştığı gün /var/log'un hâlini içerir. En eski aylık arşiv altı aylıktır ve o zaman güncel olan on dört günlük içeriği barındırır. Etkin günlük saklama süreniz on dört gün değildir. Geri yüklemeden grep bile atamayacağınız, ikinci bir makinede, başka bir ülkede, şifreli bir depoda altı aydır.

Tam olarak iki dürüst çözüm vardır. Günlük dizinlerini yedekten hariç tutun — bunlar bir sunucuda neredeyse her zaman yeniden kurabileceğiniz ya da olmadan yapabileceğiniz tek şeydir ve /var/log'u atlayan bir geri yükleme daha kötü bir geri yükleme değildir. Ya da onları bilerek dahil edin ve gerçek saklama sürenizin deponunki olduğunu kabul edin — bu durumda yayımladığınız politikada bunu belirtin, çünkü alternatifi kendi altyapınızın çelişeceği beyan edilmiş bir politikadır.

# exclude logs from the backup, and prove it took
borg create --stats                       \
    --exclude '/var/log'                  \
    --exclude '/var/lib/docker/containers' \
    ::'{hostname}-{now:%Y-%m-%d}' /

# the check that matters: can you still find an address in the newest archive?
borg list ::"$(borg list --short --last 1)" | grep -c '^var/log/' || echo "clean"

Bu zihniyetteyken, aynı sorunun iki komşusu daha. Barındırma sağlayıcınızın anlık görüntüleri sizin değildir. Hipervizör düzeyindeki bir anlık görüntü, o zamandan beri rotasyonla sildiğiniz günlükler dahil, diski olduğu hâliyle yakalar ve sağlayıcının deposunda, sağlayıcının saklama süresine tabi olarak yaşar — bu da sonradan akıllıca bir şey yapmanın değil, adreslerin baştan hiç tam olarak yazılmamış olması gerektiğinin bir sebebi daha.

Ve bir VPS'te shred'e başvurmayın. Thin-provisioned bir sanal disk üzerinde, copy-on-write bir dosya sistemi üzerinde, aşınma dengelemesi için blokları yeniden eşleyen bir SSD'nin üstünde bir dosyanın üzerine yazmak, onu tutan fiziksel hücrelerin üzerine güvenilir şekilde yazmaz. Kiralık, sanallaştırılmış depolamada güvenli silme bir tiyatrodur. İşe yarayan tek azaltma, yazma anında yaptığınızdır; ondan sonraki her şey doğrulayamayacağınız bir en iyi çabadır. Birimi şifrelemek bu denklemi değiştirir — ama onu veri yazılmadan önce değiştirir, ki bu yine aynı derstir.

SP·07

Kontrol etmediğiniz kopyalar

Kendi kutunuzdaki minimizasyon, birkaç katmandan yalnızca biridir ve diğerleri konusunda net olmak, bunun yanlış bir eksiksizlik hissine dönüşmesini engelleyen şeydir.

Barındırma ağınız akış kaydını görür. Kaynak, hedef, portlar, bayt sayısı, zamanlama — siz bir şey günlüğe kaydetseniz de kaydetmeseniz de, makineye giren ve çıkan her bağlantı için. Sunucudaki hiçbir yapılandırma bunu değiştirmez. Kutunun bulunduğu yargı bölgesinin, pazarlama değil gerçek bir değişken olmasının büyük bir kısmı da budur; ağdan yasal olarak ne istendiği ülkeye göre muazzam ölçüde değişir.

CDN'iniz ya da uç katmanınız, günlüğü kendi ucunda tutar. Cloudflare sizin için TLS'i sonlandırıyorsa, sunucunuz devreye girmeden önce istek satırına ve istemci adresine sahiptir — kendi saklama takvimine göre, kendi hukuki sürecine tabi olarak. Origin günlüğünüzü kısaltmak, bunun gerisine geçip bir şey değiştirmez. Sahibi olduğunuz bir ters vekil sunucu çalıştırmak, bunun gerçekten yapılandırabileceğiniz versiyonudur — ve bu rehberdeki günlükleme kuralları önce o uç kutuya uygulanır, çünkü kısaltılmamış adreslerin vardığı yer orasıdır.

Hata izleyicileri ve analitik araçlar, onu sizin için kutudan dışarı taşır. Sentry ve emsallerinin çoğu, varsayılan olarak istemci IP'sini her olaya iliştirir; ayar genellikle send_default_pii gibi bir şey olarak adlandırılır ve varsaymak yerine kontrol etmeye değer. Herhangi bir barındırılan analitik, yapısı gereği, az önce bir öğleden sonranızı harcayarak kısalttığınız erişim günlüğünün üçüncü taraf bir kopyasıdır.

E-posta ise hepsinden daha sızdırandır. Kutudaki herhangi bir şey e-posta gönderiyorsa, başlıklar gönderen host'u ve adresi taşır ve yoldaki her aktarım noktası, zarfın bir kopyasını zaman damgalarıyla tutar. Size e-posta gönderen bir iletişim formu, sizin yönetmediğiniz bir günlüktür.

Bunların hiçbiri yerel çalışmayı anlamsız kılmaz — yerel kopya, makineyle birlikte el konulan, bir ihlalde dışarı sızdırılan ya da sizin tarafınızdan teslim edilen kopyadır. O, yalnızca tamamen kontrol ettiğiniz tek katmandır ve onu resmin tamamı sanmak asıl hatadır.

SP·08

Tasarım gereği minimizasyon, ihbar üzerine silme değil

Burada net olmaya değer, çünkü ikisi birbirine karıştırılıyor ve aradaki fark, standart mühendislik pratiği ile yapmamanız gereken bir şey arasındaki farkın ta kendisi.

Hizmetinizin neyi topladığına ve bunu ne kadar süre tuttuğuna önceden karar vermek, sıradan, belgelenmiş, teşvik edilen bir pratiktir. GDPR kapsamında bu, temel ilkelerden ikisidir — veri minimizasyonu ve saklamanın sınırlandırılması — ve daha kısa bir günlük saklama süresi, denetçilerin itiraz ettiği değil, istediği bir kontroldür. AB'de bir web sitesi operatörünün ya da bir barındırma müşterisinin trafik günlüklerini saklama konusunda genel bir yükümlülüğü yoktur; bir zamanlar aksini ima eden toptan saklama direktifi, 2014'te Adalet Divanı tarafından iptal edildi ve ondan sağ çıkan ulusal yasalar çoğunlukla web sunucusu işletenleri değil, elektronik haberleşme sağlayıcılarını bağlar. Amerika Birleşik Devletleri'nin de site operatörleri için genel bir saklama zorunluluğu yoktur.

Belirli kayıtları, onlar hakkında resmî olarak bilgilendirildikten sonra yok etmek ise tamamen farklı bir eylemdir. Bir muhafaza talebi, bir dava kapsamında saklama emri, bir mahkeme kararı ya da bir polis soruşturması, o an var olan veriyle ne yapabileceğinizi değiştirir ve ihbar yüzünden silmeyi hızlandırdıysanız "saklama politikam onu sildi" bir savunma oluşturmaz. Bu rehberdeki hiçbir şey bununla ilgili değildir. Bir saklama politikası, sakin bir salı günü belirleyip sonra kendi hâline bıraktığınız bir şeydir; yalnızca bir şey olduğunda kısaltılıyorsa, o zaten bir politika değildi.

Aynı ayrım bu sitenin geri kalanında da geçerlidir: gizlilik mühendisliği ile kendini dokunulmazlık olarak pazarlayan "kurşun geçirmez" duruş arasında gerçek bir çizgi vardır. Asla bir ziyaretçi geçmişi biriktirmeyen bir hizmet tasarlamak, diskinizi şifrelemek ve kullanıcılardan ihtiyacınız olmayan bir e-posta adresi istememekle aynı kutuda, bu çizginin rahatça doğru tarafında durur.

İki pratik sonuç. Bir gizlilik politikası yayımlıyorsanız, saklama rakamlarının diskte gerçekte olanla eşleştiğinden emin olun — beyan edilmiş on dört gün ve bir yedek deposundaki gerçek altı ay, iyi niyetli bir operatörü kağıt üzerinde kötü niyetli birine dönüştüren türden bir boşluktur. Ve kararı, başka hiçbir yerde olmasa bile logrotate dosyasının en üstünde bir yorum satırı olarak kendiniz için yazın, çünkü on sekiz ay sonra bu rakamları savunmak zorunda kalacak kişi sizsiniz ve sayının neden yedi olduğunu hatırlamayacaksınız. Yukarıdakilerin hiçbiri hukuki tavsiye değildir; sektöre özgü saklama yükümlülükleri olan bir yerde faaliyet gösteriyorsanız, herhangi bir şeyi kısaltmadan önce bunları kendi durumunuzla karşılaştırın.

SP·09

Adım adım

  1. 01

    Kutunun şu anda neyi tuttuğunu öğrenin

    Ölçmeden hiçbir şeyi yapılandırmayın. Bu geçişin amacı, unuttuğunuz dosyayı bulmaktır — bu, çoğu makinede ya bir konteyner günlüğü ya da devreye almadan beri kimsenin bakmadığı bir uygulama günlüğüdür.

    # biggest log files anywhere on the box, largest last
    sudo du -ah /var/log /var/lib/docker/containers 2>/dev/null \
      | sort -h | tail -20
    
    # how much disk the journal holds, and how far back it goes
    journalctl --disk-usage
    journalctl --output=short-iso | head -1
    
    # how old is the oldest nginx line still on disk?
    zcat -f /var/log/nginx/access.log* 2>/dev/null | head -1

    Sonra her dosyadan bir satıra bakın ve bunun neyi tanımladığını sorun. Aşağıdaki komut, web günlüklerinizden şu anda kaç farklı tam adresin elde edilebildiğini sayar — bu genellikle rehberin geri kalanının gerekçesini oluşturan sayıdır.

    zcat -f /var/log/nginx/access.log* 2>/dev/null \
      | grep -oE '^([0-9]{1,3}\.){3}[0-9]{1,3}' | sort -u | wc -l

    Bulduklarınızı not edin. Değişikliğin etkili olduğunu kanıtlamak için aynı komutları sonunda tekrar çalıştıracaksınız.

  2. 02

    nginx satırı yazmadan önce istemci adresini kısaltın

    map'i ve iki formatı, http bağlamına yüklenen bir dosyada oluşturun. Debian ve Ubuntu'da /etc/nginx/conf.d/, site yapılandırmalarından önce nginx.conf'tan dahil edilir — tam olarak buranın ait olduğu yer de budur.

    sudo tee /etc/nginx/conf.d/00-privacy-log.conf > /dev/null <<'EOF'
    # The client address is reduced here, at log time, and never written in full
    # to the long-retention file. $realip_remote_addr still holds the connecting
    # address if you need it while debugging a proxy problem.
    map $remote_addr $ip_trunc {
        ~(?<v4>\d+\.\d+\.\d+)\.     "${v4}.0";
        ~(?<v6>[^:]+:[^:]+):          "${v6}::";
        # anything the two patterns cannot parse -- including compressed forms
        # like ::1 -- falls through here, i.e. fails closed rather than open
        default                       "0.0.0.0";
    }
    
    # Long retention: no full address, no forwarded-for header, request id instead.
    log_format privacy '$ip_trunc - - [$time_local] "$request" $status '
                       '$body_bytes_sent "$http_referer" "$http_user_agent" '
                       'rid=$request_id rt=$request_time';
    
    # Short retention: the one file allowed to hold a complete address.
    log_format secip   '$remote_addr [$time_local] "$request" $status';
    EOF
    
    sudo nginx -t && sudo systemctl reload nginx

    Şimdi siteyi bunlara yönlendirin. Server bloğunuzda, mevcut access_log satırını bu ikiliyle değiştirin ve oradayken statik varlıklar için günlüklemeyi kapatın.

    access_log  /var/log/nginx/access.log    privacy;
    access_log  /var/log/nginx/security.log  secip;

    Yeniden yükleyin, bir sayfa açın ve sonucu okuyun. İlk alan .0 ile bitmeli ve satır bir rid= değeri taşımalıdır.

    sudo nginx -t && sudo systemctl reload nginx
    curl -s -o /dev/null https://your-domain.example/
    sudo tail -1 /var/log/nginx/access.log

    Adres hâlâ eksiksizse, server bloğu formatı daha aşağıda bir yerde geçersiz kılıyordur — kazanan satırı grep -rn access_log /etc/nginx/ bulur.

  3. 03

    Tam adresleri yalnızca bir şeyin onlara göre işlem yaptığı yerde tutun

    fail2ban çalışıyorsa, şu anda az önce kısalttığınız dosyayı okuyor demektir. Onu bunun yerine güvenlik günlüğüne yönlendirin ve tam adreslerin kendiliğinden süresinin dolması için o günlüğe bir günlük bir ömür verin.

    sudo tee /etc/fail2ban/jail.d/nginx-privacy.local > /dev/null <<'EOF'
    [nginx-http-auth]
    enabled  = true
    logpath  = /var/log/nginx/error.log
    
    [nginx-botsearch]
    enabled  = true
    logpath  = /var/log/nginx/security.log
    maxretry = 6
    findtime = 10m
    bantime  = 1h
    EOF
    
    sudo fail2ban-client reload

    Sonra çoğu insanın hiç dokunmadığı fail2ban'ın kendi belleğiyle ilgilenin: varsayılan rotate 4 weekly altında kendi engelleme günlüğünü ve verdiği her engellemenin bir SQLite veritabanını tutar. dbpurgeage, o veritabanından satırların süresini dolduran ayardır — varsayılanda bırakmak yerine, onu en uzun engellemenize yakın bir değere ayarlayın.

    sudo tee /etc/fail2ban/fail2ban.d/retention.local > /dev/null <<'EOF'
    [Definition]
    dbpurgeage = 2d
    loglevel   = NOTICE
    EOF
    
    sudo systemctl restart fail2ban

    Daha da iyisi, sıradan sel saldırılarında bu işi, hiçbir şeyin hiç yazılmadığı nginx içinde yapın. limit_req, sayaçlarını paylaşımlı bellekte tutar, bir yoklama aralığı yerine mikrosaniyeler içinde yanıt verir ve kimin kısıtlandığına dair hiçbir kayıt bırakmaz — bölgeleri gerçek yük altında boyutlandırmak için ilk saat DDoS çalışma kılavuzuna bakın.

    # http context: keyed on the truncated address, so nothing complete is
    # held in memory either. 10m of shared state is plenty for a small site.
    limit_req_zone $ip_trunc zone=perip:10m rate=20r/s;
    
    # in the location you want protected
    limit_req zone=perip burst=40 nodelay;

    Bölgeyi $ip_trunc üzerinden, $binary_remote_addr yerine anahtarlamak kasıtlı bir takastır: sınır artık bir kerede tüm bir /24'e uygulanır, dolayısıyla bir bloğun arkasındaki yoğun bir ofis bir bütçeyi paylaşır. Küçük bir site için bu genellikle sorun değildir, hatta zaman zaman bir iyileştirmedir; değilse, bölgeyi tam adres üzerinden anahtarlayın — bir hız sınırlayıcının durumu paylaşımlı bellekte yaşar ve asla diske yazılmaz, dolayısıyla burada en aza indirdiğiniz şeyin bir parçası değildir.

  4. 04

    Journal'ı sınırlayın ve ikinci kopyayı durdurun

    journald bir drop-in kabul eder ve bu, journald.conf'u düzenlemenin sağlayamadığı bir şekilde paket yükseltmelerinden sağ çıkar. Aşağıdaki değerler, sert bir 200 MB tavanın içinde yaklaşık bir hafta tutar — cuma günü başlayıp pazartesi fark ettiğiniz bir şeyi araştırmaya yetecek kadar.

    sudo mkdir -p /etc/systemd/journald.conf.d
    sudo tee /etc/systemd/journald.conf.d/retention.conf > /dev/null <<'EOF'
    [Journal]
    Storage=persistent
    SystemMaxUse=200M
    SystemMaxFileSize=20M
    MaxRetentionSec=7day
    MaxFileSec=1day
    ForwardToSyslog=no
    EOF
    
    sudo systemctl restart systemd-journald
    journalctl --disk-usage

    Yeniden başlatma, boyut sınırlarını hemen uygular; saklama saati ise yeni dosyalar döndürüldükçe işletilir, dolayısıyla mevcut aşırı büyük bir journal anında değil bir sonraki rotasyonda küçülür. Diski bugün geri istiyorsanız, journalctl --vacuum-time=7d bunu şimdi zorlar.

    ForwardToSyslog=no, rsyslog ile gelen her imajda önemlidir: bu olmadan, her journal girişi journald'ınki yerine logrotate'in takvimine tabi olarak /var/log/syslog'a da eklenir ve iki farklı sona erme tarihine sahip iki kopyanız olur. Varsaymadan önce hangi durumda olduğunuzu kontrol edin:

    systemctl is-active rsyslog 2>/dev/null || echo "rsyslog not running"
    ls -la /var/log/auth.log /var/log/syslog 2>/dev/null

    Bu dosyalar varsa, onlara rsyslog sahiptir ve saklama sürelerinin ayarlandığı yer altıncı adımdır. Yoksa, journal tek kopyadır ve onu az önce sınırlamış oldunuz.

  5. 05

    Uygulamanın kaldırdığınız şeyi yeniden günlüğe kaydetmesini durdurun

    Yukarıdakilerin hiçbiri uygulamanıza dokunmaz ve yanlış moddaki bir uygulama, tam adresi, tam URL'yi ve oturum token'ını seve seve kendi dosyasına yazar. Kontrol edilecek üç şey.

    PHP-FPM, pool yapılandırmasında varsayılan olarak yorum satırı yapılmış ama pek çok kontrol paneli tarafından etkinleştirilen bir access.log direktifi barındırır. Açıksa, nginx çalışmanızın hiç dokunmadığı bir dosyadan gelen, her istek satırının ikinci bir kopyasıdır bu.

    grep -rn '^access.log' /etc/php/*/fpm/pool.d/ || echo "fpm access log off"

    Framework'ünüzün günlük seviyesi, sorgu dizelerinin ve istek gövdelerinin diske düşüp düşmeyeceğine karar verir. Çoğu framework'te hata ayıklama modu tam URL'yi günlüğe kaydeder ve bir parola sıfırlama bağlantısı tam bir URL'dir. Üretim seviyesini ayarlayın ve okunduğunu sandığınız dosyadaki değil, gerçekten yüklenenin bu olduğunu doğrulayın.

    Docker'ın varsayılan sürücüsü asla rotasyona girmez. Bunu daemon düzeyinde düzeltin, böylece gelecekteki her konteyner bu sınırı devralır. Bunun yeniden başlatmadan sonra oluşturulan konteynerlere uygulandığını unutmayın — mevcut olanlar, yeniden oluşturulana kadar şu anki sınırsız dosyalarını korur.

    sudo tee /etc/docker/daemon.json > /dev/null <<'EOF'
    {
      "log-driver": "json-file",
      "log-opts": { "max-size": "10m", "max-file": "3" }
    }
    EOF
    
    sudo systemctl restart docker
    docker inspect --format '{{.HostConfig.LogConfig}}' $(docker ps -q) 2>/dev/null

    Bir konteyner zaten büyük bir dosya tutuyorsa, geçmişi gerçekten kısaltan şey onu docker compose up -d --force-recreate ile yeniden oluşturmaktır — yalnızca yeniden başlatmak aynı günlük dosyasını korur.

  6. 06

    Saklama süresini bilerek, dosya başına tek bir yerde belirleyin

    rsyslog ve nginx'in yazdığı her şey için saatin yaşadığı yer logrotate'tir. İkinci bir bölüm eklemek yerine gelen bölümü düzenleyin: aynı path'i adlandıran iki bölüm, logrotate'in yinelenen-girdi hatasıyla başarısız olmasına ve o dosyayı rotasyona sokmayı tamamen bırakmasına neden olur — bu da bir saklama süresi değişikliğinin sessizce sonsuza dönüşmesinin en yaygın yoludur.

    # check for duplicates BEFORE editing, then again after
    sudo logrotate --debug /etc/logrotate.conf 2>&1 | grep -i 'duplicate\|error'

    nginx için iki dosya farklı saatler ister: kısaltılmış olan haftalarca yaşayabilir, tam adresleri tutan ise günü aşmamalıdır. Güvenlik günlüğü için ayrı bir bölüm ekleyin — farklı bir path, dolayısıyla yineleme olmaz — ve gelen bölümü kısaltın.

    sudo tee /etc/logrotate.d/nginx-security > /dev/null <<'EOF'
    # The only file on this box that holds complete client addresses.
    # One day, uncompressed so fail2ban can read it. Do not lengthen without
    # a reason you would be happy to write down here.
    /var/log/nginx/security.log {
        daily
        rotate 1
        maxage 1
        missingok
        notifempty
        nocompress
        create 0640 www-data adm
        sharedscripts
        postrotate
            [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
        endscript
    }
    EOF
    
    sudo sed -i 's/^\trotate 14$/\trotate 7/' /etc/logrotate.d/nginx
    sudo logrotate --debug /etc/logrotate.d/nginx-security

    rsyslog kuruluysa aynı hesabı /etc/logrotate.d/rsyslog için de yapın — varsayılanları dört hafta boyunca auth.log'u tutar, ki bu da dört hafta boyunca kendi SSH oturumlarınız demektir. Ve hata ayıklama geçişini bir son kez çalıştırın: bu, tam olarak hangi dosyaları döndüreceğini ve sileceğini yazdırır — az önce yazdığınız sayıların yürürlükteki sayılar olduğunu doğrulamanın tek yolu budur.

  7. 07

    Bunu kanıtlayın — yedek dahil

    Birinci adımdaki ölçümleri tekrar çalıştırın. Uzun saklama süreli günlükteki farklı tam adres sayısı artık artmayı durdurmalı ve bir rotasyon döngüsünden sonra sıfır olmalıdır.

    # should print 0 once the pre-change files have rotated out
    zcat -f /var/log/nginx/access.log* 2>/dev/null \
      | grep -oE '^([0-9]{1,3}\.){3}[0-9]{1,3}' \
      | grep -v '\.0$' | sort -u | wc -l
    
    # and the journal should now be bounded
    journalctl --disk-usage

    Sonra neredeyse kimsenin yapmadığı kontrol: yedeğin içine bakın. Hâlâ /var/log'u taşıyan bir depo, az önce bir öğleden sonranızı harcayarak kaldırdığınız doğruluğu ve saklama süresini tutmaya devam ediyordur — ve en eski arşiviniz hayatta kaldığı sürece de tutmaya devam edecektir.

    borg list | head -3          # borg lists oldest first
    borg list ::"$(borg list --short | head -1)" 2>/dev/null \
      | grep -c '^var/log/' || echo "no logs in oldest archive"

    Sayı sıfır değilse, ya daha önceki hariç tutmayı ekleyip eski arşivlerin kendiliğinden düşmesini bekleyin ya da onları bilerek budayın. Bunlardan biri gerçekleşene kadar, gerçek saklama süreniz deponunkidir — bu rehberdeki en yararlı tek cümle ve unutulması en kolay olanı da budur.

    Son olarak, sayıları bir sonraki kişinin bulacağı bir yere yazın: seçtiğiniz saklama süresi, sebebi ve tarih. logrotate dosyasının en üstünde bir yorum satırı yeterlidir. Yukarıdaki yapılandırma bir karardır ve bir yıl içinde kimsenin yeniden kuramayacağı bir karar, yeniden bir varsayılana dönüşür.

SP·10 — SSS

Hızlı yanıtlar

Son okteti sıfırlamak bir IP adresini gerçekten anonimleştirir mi?

Hayır — ve burada rahatlatıcı olmak yerine kesin olmakta fayda var. Bir /24'e kısaltmak psödonimleştirmedir: bir ziyaretçiyi tek bir adres yerine en fazla 256 adresten oluşan bir bloğa daraltır — bu, bir konut internet sağlayıcısında bir mahalle, küçük bir işletme ağında ise hâlâ tek bir kuruluş olabilir. /24'ü hassas bir zaman damgası ve ayrıntılı bir user-agent dizesiyle birleştirin; kararlı bir taraf için yeniden kimliklendirme genellikle mümkündür.

Gerçekten yaptığı şey, bir günlük dosyasını dünyadaki her başka veri kümesiyle önemsizce birleştirilebilir kılan tek alanı kaldırmaktır ve bunu satır yazılmadan önce kaldırır — bu da tam değerin el konulmak, sızdırılmak ya da mahkeme celbiyle istenmek üzere diskinizde hiç var olmadığı anlamına gelir. Bu, riskte büyük, gerçek bir azalmadır. Anonimlik değildir ve buna anonimlik diyen her araç olduğundan fazla satmaktadır.

Erişim günlüğüm kısaltılırsa fail2ban yine de çalışır mı?

Kısaltılmış dosyadan çalışarak değil — <HOST> yakalaması 203.0.113.0 ile eşleşir ve size hiç bağlanmamış tek bir adresi engelleyerek gerçek kaynağı dokunulmamış bırakır. Bunu fark etmeden günlüklerini anonimleştiren herkes, fail2ban'ı sessizce kapatmış olur.

Çözüm, yukarıda anlatılan ayırmadır: nginx, tam adreslere sahip ikinci, kısa ömürlü bir dosya yazar ve fail2ban onu okur. /var/log/nginx/error.log'un da tam adresler taşıdığını ve yeniden biçimlendirilemeyeceğini unutmayın — dolayısıyla ona göre anahtarlanan jail'ler (tipik olarak kimlik doğrulama hatası olanlar) her durumda çalışmaya devam eder. credential stuffing yerine hacim temelli kötüye kullanım için nginx'in limit_req'i ikisinden de iyidir: süreç içinde, mikrosaniyeler içinde işlem yapar ve hiçbir şeyi yazmaz.

İstemcinin adresi olmadan üretimdeki bir 500 hatasında nasıl hata ayıklarım?

Bir istek kimliğiyle — ki bu genellikle adresten daha iyidir. nginx, istek başına $request_id üretir; bunu günlük formatınıza koyun, proxy_set_header X-Request-ID $request_id; ile üst akışa iletin ve uygulamadan stack trace'in yanında günlüğe kaydedin. Artık tek bir dize, erişim satırını, üst akış hatasını ve exception'ı birbirine bağlar.

Adresi üç noktada geride bırakır: istemci başına değil, istek başına benzersizdir, dolayısıyla iki eşzamanlı hata birbirine karışmaz; oturum ortasında adresi değişen mobil bir istemciye rağmen geçerliliğini korur; ve hata sayfasında kullanıcıya göstermek güvenlidir, dolayısıyla bir hata bildirimi tam ihtiyacınız olan tanımlayıcıyla gelir. Gerçekten adrese ihtiyaç duyan nadir durum için — diyelim ki hedefli bir saldırı — kısa saklama süreli güvenlik günlüğü onu bir gün boyunca tutar.

Sunucu günlüklerini yasal olarak tutmak zorunda mıyım?

AB'de bir web sitesi operatörü ya da bir barındırma müşterisi için genellikle hayır. İnsanların yarım yamalak hatırladığı toptan saklama rejimi, Adalet Divanı'nın 2014'te geçersiz kıldığı 2006 tarihli Veri Saklama Direktifi'nden geliyordu; kalan ulusal yasalar çoğunlukla web sunucusu işleten birini değil, elektronik haberleşme sağlayıcılarını — telekomları ve internet sağlayıcılarını — bağlar. GDPR ise tam tersi yönde iter: daha azını, daha kısa süre tutmaya doğru. Amerika Birleşik Devletleri'nin de site operatörleri için genel bir saklama zorunluluğu yoktur, gerçi bir muhafaza talebi gelebilir ve bu, o an var olan veriyle ne yapabileceğinizi değiştirir.

İşin sektöre ve role göre özelleştiği yer şurasıdır: ödeme işleme, düzenlemeye tabi finans, sağlık hizmetleri ve gerçek bir internet sağlayıcısı olmak, kendi yükümlülüklerini taşır; bazı yargı bölgeleri ise herkese açık wifi ya da platform operatörlerine yükümlülükler getirir. Makinenin hangi ülkede bulunduğu, cevabı önemli ölçüde değiştirir. Bu hukuki tavsiye değildir — bu kategorilerden birindeyseniz, herhangi bir şeyi kısaltmadan önce kendi durumunuzu kontrol edin.

Peki ya Cloudflare'ın ya da CDN'imin tuttuğu günlükler?

Bunlar, origin'de ne yaparsanız yapın var olmaya devam eder ve kendi dosyanızı kısaltmak uç katmanın gerisine geçip bir şey değiştirmez. Cloudflare sizin için TLS'i sonlandırıyorsa, sunucunuz devreye girmeden önce tam isteği ve gerçek istemci adresini görür, bunu kendi takvimine göre saklar ve kendi hukuki sürecine yanıt verir. Aynısı barındırılan herhangi bir WAF ya da DDoS temizleyici için de geçerlidir.

İki kolunuz var. Uç katman sağlayıcınızın neyi ne kadar süreyle sakladığını kontrol edin ve izin verdiği her şeyi kısabildiğiniz kadar kısın. Ya da kontrolünüzdeki bir ters vekil sunucu çalıştırın — TLS sonlandırmasını yapan ve bir tünel üzerinden geri vekaleten ileten küçük bir VPS — bu durumda bu rehberdeki her şey önce o kutuya uygulanır, çünkü tam adreslerin gerçekten vardığı makine odur.

Erişim günlüklerini tamamen kapatmalı mıyım?

Genellikle hayır — ve sebep felsefi değil pratiktir. Erişim günlüğü olmadan bir kötüye kullanım şikayetini yanıtlayamazsınız, bir crawler'ı saldırıdan ayırt edemezsiniz, makineyi doğru boyutlandıramazsınız ve bir saat önce başlayan bir deploy'un 404 döndürmeye başladığını göremezsiniz. Ayrıca biri sunucunuzun yapmadığı bir şeyi yaptığını iddia ederse, ne olduğunu kanıtlama yeteneğinizi de kaybedersiniz.

Bu rehberin inşa ettiği orta yol şudur: satırı tutun, tanımlayıcıyı kaldırın, bir ilişkilendirme kimliği ekleyin ve tam adresleri tutan tek dosyanın bir gün içinde süresinin dolmasına izin verin. access_log off'un dosdoğru doğru olduğu tek yer statik varlıklardır — görseller, CSS, fontlar — bunlar salt hacimdir ve size, öncesindeki HTML isteğinden zaten alamayacağınız hiçbir şey söylemez. Hata günlüğünün her durumda hatalarda istemci adreslerini kaydetmeye devam ettiğini unutmayın — bu da kapatma düğmesine uzanmak yerine ona kısa bir saklama süresi vermek için bir sebep daha.

Pratiğe dökün

VPS 15 min içinde çevrimiçi, özel sunucu 2–12 h içinde teslim edilir. $30.00'den kripto ile yükleyin — kimlik bağlı değil.

VPS dağıt