Mendeteksi efek Slashdot di nginx

10

Apakah ada cara saya dapat membuat Nginx untuk memberi tahu saya jika klik dari pengarah melampaui ambang batas?

mis. Jika situs web saya ditampilkan di Slashdot dan tiba-tiba saya mendapat 2K hit dalam satu jam, saya ingin diberi tahu ketika melampaui 1K hit satu jam.

Apakah mungkin untuk melakukan ini di Nginx? Mungkin tanpa lua? (karena prod saya tidak dikompilasi)

Quintin Par
sumber
4
Apa itu "Slashdot" ??
ewwhite
Saya melakukan sesuatu seperti ini untuk mendeteksi ddos ​​di ngix. Saya mencapainya dengan mengurai log akses. Saya melakukan pekerjaan cron untuk mengurai log akses dan menghitung koneksi ip unik per jam.
Hex
8
Maksud Anda, Anda ingin nginx dapat mendeteksi jika Anda telah dibeli oleh Dice?
MDMarra
1
@Hex That (dan mungkin beberapa cuplikan dari skrip Anda) akan membuat jawaban yang sangat baik untuk pertanyaan ini :)
voretaq7
3
Mungkin tidak perlu khawatir tentang Slashdotted lagi. Server web Anda harus dapat menangani 4 koneksi tambahan satu jam. Mungkin ingin khawatir tentang Redditted, ...
HopelessN00b

Jawaban:

3

Solusi yang paling efisien mungkin untuk menulis sebuah daemon yang akan tail -fdengan access.log, dan melacak $http_refererlapangan.

Namun, solusi cepat dan kotor adalah menambahkan access_logfile tambahan , hanya mencatat $http_referervariabel dengan kustom log_format, dan memutar log secara otomatis setiap X menit.

  • Ini dapat dilakukan dengan bantuan skrip logrotate standar, yang mungkin perlu melakukan restart nginx agar file dibuka kembali (misalnya, prosedur standar, lihat / a / 15183322 pada SO untuk waktu yang sederhana- skrip berbasis) ...

  • Atau, dengan menggunakan variabel di dalamnya access_log, mungkin dengan mengeluarkan spesifikasi menit $time_iso8601dengan bantuan mapatau ifarahan (tergantung di mana Anda ingin menempatkan Anda access_log).

Jadi, dengan yang di atas, Anda mungkin memiliki 6 file log, masing-masing mencakup periode 10 menit http_referer.Txx{0,1,2,3,4,5}x.log, misalnya, dengan mendapatkan digit pertama dari menit untuk membedakan setiap file.

Sekarang, yang harus Anda lakukan adalah memiliki skrip shell sederhana yang dapat berjalan setiap 10 menit, catsemua file di atas, pipa ke sort, pipa ke uniq -c, ke sort -rn, ke head -16, dan Anda memiliki daftar 16 Referervariasi paling umum - bebas memutuskan apakah kombinasi angka dan bidang melebihi kriteria Anda, dan melakukan pemberitahuan.

Selanjutnya, setelah satu pemberitahuan berhasil, Anda dapat menghapus semua 6 file ini, dan, dalam menjalankan selanjutnya, tidak mengeluarkan pemberitahuan KECUALI semua enam file hadir (dan / atau nomor lain tertentu sesuai keinginan Anda).

cnst
sumber
Ini terlihat sangat berguna. Saya mungkin meminta terlalu banyak tetapi seperti jawaban sebelumnya, apakah Anda keberatan membantu dengan skrip?
Quintin Par
@QuintinPar Kedengarannya ekstra kurikulum! ;-) Jika Anda mau, saya dapat disewa dan berkonsultasi; email saya adalah [email protected], juga di Constantine.SU
cnst
Sangat mengerti. Terima kasih banyak atas semua bantuannya sampai sekarang. Semoga saya bisa memberi Anda beberapa hari :-)
Quintin Par
1
@ QuintinPar sama-sama Jangan khawatir, seharusnya skrip yang cukup sederhana dengan spesifikasi di atas; hanya masalah pengujian, konfigurasi dan pengemasan, pada dasarnya. :)
cnst
1
Anda adalah pahlawan super!
Quintin Par
13

