Nginx – Varnish client.ip says 127.0.0.1

apache-2.2nginxUbuntuvarnishx-forwarded-for

So I have a setup like Nginx -> varnish -> apache2
If I get a request with a static file it is sent through nginx to varnish and back to nginx again since its a lot faster than letting apache2 server it. My problem is that when I do a

sub vcl_fetch {
    set beresp.http.X-Tabulex-Client = client.ip;

to see what the clients ip address is I am being told its 127.0.0.1 (X-Tabulex-Client 127.0.0.1) In the vcl_recv I have:

sub vcl_recv {
    if ((!req.url ~"^/typo3temp/*" && !req.url ~"^/typo3/*") &&req.url ~"\.(jpg|css|gif|png|js)(\?.*|)$"){
        set req.backend = aurum;
        set client.identity = req.http.X - Forwarded - For;
    } elseif(client.ip == "192.168.3.189") {
        /* Traffic from the other Varnish server, serve using real backends */
        set req.backend = balance;
        /* Set client.identity to the X-Forwarded-For (the real IP) */
        set client.identity = req.http.X - Forwarded - For;
    } else{
        /* Traffic coming from internet, use the other Varnish as backend */
        set req.backend = iridium;
    }
}

The nginx config contains

proxy_set_header  X-Real-IP  $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_intercept_errors on;

when sending to varnish the first time and nothing when receiving from varnish again.

Im not sure where the problem is. I would expect the client.ip to contain the external ip address so I could use it for acl. Any ideas?

Best Answer

The value of client.ip is 127.0.0.1 because nginx is the client. It wouldn't make sense for Varnish to mask this value -- even in situations like yours where Varnish is sitting behind a front-end proxy, you often want to base decisions on the ip address of the thing actually making the connecting to Varnish.

What you really want to do is have nginx put the remote client ip address into a dedicated header (which you're already doing with X-Real-IP) and use that for making connection decisions. We do exactly this in our environment where we have Apache provided SSL connectivity in front of varnish, and then we use this header to make access decisions.

It's not as nice as using client.ip (you can't match it using acls), but it works. We do something like this:

if (! (
        req.http.X-Real-IP ~ "^10\." ||
        req.http.X-Real-IP ~ "^100\.100\." ||
        req.http.X-Real-IP ~ "^200\.200\."
)) {
        error 403 "Forbidden";
}

Varnish doesn't provide a native mechanism for overriding client.ip with a custom header, but it is possible to solve the problem anyway because you can insert arbitrary C code into your configuration.

Here is an example that exactly parallels your situation that includes an example of replacing client.ip with another value so that it can be used in Varnish ACLs.