Varnish allow/reject connections with IPs list (ACL)

 TAGS:In a web server you can use directives to deny the unwanted eye to look at your content. This is an example of how Apache would handle a virtual host that only accepts connections from a series of IPs, but in Varnish that won't work.

        Order Deny,Allow
        Deny from all
        # Barcelona IPs
        Allow from 89.140.xxx.xxx/27
        # Japan IP
        Allow from 222.229.xxx.xxx/32

The reason that this won't work in Varnish is because the IP Apache is receiving is 127.0.0.1. (Varnish is the one connecting to Apache, not the client). The real client IP comes in the header X-HTTP-FORWARDED-FOR. If you want to fix this situation with Varnish in the middle you have basically two options:

  1. Fix Apache (or Nginx) to allow connections by looking into this header (explained here)
  2. Handle the ACL by Varnish 

The following recipe handles this second scenario and helps you create a whitelist with IPs that Varnish will serve content to. Any other incoming source will be denied.

Whitelist

So, open your /etc/varnish/default.vcl and add:

acl offices {
    "localhost";
    "11.22.33.44"/32; # You can remove the mask /32 if you need one IP only
    # Add as many IPs as you need here
}

Then put in the beginning of your vcl_recv:

 sub vcl_recv {

     # Ban all requests to domains like xx.myhost.com or xxx.myhost.com
     if ( req.http.host ~ "([a-z]{2,3}\.myhost\.com)$" && !(client.ip ~ offices) ) {
         error 403 "Access denied";
     }

Blacklist

This will discard any request not coming from the list. If you want to create a blacklist (e.g: undesired bots) then you only have to remove the negation of the condition, being:

acl suckers {
    "11.22.33.44"/32; # You can remove the mask /32 if you need one IP only
    # Add as many IPs as you need here
}

And in the vcl_recv:

 sub vcl_recv {

     # Ban all requests to domains like xx.myhost.com or xxx.myhost.com
     if ( req.http.host ~ "([a-z]{2,3}\.myhost\.com)$" && client.ip ~ suckers ) {
         error 403 "Access denied";
     }

I added a regular expression in the virtualhost to make an example a little bit more useful, but you can just write the host as is. Bad news is that the req.http.host does not accept a list like you did with the IPs.