Bagaimana cara menilai-batas dalam nginx, tetapi termasuk / tidak termasuk alamat IP tertentu?

27

Saya dapat menggunakan limit_requntuk menilai-membatasi semua permintaan ke server saya.

Namun saya ingin menghapus batasan laju untuk alamat IP tertentu (mis. Daftar putih) dan menggunakan batasan tarif lain untuk yang lain (mis. IP tertentu saya ingin serendah 1r / s).

Saya mencoba menggunakan conditional (misalnya if ( $remote_addr = "1.2.3.4" ) {}) tetapi tampaknya hanya berfungsi dengan aturan penulisan ulang, bukan untuk aturan batas-tingkat.

Jason Cohen
sumber

Jawaban:

33

Sebaiknya hindari penggunaan arahan "jika". Ketika kunci di limit_req_zone (dan limit_conn_zone) kosong, batas tidak diterapkan. Anda dapat menggunakan ini bersama dengan modul peta dan geo untuk membuat daftar putih IPs di mana batas throttle tidak diterapkan.

Contoh ini menunjukkan cara mengonfigurasi batas untuk permintaan bersamaan dan tingkat permintaan dari satu IP.

http {
    geo $whitelist {
       default 0;
       # CIDR in the list below are not limited
       1.2.3.0/24 1;
       9.10.11.12/32 1;
       127.0.0.1/32 1;
    }

    map $whitelist $limit {
        0     $binary_remote_addr;
        1     "";
    }

    # The directives below limit concurrent connections from a 
    # non-whitelisted IP address to five

    limit_conn_zone      $limit    zone=connlimit:10m;

    limit_conn           connlimit 5;
    limit_conn_log_level warn;   # logging level when threshold exceeded
    limit_conn_status    503;    # the error code to return

    # The code below limits the number requests from a non-whitelisted IP
    # to one every two seconds with up to 3 requests per IP delayed 
    # until the average time between responses reaches the threshold. 
    # Further requests over and above this limit will result 
    # in an immediate 503 error.

    limit_req_zone       $limit   zone=one:10m  rate=30r/m;

    limit_req            zone=one burst=3;
    limit_req_log_level  warn;
    limit_req_status     503;

Arahan zona harus ditempatkan di tingkat http, namun arahan lain dapat ditempatkan lebih jauh ke bawah, misalnya di server atau tingkat lokasi untuk membatasi ruang lingkup mereka atau lebih lanjut menyesuaikan batas.

Untuk informasi lebih lanjut lihat dokumentasi Nginx ngx_http_limit_req_module dan ngx_http_limit_conn_module

pengguna linux shonky
sumber
Apa perbedaan antara 2 modul ini?
mente
1
Sesuai komentar, yang pertama membatasi koneksi konkuren, yang kedua membatasi laju koneksi
pengguna linux shonky
Bisakah Anda menjelaskan mengapa Anda melakukan pemetaan dalam dua tahap, dengan geodiikuti oleh map, daripada hanya digunakan geountuk mengatur $limitsecara langsung?
Marcus Downing
2
Tampaknya geotidak dapat memetakan ke variabel jadi jika Anda menetapkan $binary_remote_addrsebagai nilai pemetaan, ini akan diterjemahkan ke string literal "$binary_remote_addr", bukan nilai variabel.
ColinM
1
Saya ingin menambahkan bahwa jika IP yang dimaksud sudah ada di zona, Anda harus me - restart nginx; memuat ulang tidak cukup.
Halfgaar
5

Anda dapat menggunakan lokasi yang ditentukan dengan aman, seperti "@lokasi" di blok if ().

Lihat: http://wiki.nginx.org/IfIsEvil

Sesuatu seperti ini seharusnya bekerja:

http {

   limit_req_zone $binary_remote_addr zone=delay:10m rate=1r/m;

   server {
      ...

      error_page 410 = @slowdown;

      if( $remote_addr != "1.2.3.4" ) {
         return 410;
      }

      location @slowdown {
         limit_req zone=delay burst 5;
         ...
      }

      location / {
         ...
      }
   }

Isi "location @slowdown {}" dengan informasi yang sama dengan "location / {}, seperti proxy_pass jika Anda menggunakan nginx sebagai proxy terbalik.

Robert Suh
sumber
Saya tidak yakin saya mengerti bagian 410? Apakah klien benar-benar melihat kode status http 410?
svrist
1
Wow, ini benar-benar berfungsi! error_pageTrik yang sangat bagus , +1! @svrist, lihat serverfault.com/a/870170/110020 untuk penjelasan lengkap tentang bagaimana sesuatu seperti ini akan bekerja, dan mengapa.
cnst