Saya pikir ini akan jauh lebih baik dilakukan dengan logtail dan grep. Bahkan jika itu mungkin dilakukan dengan lua inline, Anda tidak ingin overhead itu untuk setiap permintaan dan Anda terutama tidak menginginkannya ketika Anda telah Slashdotted.

Ini versi 5 detik. Tempelkan di skrip dan letakkan beberapa teks yang lebih mudah dibaca di sekitarnya dan Anda menjadi emas.

5 * * * * logtail -f /var/log/nginx/access_log -o /tmp/nginx-logtail.offset | grep -c "http://[^ ]slashdot.org"

Tentu saja, itu sepenuhnya mengabaikan reddit.com dan facebook.com dan semua juta situs lain yang dapat mengirimkan banyak lalu lintas kepada Anda. Belum lagi 100 situs yang berbeda mengirimkan Anda masing-masing 20 pengunjung. Anda mungkin hanya memiliki ambang lalu lintas lama polos yang menyebabkan email dikirimkan kepada Anda, terlepas dari pengarah.

Ladadadada
sumber
1
Masalahnya adalah bersikap proaktif. Saya perlu tahu dari situs mana pun. Pertanyaan lain adalah di mana saya harus meletakkan ambang pintu? Apakah maksud Anda beberapa penguraian log tambahan? Juga saya tidak menemukan –o di fourmilab.ch/webtools/logtail
Quintin Par
Ambang batas akan bergantung pada seberapa banyak lalu lintas yang dapat ditangani server Anda. Hanya Anda yang bisa mengaturnya. Jika Anda ingin pemberitahuan yang lebih cepat, jalankan setiap lima menit, bukan setiap jam dan bagi ambang dengan 12. -o Pilihannya adalah untuk file offset sehingga tahu di mana harus mulai membaca waktu berikutnya.
Ladadadada
@ Ladadada, saya tidak setuju bahwa overhead akan menjadi substansial, lihat solusi saya - serverfault.com/a/870537/110020 - Saya percaya overhead akan sangat minim jika ini diterapkan dengan benar, terutama, (1), jika backend Anda benar-benar lambat, maka overhead ini akan diabaikan, atau, (2), jika backend Anda sudah cukup cekatan dan / atau di-cache dengan benar, maka Anda seharusnya memiliki sedikit masalah dengan penanganan lalu lintas di tempat pertama, dan sedikit beban tambahan tidak akan t membuat penyok. Secara keseluruhan, sepertinya pertanyaan ini memiliki dua kasus penggunaan, (1), baru saja diinformasikan, dan, (2), penskalaan otomatis.
cnst
4

Arahan nginx limit_req_zone dapat mendasarkan zona pada variabel apa pun, termasuk $ http_referrer.

