Search code examples
nginxreverse-proxy

Can NGINX choose proxy_pass backend based on IP?


I have a situation where we have multiple test environments. Each environment needs to access different versions of a service and we have an NGINX proxy that sits in-front of these different services. Currently we're using multiple servers to do the proxy. Is there a way I can use NGINX allow or deny to filter which backend the environments connect to based on remote IP?

The v1 environment has IP addresses in the 10.0.1.0/24 range, and v2 is only connected to by IP's in 10.0.2.0/24.

Current Config

Simplified for brevity.

server {
  listen 80;
  server_name service.v1.net;
  proxy_pass http://10.0.10.56:8081;
}
server {
  listen 80;
  server_name service.v2.net;
  proxy_pass http://10.0.10.56:8082;
}

What I've tried

Clearly this doesn't work.

server {
  listen 80;
  server_name service.net;

  location / {
    # v1 proxy
    allow 10.0.1.0/24;
    deny all;
    proxy_pass http://10.0.10.56:8081;
  }

  location / {
    # v2 proxy
    allow 10.0.2.0/24;
    deny all;
    proxy_pass http://10.0.10.56:8082;
  }

}

Also Note...

I know this can be done with serving a proxy on different ports and iptables rules - I'm trying to figure out if NGINX can do this by itself.


Solution

  • You can use ngx_http_geo_module for that. (Which should just work out of the box). It sets variables depending on the client IP address, which can then be used in an if.

    geo $environment {
      10.0.1.0/24 v1;
      10.0.2.0/24 v2;
    }
    
    server {
      listen 80;
      server_name service.net;
    
      location / {
        if ($environment = v1) {
          proxy_pass http://10.0.10.56:8081;
        }
    
        if ($environment = v2) {
          proxy_pass http://10.0.10.56:8082;
        }
      }
    }
    

    All other IPs will see a 404 in this case.

    Although this works, be advised that using if within a location block can be very tricky: http://wiki.nginx.org/IfIsEvil