Varnish ACL with X-Forwarded-For Header

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 = &(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

About mike
Currently works for OpenSky as a Senior Linux Admin. He has a wonderful wife Thanuja and 2 great dogs. His major side project is Photoblog.

Comments

4 Responses to “Varnish ACL with X-Forwarded-For Header”
  1. gurutech says:

    if you have commas in your X-Forwarded-For header, you can cut it to your first IP adding a couple of lines to the code.

    if (xff_ip != NULL) {
    char *firstcomma = strchr( xff_ip, ‘,’); // find the first comma in the string
    if (firstcomma) xff_ip[firstcomma-xff_ip]=”; // if found, terminate the string at first comma position
    // Copy the ip address into the struct’s sin_addr.
    inet_pton(AF_INET, xff_ip, client_ip_ia);
    }

    • gurutech says:

      second was altered in the comment, maybe for website protection
      char *firstcomma = strchr( xff_ip, ‘,’); // find the first comma in the string
      if (firstcomma) xff_ip[firstcomma-xff_ip]=’BACKSLASH ZERO’; // if found, terminate the string at first comma position
      substitute BACKSLASH ZERO with a backslash and a zero, without spaces

  2. “Varnish ACL with X-Forwarded-For Header | Mike Zupan’s Random Blog” in fact enables me personally contemplate a tiny bit extra. I really treasured every particular piece of this blog post. Many thanks -Holley

  3. Have you ever thought about adding a little bit more than just your articles?

    I mean, what you say is important and all. But just imagine
    if you added some great visuals or video clips to give
    your posts more, “pop”! Your content is excellent but with images and
    clips, this blog could undeniably be one of the most beneficial in its
    field. Awesome blog!

Speak Your Mind

Tell us what you're thinking...
and oh, if you want a pic to show with your comment, go get a gravatar!