Nginx – An internal IP address showed up in the website logs

ipnginxreverse-proxy

I log all visits to my website, such as date, url requested, and IP Address. I gained a few entries today from an "internal" IP of the format 10.x.x.x. Whoever this was, accessed several links on my site, and I can't tell what this "connection" was doing exactly. I really want to ask, "how" did they spoof their IP, "what" was likely this "connection's" intent, "how" did they receive any data back from my site if they are using an internal IP? I hope these questions aren't too vague.

Thanks

Best Answer

Since your server "proxies requests through nginx to apache" you might see the proxy-server's internal IP-address (although you would normally then see that in all requests) rather than the clients IP-address.

If your proxy server adds a header with the original IP-address and your application records that header, you need to ensure the proxy server actually replaces the ip-address in that header if it is already present.

For instance in situations where the client is also behind a proxy that sets the same header i.e

client with internal-IP 
\__ client-proxy sets "X-forwarded-for: internal-IP" 
   \__ (internet) 
      \__ your nginx proxy passes unmodified "X-forwarded-for: internal-IP" 
         \__ PHP reads the  internal-IP

where you would need:

client with internal-IP 
\__ client-proxy sets "X-forwarded-for: internal-IP" 
   \__ (internet) 
      \__ your nginx proxy sets a replacement "X-forwarded-for: public-IP-of-client-proxy" 
         \__ PHP reads the public-IP-of-client-proxy

It could also be that nginx appends the public-IP-of-client-proxy to the existing header and you need to check your code to see how it would handles multiple values.


In response to your comment:

Your PHP code is the following:

function get_ip_address() {
    foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key) {
        if (array_key_exists($key, $_SERVER) === true) {
            foreach (explode(',', $_SERVER[$key]) as $ip) {
                if (filter_var($ip, FILTER_VALIDATE_IP) !== false) {
                    return $ip;
                }
            }
        }
    }
}

That code checks for a number of different (potential) headers and will return the first header that contains a valid IP-address and if no such headers are present it will return the server variable $_SERVER[REMOTE_ADDR] which contains the client IP-address apache detected.

In case of a reverse proxy $_SERVER[REMOTE_ADDR] will always be the ip-address of your proxy, so that probably doesn't tell you what you want to know.

A fairly common thing to do is to instruct your reverse proxy to set a header with the client ip-address as detected by the proxy.
In your nginx config that would look like:

location /some/path/ {
    proxy_set_header FORWARDED_FOR $remote_addr;
    proxy_pass http://apache.example.com:8000/some/path;
}

Apache with mod_php will make that available as the _SERVER[HTTP_FORWARDED_FOR] header and there you go: your code detects the correct remote ip-address of the client.

Except that your code falls apart if another header, that has precedence in your code, is already set. I.E. When a client request with the custom header CLIENT_IP: <some-ip> gets parsed by your code, that is the ip-address that will be detected by your PHP function, instead of the actual client ip-address.

Test what happens with for instance: curl -H "CLIENT_IP: 10.0.0.0" http://yoursite.example.com/

Solution: in your code only accept the actual header that you set in nginx, or use nginx to unset or overwrite all the potential headers that your code is looking for.

Related Topic