Varnish ACL with X-Forwarded-For Header

March 16, 2012    header nginx varnish

So I did a setup like this once

nginx -> varnish -> backend apaches

I did the nginx in front of varnish to handle SSL termination since varnish doesn’t do SSL. So the issue is you can do this for subnet checking in your varnish config

acl vpn {
  "192.168.0.0"/16;
}

sub vcl_recv {
  if (client.ip ~ vpn) {
    # something here
  }
  return(pass);
}

So the issue with this is varnish thinks the client.ip is 127.0.0.1 which is correct since the connection is coming from nginx. If varnish was out in front of nginx we wouldn’t have this problem and the example above would just work. So you might be thinking why not just replace client.ip with something like req.http.x-forwarded-for and be done with it all. Well that is a string in varnish and client.ip is an object I believe so you can’t do that. So we have to do some C hacking in the config to get around this.

I found the following example a bit ago on another blog in the comments and if I remember I’ll give credit.

So we need to add the following to make it work

C{
#include <netinet/in.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
}C

acl vpn {
  "192.168.0.0"/16;
}

sub vcl_recv {

C{
//
// This is a hack from Igor Gariev (gariev hotmail com):
// Copy IP address from "X-Forwarded-For" header
// into Varnish's client_ip structure.
// This works with Varnish 3.0.1; test with other versions
//
// Trusted "X-Forwarded-For" header is a must!
// No commas are allowed. If your load balancer something other
// than a single IP, then use a regsub() to fix it.
//
struct sockaddr_storage *client_ip_ss = VRT_r_client_ip(sp);
struct sockaddr_in *client_ip_si = (struct sockaddr_in *) client_ip_ss;
struct in_addr *client_ip_ia = &#038;(client_ip_si->sin_addr);
char *xff_ip = VRT_GetHdr(sp, HDR_REQ, "\020X-Forwarded-For:");

if (xff_ip != NULL) {
// Copy the ip address into the struct's sin_addr.
inet_pton(AF_INET, xff_ip, client_ip_ia);
}
}C

  if (client.ip ~ vpn) {
    # do something here
  }

  return(pass);
}

Now client.ip is set with the value of x-forwarded-for



comments powered by Disqus