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·02Altı 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/secure — sizi 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·03Yazma 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.
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.
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.
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.
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·08Tasarı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·09Adım adım
-
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 -lBulduklarınızı not edin. Değişikliğin etkili olduğunu kanıtlamak için aynı komutları sonunda tekrar çalıştıracaksınız.
-
02
nginx satırı yazmadan önce istemci adresini kısaltın
map'i ve iki formatı,
httpbağlamına yüklenen bir dosyada oluşturun. Debian ve Ubuntu'da/etc/nginx/conf.d/, site yapılandırmalarından öncenginx.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_logsatı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
.0ile bitmeli ve satır birrid=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. -
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 weeklyaltı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_addryerine 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. -
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=7dbunu ş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.
-
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.logdirektifi 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/nullBir konteyner zaten büyük bir dosya tutuyorsa, geçmişi gerçekten kısaltan şey onu
docker compose up -d --force-recreateile yeniden oluşturmaktır — yalnızca yeniden başlatmak aynı günlük dosyasını korur. -
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-securityrsyslog kuruluysa aynı hesabı
/etc/logrotate.d/rsyslogiçin de yapın — varsayılanları dört hafta boyuncaauth.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. -
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-usageSonra 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.


