3

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?

Ronnie Jespersen
  • 221
  • 5
  • 13

3 Answers3

3

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.

larsks
  • 41,276
  • 13
  • 117
  • 170
  • Makes sence. I couldnt find any documentation on how the client works. Am I able to overwrite client.ip somehow? As you said yourself its the normal way of doing ACL :) – Ronnie Jespersen May 24 '12 at 07:09
  • So I tried your approach but I cant seem to get it working. `if (req.http.Cache-Control ~ "no-cache" && req.http.X-Real-IP ~ "^111\.111\.111\.111") { ban(req.url); return (pass); }` also tried ip address just using "111.111.111.111" – Ronnie Jespersen May 24 '12 at 22:18
  • Are these rules matching? What does your `varnishlog` output show for a single request? – larsks May 25 '12 at 00:19
  • Sorry it is working. It does recognize my IP address but it dosent ban the file. I think this is due to the communication between the two varnish caches. I get the new file when doing a no-cache hit but as soon as I make a normal request I get the old file. – Ronnie Jespersen May 26 '12 at 21:19
1

If you're using Varnish as your front-end webserver (our backend is NGINX) behind a Rackspace Cloud Load Balancer, then you need to use a pattern like the following:

if (!(
    req.http.X-Forwarded-For ~ "1\.2\.3\.4" || 
    req.http.X-Forwarded-For ~ "2\.3\.4\.5" 
    )) {
    # IP-based Rules
}

Rackspace Cloud Load Balancer won't pass anything but 127.0.0.1 to Varnish as client.ip. Also, I tried using a more restrictive pattern, like ^2\.3\.4\.5$ but it wouldn't match. I think the load balancer was adding extra characters to the X-Forwarded-For header.

Brendan
  • 145
  • 2
  • 10
1

If req.http.X-Real-IP is available in your request then you can use the function ip() of the std module to convert a string (like req.http.X-Real-IP) to a type IP and then use the ~ operator to compare it to an ACL list. This is more efficient than comparing multipe times with some IP strings. acl aclRestricted { "1.1.1.1"; "2.2.2.2"; } if (std.ip(req.http.X-Real-IP, "0.0.0.0") ~ aclRestricted ) { ... }

Reference Varnish V4.1: https://varnish-cache.org/docs/4.1/reference/vmod_std.generated.html#func-ip

Paul
  • 11
  • 2