catalin
catalin

Reputation: 1116

varnish purge allowed only from a public ip

My setup is the following: Nginx(443 https) -> Varnish(port 6081) -> Nginx(port 83 - the app itself)

#nginx https conf:

location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 1800;
        proxy_request_buffering off;
        proxy_buffering off;
        proxy_pass http://127.0.0.1:6081;
}

#part of default.vlc conf:

backend default {
    .host = "127.0.0.1";
    .port = "83";
}

Of course, there's another nginx config for port 83, which is the application itself. I've configured it this way, so I can run varnish behind HTTPS.

Trying to setup purge to invalidate cache for specific endpoints, I've configured the following in the default.vcl:

acl purge {
    "127.0.0.1";
    "some_public_ip"
  }

sub vcl_recv {
    if (req.method == "PURGE") {
      if (!client.ip ~ purge) { 
         return (synth(405, "This IP is not allowed to send PURGE requests."));
      }
      return (purge);
    }
}

Everything ok, I can execute:

curl -X PURGE -I "https://web_server/index.php".

The issue is that, if I remove "127.0.0.1" from the acl list, and only let "some_public_ip", it won't work anymore. It will return "This IP is not allowed to send PURGE requests". I only want the purge to work for that "some_public_ip" only. Is it possible?

Upvotes: 0

Views: 789

Answers (1)

Thijs Feryn
Thijs Feryn

Reputation: 4818

Because Nginx sits in front of Varnish the client.ip variable always refers to it through localhost. The actual client IP address is stored in the X-Forwarded-For header.

You can extract it by calling std.ip(req.http.X-Forwarded-For,client.ip). This will convert the value of X-Forwarded-For from a string to an IP address and will still use client.ip as a fallback in case of errors.

Because Nginx already sets the X-Forwarded-For header, Varnish will add the IP address of its client to it.

My varnishlog output shows the following log lines:

-   ReqUnset       X-Forwarded-For: 178.118.13.77
-   ReqHeader      X-Forwarded-For: 178.118.13.77, 127.0.0.1

We have to remove the second part in order to get the right IP address. We can do this using the following VCL snippet:

set req.http.X-Forwarded-For = regsub(req.http.X-Forwarded-For,"^([^,]+),.*$","\1");

In the end, this is the VCL code we need to make this work:

vcl 4.0;

import std;

acl purge {
    "127.0.0.1";
    "some_public_ip"
  }

sub vcl_recv {
    if(req.http.X-Forwarded-For ~ ","){
        set req.http.X-Forwarded-For = regsub(req.http.X-Forwarded-For,"^([^,]+),.*$","\1");
    }
    if (req.method == "PURGE") {
      if (!std.ip(req.http.X-Forwarded-For,client.ip) ~ purge) {
         return (synth(405, "This IP is not allowed to send PURGE requests."));
      }
      return (purge);
    }
}

Upvotes: 1

Related Questions