http {
    limit_req_zone  $http_referrer  zone=one:10m   rate=1r/s;

    ...

    server {

        ...

        location /search/ {
            limit_req   zone=one  burst=5;
        }

Anda juga akan ingin melakukan sesuatu untuk membatasi jumlah negara yang diperlukan pada server web, karena header pengarah bisa sangat panjang dan bervariasi dan Anda mungkin melihat variet infinte. Anda dapat menggunakan fitur nginx split_clients untuk mengatur variabel untuk semua permintaan yang didasarkan pada hash dari header pengarah. Contoh di bawah ini hanya menggunakan 10 buckes, tetapi Anda dapat melakukannya dengan 1000 dengan mudah. Jadi, jika Anda memiliki slashdotted, orang-orang yang perujuknya hash ke dalam ember yang sama dengan URL slashdot juga akan diblokir, tetapi Anda dapat membatasi itu hingga 0,1% pengunjung dengan menggunakan 1000 ember di split_clients.

Akan terlihat seperti ini (benar-benar belum diuji, tetapi benar secara arah):

http {

split_clients $http_referrer $refhash {
               10%               x01;
               10%               x02;
               10%               x03;
               10%               x04;
               10%               x05;
               10%               x06;
               10%               x07;
               10%               x08;
               10%               x09;
               *                 x10;
               }

limit_req_zone  $refhash  zone=one:10m   rate=1r/s;

...

server {

    ...

    location /search/ {
        limit_req   zone=one  burst=5;
    }
rmalayter
sumber
Ini adalah pendekatan yang menarik; Namun, saya percaya pertanyaannya adalah tentang peringatan otomatis ketika efek Slashdot sedang berlangsung; solusi Anda tampaknya menyelesaikan sekitar secara acak memblokir sekitar 10% pengguna. Selain itu, saya percaya alasan Anda untuk menggunakan split_clientsmungkin salah informasi - limit_reqdidasarkan pada "ember bocor", yang berarti bahwa keadaan keseluruhan tidak boleh melebihi ukuran zona yang ditentukan.
cnst
2

Ya, tentu saja mungkin di NGINX!

Yang bisa Anda lakukan adalah menerapkan DFA berikut :

  1. Terapkan pembatasan tarif, berdasarkan $http_referer, mungkin menggunakan beberapa regex melalui a mapuntuk menormalkan nilai-nilai. Ketika batas terlampaui, halaman kesalahan internal dinaikkan, yang dapat Anda tangkap melalui error_pagepenangan sesuai pertanyaan terkait , pergi ke lokasi internal baru sebagai pengalihan internal (tidak terlihat oleh klien).

  2. Di lokasi di atas untuk batas terlampaui, Anda melakukan permintaan peringatan, membiarkan logika eksternal melakukan pemberitahuan; permintaan ini selanjutnya di-cache, memastikan Anda hanya akan mendapatkan 1 permintaan unik per jendela waktu tertentu.

  3. Tangkap kode Status HTTP dari permintaan sebelumnya (dengan mengembalikan kode status ≥ 300 dan menggunakan proxy_intercept_errors on, atau, alternatifnya, gunakan not-built-by-default auth_requestatau add_after_bodyuntuk membuat subrequest "bebas"), dan lengkapi permintaan asli seolah-olah langkah sebelumnya tidak terlibat. Perhatikan bahwa kita perlu mengaktifkan error_pagepenanganan rekursif agar ini berfungsi.

Ini PoC dan MVP saya, juga di https://github.com/cnst/StackOverflow.cnst.nginx.conf/blob/master/sf.432636. Mendeteksi-slashdot-effect-in-nginx.conf :

limit_req_zone $http_referer zone=slash:10m rate=1r/m;  # XXX: how many req/minute?
server {
    listen 2636;
    location / {
        limit_req zone=slash nodelay;
        #limit_req_status 429;  #nginx 1.3.15
        #error_page 429 = @dot;
        error_page 503 = @dot;
        proxy_pass http://localhost:2635;
        # an outright `return 200` has a higher precedence over the limit
    }
    recursive_error_pages on;
    location @dot {
        proxy_pass http://127.0.0.1:2637/?ref=$http_referer;
        # if you don't have `resolver`, no URI modification is allowed:
        #proxy_pass http://localhost:2637;
        proxy_intercept_errors on;
        error_page 429 = @slash;
    }
    location @slash {
        # XXX: placeholder for your content:
        return 200 "$uri: we're too fast!\n";
    }
}
server {
    listen 2635;
    # XXX: placeholder for your content:
    return 200 "$uri: going steady\n";
}
proxy_cache_path /tmp/nginx/slashdotted inactive=1h
        max_size=64m keys_zone=slashdotted:10m;
server {
    # we need to flip the 200 status into the one >=300, so that
    # we can then catch it through proxy_intercept_errors above
    listen 2637;
    error_page 429 @/.;
    return 429;
    location @/. {
        proxy_cache slashdotted;
        proxy_cache_valid 200 60s;  # XXX: how often to get notifications?
        proxy_pass http://localhost:2638;
    }
}
server {
    # IRL this would be an actual script, or
    # a proxy_pass redirect to an HTTP to SMS or SMTP gateway
    listen 2638;
    return 200 authorities_alerted\n;
}

Perhatikan bahwa ini berfungsi seperti yang diharapkan:

% sh -c 'rm /tmp/slashdotted.nginx/*; mkdir /tmp/slashdotted.nginx; nginx -s reload; for i in 1 2 3; do curl -H "Referer: test" localhost:2636; sleep 2; done; tail /var/log/nginx/access.log'
/: going steady
/: we're too fast!
/: we're too fast!

127.0.0.1 - - [26/Aug/2017:02:05:49 +0200] "GET / HTTP/1.1" 200 16 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:49 +0200] "GET / HTTP/1.0" 200 16 "test" "curl/7.26.0"

127.0.0.1 - - [26/Aug/2017:02:05:51 +0200] "GET / HTTP/1.1" 200 19 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:51 +0200] "GET /?ref=test HTTP/1.0" 200 20 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:51 +0200] "GET /?ref=test HTTP/1.0" 429 20 "test" "curl/7.26.0"

127.0.0.1 - - [26/Aug/2017:02:05:53 +0200] "GET / HTTP/1.1" 200 19 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:53 +0200] "GET /?ref=test HTTP/1.0" 429 20 "test" "curl/7.26.0"
%

