Home > front end >  Nginx — is it possible to allow access from certain subnets only combining with set_real_ip_from?
Nginx — is it possible to allow access from certain subnets only combining with set_real_ip_from?

Time:02-05

I need to deny access to site for all, except number of subnets, where the frontend proxies are located. At the same time I need to set real IP for further processing. The diagram looks something like this:

                   Some infrastructure proxying traffic to nginx
 ________         ______________________________________________             _________________
|        |       |                                              |           |                 |
| Client |------>|public-allowed ingress IP          egress IP's|<--------->| Nginx           |
|________|       |______________________________________________|           |_________________|
                                                                             Allowed access
                                                                             for «egress IP's»
                                                                             only.

First I tried next configuration (all IP's are for example only):

server {
       server_name someserver.tld;

       # Subnets where frontend proxies are located
       allow   1.2.3.0/24;
       allow   4.5.6.0/24;
       allow   7.8.9.0/24;
       
       # Deny access bypassing proxies
       deny    all;

       set_real_ip_from 1.2.3.0/24;
       set_real_ip_from 4.5.6.0/24;
       set_real_ip_from 7.8.9.0/24;
       real_ip_header X-Forwarded-For;
       real_ip_recursive on;

        … skipped …
}

As expected that's wasn't working: nginx sets the client's real IP, then checks the conditions and denies access to site for everybody. So I tried next:

# This block is outside of «server» section, so I expect it will be processed first
geo $someserver_allow {
        default     0;
        1.2.3.0/24  1;
        4.5.6.0/24  1;
        7.8.9.0/24  1;
}

server {
       server_name someserver.tld;

       # Deny access bypassing proxies
       if ($someserver_allow != 1) {
               return 403 "Bypassing the frontend is not allowed.";
       }

       set_real_ip_from 1.2.3.0/24;
       set_real_ip_from 4.5.6.0/24;
       set_real_ip_from 7.8.9.0/24;
       real_ip_header X-Forwarded-For;
       real_ip_recursive on;

        … skipped …
}

I expected that nginx will process geo instruction, setting the $someserverallow ignoring real_ip_header in server section, but it seems that I was wrong. To be sure I commented out entire if clause and added next lines right after the geo section:

map $someserver_allow $downstream {
        default $remote_addr;
}

And also added next in server section:

add_header X-Downstream-IP "$downstream"

Requesting the frontend server with my browser I got my own IP address in X-Downstream-IP header, not the IP of proxy server.

So, what's wrong with my configuration or, maybe, with my understanding of that how nginx processes instructions?

CodePudding user response:

The realip module is convenient when working with Nginx behind a reverse proxy or load balancer. But it does redefine $remote_addr which affects logging and the allow directive amongst others.

You can use $realip_remote_addr to get the address of your reverse proxy or load balancer.

You can test it using the geo block in your question, just include that variable as the address parameter.

For example:

geo $realip_remote_addr $someserver_allow {
    default     0;
    1.2.3.0/24  1;
    4.5.6.0/24  1;
    7.8.9.0/24  1;
}
  • Related