Nginx Map how to check value if empty

c0nw0nk nginx-forum at forum.nginx.org
Wed Mar 8 06:56:04 UTC 2017


Hey again,

So I modified my config to this as to prevent client's IP spoofing.


map $http_x_forwarded_for $client_ip_x_forwarded_for {
"" $remote_addr; #if this header missing set remote_addr as real ip
default $http_x_forwarded_for;
}
map $http_cf_connecting_ip $client_ip_from_cf {
"" $client_ip_x_forwarded_for; #if this header missing set x-forwarded-for
as real ip
default $http_cf_connecting_ip;
}
map $remote_addr $client_ip_output {
192.168.0.12      $client_ip_from_cf; #if ip match then it is a trusted ip
(whitelist)
192.168.0.13      $client_ip_from_cf; #if ip match then it is a trusted ip
(whitelist)
192.168.0.14      $client_ip_from_cf; #if ip match then it is a trusted ip
(whitelist)
192.168.0.15      $client_ip_from_cf; #if ip match then it is a trusted ip
(whitelist)

default $remote_addr; #if none of ip matched then default to remote_addr
}




Don't know if i can do ranges in my map example above so I made a geo map
too doing the exact same thing in case it may be better suited.

geo $remote_addr $client_ip_output { #most likely should use geo for the
range capability and other benefits
127.0.0.1         $client_ip_from_cf; #if ip match then it is a trusted ip
(whitelist)
192.168.1.0/24    $client_ip_from_cf; #if ip match then it is a trusted ip
(whitelist)
10.1.0.0/16       $client_ip_from_cf; #if ip match then it is a trusted ip
(whitelist)
::1               $client_ip_from_cf; #if ip match then it is a trusted ip
(whitelist)
2001:0db8::/32    $client_ip_from_cf; #if ip match then it is a trusted ip
(whitelist)

default $remote_addr; #if none of ip matched then default to remote_addr
}


The usage of the final output is as easy as this. "$client_ip_output;"
limit_req_zone $client_ip_output zone=one:10m rate=1r/s; #usage example for
the resulting output after all fallback checks and ip whitelist checks etc.


Based of what I showed here can anyone point out any problems they see with
it etc from what I want to achieve I think it should work fine.

IP whitelist basis, Fallback header's in case one is missing it goes to the
next header for the realip, If the next header is missing it goes to the
next until no more potential realip headers exist so we set their IP as
their connection $remote_addr.

Be nice if the realip module did this but lucky we don't need the realip
module this shows and can do so with map's and geo maps.


c0nw0nk Wrote:
-------------------------------------------------------
> Hey,
> 
> I was just looking at the realip module but that module does not seem
> to support fallback methods like I demonstrated I was in need of. (If
> it does support multiple headers and fallback conditions can someone
> provide a demonstration)
> 
> If real_ip_header CF-Connecting-IP; is missing then fallback to
> real_ip_header X-Forwarded-For; and if that header is missing use
> $binary_remote_addr;
> 
> I guess to prevent spoofing what if we merge the map's with a IP
> header check map so we can keep our dynamic capabilities but check
> that only the matching whitelisted IP's / IP ranges may send one of
> those headers.
> If a non whitelisted IP sends one of those headers we fall back to
> $binary_remote_addr; making their spoofing pointless.
> 
> That is the solution I can think of to prevent spoofing is to add to
> the map's unless anyone has better or known way's that could be simple
> or more easy.
> 
> Francis Daly Wrote:
> -------------------------------------------------------
> > On Mon, Mar 06, 2017 at 02:12:40PM -0500, c0nw0nk wrote:
> > 
> > Hi there,
> > 
> > good that you've found some more answers.
> > 
> > There's still some to be worked on, though, I suspect.
> > 
> > > So to explain how to get the origin IP for each method someone
> could
> > be
> > > using here is the list :
> > > 
> > > Cloudflares proxied traffic :
> > > sets the header $http_cf_connecting_ip so use this header to get
> the
> > > Client's real IP
> > 
> > Stock nginx has the realip module which will allow you to use a
> value
> > from one specific http header, as if it were the connecting
> address.
> > 
> > And stock nginx knows that the client can set any header to any
> value,
> > so it can be configured to only believe the value if it was set by
> a
> > trusted source. (More or less).
> > 
> > It looks like this $http_cf_connecting_ip contains a single IP
> > address,
> > which is the address of the thing that connected to Cloudflare --
> > either
> > the client, or a proxy that it uses. And it can be trusted, if the
> > incoming request went through the Cloudflare reverse proxy. (And,
> > presumably, it is spoofed if the incoming request did not go
> through
> > the Cloudflare reverse proxy.)
> > 
> > > Traffic from cloudflare via the DNS only connections :
> > > These would not have the $http_cf_connecting_ip header present.
> > > But those connections hit a load balancing ip what sets the
> header
> > > $http_x_forwarded_for header so that is the way to get the
> Clients
> > real ip
> > > via those connections.
> > 
> > $http_x_forwarded_for is common enough; it can hold a list of IP
> > addresses. The realip module knows how to deal with it.
> > 
> > Whatever method you use to read it, you should be aware that the
> > header is not necessarily exactly one IP address. And the client
> can
> > set the header to any initial value; the "load balancing ip"
> (unless
> > documented otherwise) probably creates-or-adds-to the header,
> rather
> > than creates-or-replaces.
> > 
> > > And then some connections don't hit my load balancing IP and go
> > directly to
> > > a specific origin server these connections can use $remote_addr.
> > 
> > They can. But those connections might also have
> $http_x_forwarded_for.
> > And
> > $http_cf_connecting_ip. So you will need a reliable way of
> > distinguishing
> > between case#1 and case#2 and case#3, if you care about that.
> > 
> > (Probably, the majority of "innocent" requests will not have
> spoofed
> > headers. If that is good enough for what you are trying to achieve,
> > then you're ok.)
> > 
> > > My Solution / conclusion :
> > > 
> > > How to come up with a fix that allows me to obtain the real IP in
> a
> > dynamic
> > > situation like this ?
> > 
> > I would suggest one of:
> > 
> > * go to extra measures to cause there to exist a new feature in
> nginx,
> > such that the realip module will look at more than one header to
> > determine
> > the address to use
> > 
> > or
> > 
> > * recognise that if Cloudflare put in a CF-Connecting-IP header,
> they
> > probably also put in a X-Forwarded-For header; ignore
> CF-Connecting-IP
> > and just use the realip module with X-Forwarded-For.
> > 
> > http://nginx.org/r/real_ip_header and the rest of that page.
> > 
> > > I have solved my issue with the following.
> > 
> > This will work, with the above caveats.
> > 
> > If you have time to experiment, you may find that the realip module
> > does
> > something similar in a less fragile way.
> > 
> > Cheers,
> > 
> > 	f
> > -- 
> > Francis Daly        francis at daoine.org
> > _______________________________________________
> > nginx mailing list
> > nginx at nginx.org
> > http://mailman.nginx.org/mailman/listinfo/nginx

Posted at Nginx Forum: https://forum.nginx.org/read.php?2,272744,272824#msg-272824



More information about the nginx mailing list