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?
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:The
geo
directive does allow me a bit more flexibly in that i can choose which where to send users. Thedeny
directive is a 403 and can not be changed.