what is the better way to block CIDR ranges in nginx when using proxy_protocol?

1.9k views Asked by At

I have a small pool of nginx instances that are behind an ELB in AWS. This ELB is internet-facing and speaks PROXY protocol, not HTTP.

Here's the relevant section of my main.vhost

server {

    # speak only PROXY protocol
    # Accept 80/443; we'll do the http -> https re-dir elsewhere in conf
    # SSL is default mode
    listen 443 default_server ssl proxy_protocol;
    listen 80 proxy_protocol;

I am trying to use the deny directive from the ngx_http_access_module to prevent access to a range of CIDR blocks.

E.G.: in a .conf file loaded by nginx at startup:

include /etc/nginx/ip_block/*.conf;

and in the /etc/nginx/ip_block/ dir there is at least one file:

$ cat /etc/nginx/ip_block/some_cidr_ranges.conf | wc -l
361
$ head /etc/nginx/ip_block/some_cidr_ranges.conf
deny 2604:a880:1::/48;
<snip>
deny 208.68.36.0/22;

However, it appears that nginx's deny directive only works with the $remote_addr variable and does not work with the $proxy_protocol_addr variable. This effectly means that I can not use both the deny directive and the proxy_protocol together.

It appears as though the ngx_stream_realip_module module can be used to adjust the values of $remote_addr to the $proxy_protocol_addr value however, the build of nginx I have available to me is not currently configured with the --with-stream_realip_module build-flag. I am currently running 1.10.3 but it looks like the with-stream_realip_module flag was introduced in build 1.11.4 (https://github.com/nginx/nginx/commit/fe2774a9d689fa1bf201dd0e89449e3d9e4ad926)

Option 1: Build a version of nginx from source with the features i need compiled in.

While looking at the documentation for the deny directive, I found this note:

In case of a lot of rules, the use of the ngx_http_geo_module module variables is preferable.

From: https://nginx.org/en/docs/http/ngx_http_access_module.html

Which makes me wonder if there is a better way to achieve my goal of blocking CIDR ranges that may work with the binary of nginx that i currently have.

Could i try something like this:

geo $proxy_protocol_addr $blocked_cidr {
    default        01;
    include        conf/some_cidr_ranges_to_block.conf;
}

where the file conf/some_cidr_ranges_to_block.conf looks like:

2604:a880:1::/48    02;
<snip>
208.68.36.0/22      02;

and then in my server directive i could do something like:

if ($blocked_cidr != 01) {
 return 403;
}

Option 2: Attempt to use the geo directive and a custom IP range -> "country code" database to block traffic.

My questions:

- Is *option 1* going to be a better use of time / is it worth it to build my own version of nginx with the necessary `stream_realip_module` compiled in or is it going to be more performant / effective to use the `geo` directive to map the `$proxy_protocol_addr` onto a set of ranges as shown above (*option 2*) 


- Is there some other way to block or filter traffic in nginx by cidr block when using nginx in `proxy_protocol` mode that i have not yet considered?
2

There are 2 answers

0
user1521764 On BEST ANSWER

Quick update / answer to my question.

Thanks to the suggestion from Tan Hong Tat, there is no need for me to compile my own version of nginx. This takes option 1 off the table and saves me some work. Thanks!

I have loaded a few hundred deny rules into nginx and I am keeping an eye on performance impact.

Additionally, I have implemented something that looks like option 2 from my OP using the geo directive. I am similarly monitoring impact.

So far, it is not clear to me why the nginx documentation issues the suggestion about using deny with "lots" of rules:

In case of a lot of rules, the use of the ngx_http_geo_module module variables is preferable.

The geo directive does allow me a bit more flexibly in that i can choose which where to send users. The deny directive is a 403 and can not be changed.

0
Fizz On

You could use -

if ($proxy_protocol_addr != a.b.c.d) { return 403;
}