Comment limiter les taux dans nginx, mais en incluant / excluant certaines adresses IP?

27

Je peux utiliser limit_reqpour limiter la fréquence de toutes les demandes à mon serveur.

Cependant, je voudrais supprimer la restriction de débit pour certaines adresses IP (c'est-à-dire la liste blanche) et utiliser une restriction de débit différente pour certaines autres (c'est-à-dire certaines IP que j'aimerais aussi bas que 1r / s).

J'ai essayé d'utiliser des conditions (par exemple if ( $remote_addr = "1.2.3.4" ) {}), mais cela semble fonctionner uniquement avec des règles de réécriture, pas pour des règles de limite de débit.

Jason Cohen
la source

Réponses:

33

Il est vraiment préférable d'éviter d'utiliser la directive "if". Lorsque la clé de limit_req_zone (et limit_conn_zone) est vide, les limites ne sont pas appliquées. Vous pouvez l'utiliser en conjonction avec les modules de carte et de géo pour créer une liste blanche d'adresses IP où les limites de limitation ne sont pas appliquées.

Cet exemple montre comment configurer une limite pour les demandes simultanées et le taux de demande à partir d'une seule 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;

Les directives de zone doivent être placées au niveau http, mais les autres directives peuvent être placées plus bas, par exemple au niveau du serveur ou de l'emplacement pour limiter leur portée ou adapter davantage les limites.

Pour plus d'informations, reportez-vous à la documentation Nginx ngx_http_limit_req_module et ngx_http_limit_conn_module

utilisateur shonky linux
la source
Quelle est la différence entre ces 2 modules?
mente
1
Selon les commentaires, le premier limite les connexions simultanées, le second limite le taux de connexions
shonky linux user
Pouvez-vous expliquer pourquoi vous effectuez le mappage en deux étapes, geosuivi de map, plutôt que de simplement utiliser geopour définir $limitdirectement?
Marcus Downing du
2
Il semble geoimpossible de mapper sur une variable, donc si vous spécifiez $binary_remote_addrune valeur de mappage, cela se traduira par la chaîne littérale "$binary_remote_addr", et non par la valeur de la variable.
ColinM
1
Je voudrais ajouter que si l'IP en question est déjà dans la zone, vous devez redémarrer nginx; un rechargement ne suffit pas.
Halfgaar
5

Vous pouvez utiliser en toute sécurité des emplacements nommés, tels que "@location" dans un bloc if ().

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

Quelque chose comme ça devrait fonctionner:

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

Remplissez "location @slowdown {}" avec les mêmes informations que "location / {}, comme proxy_pass si vous utilisez nginx comme proxy inverse.

Robert Suh
la source
Je ne suis pas sûr de comprendre la partie 410? Le client voit-il réellement un code d'état http 410?
svrist
1
Wow, cela fonctionne réellement! Astuce très astucieuse error_page, +1! @svrist, voir serverfault.com/a/870170/110020 pour une explication complète du fonctionnement de quelque chose comme ça, et pourquoi.
2017