Anda dapat melihat bahwa permintaan pertama menghasilkan satu front-end dan satu pukulan backend, seperti yang diharapkan (saya harus menambahkan backend dummy ke lokasi yang telah limit_req, karena return 200akan didahulukan dari batas, backend nyata tidak diperlukan selama sisa penanganan).

Permintaan kedua adalah di atas batas, jadi, kami mengirim peringatan (mendapatkan 200), dan menyimpannya, mengembalikan 429(ini diperlukan karena batasan yang disebutkan sebelumnya bahwa permintaan di bawah 300 tidak dapat ditangkap), yang kemudian ditangkap oleh front-end , yang bebas sekarang bebas untuk melakukan apa pun yang diinginkannya.

Permintaan ketiga masih melebihi batas, tetapi kami sudah mengirimkan lansiran, jadi, tidak ada lansiran baru yang dikirim.

Selesai! Jangan lupa untuk memotongnya di GitHub!

cnst
sumber
Dapatkah dua kondisi pembatasan tingkat bekerja bersama? Saya menggunakan ini sekarang: serverfault.com/a/869793/26763
Quintin Par
@ QuintinPar :-) Saya pikir itu akan tergantung pada bagaimana Anda menggunakannya - masalah yang jelas akan membedakan di satu lokasi yang batasnya memperkenalkan kondisi; tetapi jika ini adalah limit_req, dan yang lainnya adalah limit_conn, maka gunakan saja di limit_req_status 429atas (membutuhkan nginx yang sangat baru), dan saya pikir Anda harus menjadi emas; mungkin ada opsi lain (satu untuk bekerja pasti adalah chaining nginx w / set_real_ip_from, tetapi, tergantung pada apa yang ingin Anda lakukan, mungkin ada pilihan yang lebih efisien).
cnst
@ QuintinPar jika ada yang hilang dari jawaban saya, beri tahu saya. BTW, perhatikan bahwa setelah batas tercapai, dan skrip Anda dipanggil, sampai skrip tersebut di-cache dengan benar oleh nginx, maka konten Anda mungkin tertunda; misalnya, Anda mungkin ingin mengimplementasikan skrip secara asinkron dengan sesuatu seperti golang, atau mencari opsi batas waktu untuk upstreams; juga, mungkin ingin menggunakan proxy_cache_lock onjuga, dan mungkin menambahkan beberapa penanganan kesalahan untuk apa yang harus dilakukan jika skrip gagal (misalnya, menggunakan error_pagedan juga proxy_intercept_errorslagi). Saya percaya POC saya adalah awal yang baik. :)
cnst
Terima kasih telah mencoba ini. Satu masalah besar bagi saya masih adalah, saya menggunakan limit_req dan limit_conn sudah di level http dan itu berlaku untuk semua situs web yang saya miliki. Saya tidak bisa menimpa ini akan ini. Jadi solusi ini menggunakan fungsi yang dimaksudkan untuk sesuatu yang lain. Adakah pendekatan lain untuk solusi ini?
Quintin Par
@ QuintinPar Bagaimana dengan memiliki instance nginx bersarang, di mana masing-masing akan menggunakan satu set limit_req/ limit_conn? Misalnya, letakkan konfigurasi di atas di depan server front-end Anda saat ini. Anda dapat menggunakan set_real_ip_fromdi hulu nginx untuk memastikan IP dicatat dengan benar di telepon. Selain itu, jika masih tidak sesuai, saya pikir Anda harus mengartikulasikan kendala dan spesifikasi Anda dengan lebih jelas - tingkat lalu lintas apa yang sedang kita bicarakan? Seberapa sering stat harus dijalankan (1 menit / 5 menit / 1 jam)? Apa yang salah dengan logtailsolusi lama ?
cnst