5

We need to block a large number of requests by IP address with nginx. The requests are proxied by a CDN, and so we cannot block with the actual client IP address (it would be the IP address of the CDN, not the actual client). So, we have $http_x_forwarded_for which contains the IP which we need to block for a given request.

Similarly, we cannot use IP tables, as blocking the IP address of the proxied client will have no effect. We need to use nginx to block the requested based on the value of $http_x_forwarded_for.

Initially, we tried multiple, simple if statements: http://pastie.org/5110910

However, this caused our nginx memory usage to jump considerably. We went from somewhere around a 40MB resident size to over a 200MB resident size.

If we changed things up, and created one large regex that matched the necessary IP addresses, memory usage was fairly normal: http://pastie.org/5110923

Keep in mind that we're trying to block many more than 3 or 4 IP addresses... more like 50 to 100, which may be included in several (20+) nginx server configuration blocks.

Thoughts? Suggestions?

I'm interested both in why memory usage would spike so greatly using multiple if blocks, and also if there are any better ways to achieve our goal.

2 Answers2

5

I would suggest trying the map module with something like this:

map $http_x_forwarded_for $deny_access {
    default     0;
    1.2.3.4     1;
    1.2.3.5     1;
    1.2.3.6     1;
}

if ($deny_access = 1) {
    return 403;
}

444 is a special status code which causes nginx to drop the connection without sending a response. In your case this is dropping the connection between nginx and the CDN -- the CDN then decides what to return to the client. I would suggest returning the standard 403 (Forbidden).

mgorven
  • 30,615
  • 7
  • 79
  • 122
  • Thank you! This works rather well. Unfortunately, for some strange reason, nginx refuses to return a HTTP 444 normally. When we return 444, nginx logs as returning 444, but the client receives a 200. If we return something else, like HTTP 429, the client receives the 429 as expected. – Justin Kulesza Oct 25 '12 at 14:15
  • @JustinKulesza Edited answer. – mgorven Oct 25 '12 at 16:37
  • That makes sense. Thanks for the advice. Everything seems to be working very well right now -- our resident memory size is back down to around 40 MB or so. – Justin Kulesza Oct 25 '12 at 20:10
1

Another option would be to use the Real IP module to set the client IP to the value of X-Forwarded-For and then use deny directives to control access.

However, this would return a 403 (rather than the empty response returned by using 444). It also requires that Ngnix be compiled with --with-http_realip_module—something that may not be an option if you are restricted to using a binary distribution.

englishm
  • 11
  • 1