Jak ograniczyć stawki w Nginx, ale włączając / wyłączając niektóre adresy IP?

27

Jestem w stanie limit_reqograniczyć liczbę żądań do mojego serwera.

Chciałbym jednak usunąć ograniczenie szybkości dla niektórych adresów IP (tj. Białej listy) i zastosować inne ograniczenie prędkości dla niektórych innych (tj. Niektórych adresów IP, które chciałbym mieć tylko 1r / s).

Próbowałem użyć warunków warunkowych (np. if ( $remote_addr = "1.2.3.4" ) {}), Ale wydaje się, że działa to tylko z regułami przepisywania, a nie z regułami ograniczenia prędkości.

Jason Cohen
źródło

Odpowiedzi:

33

Naprawdę lepiej jest unikać stosowania dyrektywy „jeśli”. Kiedy klucz w limit_req_zone (i limit_conn_zone) jest pusty, limity nie są stosowane. Możesz użyć tego w połączeniu z mapą i modułami geograficznymi, aby utworzyć białą listę adresów IP, w których ograniczenia przepustnicy nie są stosowane.

Ten przykład pokazuje, jak skonfigurować limit zarówno dla równoczesnych żądań, jak i częstotliwości żądań z jednego adresu 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;

Dyrektywy stref muszą być umieszczone na poziomie http, jednak pozostałe dyrektywy mogą być umieszczone niżej, np. Na poziomie serwera lub lokalizacji, aby ograniczyć ich zakres lub jeszcze bardziej dostosować limity.

Dalsze informacje znajdują się w dokumentacji Nginx ngx_http_limit_req_module i ngx_http_limit_conn_module

shonky użytkownik Linux
źródło
Jaka jest różnica między tymi 2 modułami?
mente
1
Zgodnie z komentarzami, pierwsze ogranicza równoczesne połączenia, drugie ogranicza szybkość połączeń
shonky użytkownik systemu Linux
Czy możesz wyjaśnić, dlaczego wykonujesz mapowanie w dwóch etapach, a geonastępnie mapzamiast geoustawiać $limitbezpośrednio?
Marcus Downing
2
Wygląda na to, że geonie można odwzorować na zmienną, więc jeśli podasz $binary_remote_addrjako wartość odwzorowania, przełoży się to na ciąg literalny "$binary_remote_addr", a nie na wartość zmiennej.
ColinM
1
Chciałbym dodać, że jeśli dany adres IP znajduje się już w strefie, musisz zrestartować nginx; przeładowanie to za mało.
Halfgaar
5

Możesz bezpiecznie używać nazwanych lokalizacji, takich jak „@lokalizacja” w bloku if ().

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

Coś takiego powinno działać:

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 / {
         ...
      }
   }

Wpisz „location @slowdown {}” tymi samymi informacjami, co „location / {}, takie jak proxy_pass, jeśli używasz nginx jako odwrotnego proxy.

Robert Suh
źródło
Nie jestem pewien, czy rozumiem część 410? Czy klient faktycznie widzi kod statusu HTTP 410?
svrist
1
Wow, to naprawdę działa! Bardzo fajna error_pagesztuczka, +1! @svrist, zobacz serverfault.com/a/870170/110020, aby uzyskać pełne wyjaśnienie, jak coś takiego mogłoby działać i dlaczego.
cnst