I recently needed to restrict which IP addresses can access wp-login.php for a wordpress site of mine. This site is sitting behind varnish cache for speed. Modifying htaccess doesn’t work in this case so I have to modify the varnish configuration in order to get this to work.
The varnish documentation is actually quite good at telling you what you need to do. You have to first specify an acl and then in the vcl_recv function specify the action when these IPs are used.
I kept running into a problem where varnish wouldn’t compile. I kept receiving this error:
"Expected CSTR got 'admin_net'" (C String?)
It turns out my load balancer does not support the PROXY protocol, so client.ip is always the IP of the load balancer, not the IP of the person making the request.
The solution was finally found here where it was explained that in absence of PROXY protocol you can use the std.ip() function to convert a string containing an IP address to the value varnish expects an IP address to be, in order to check it against an ACL (see here for syntax reference.)
I then had to take it a step further to trim all the extraneous commas and quotes from X-Forwarded-For so that the std.ip() function would work:
# set realIP by trimming CloudFlare IP which will be used for various checks set req.http.X-Actual-IP = regsub(req.http.X-Forwarded-For, "[, ].*$", "");
With these three bits combined I was able to properly restrict access to wp-login.php to a specified whitelist of IP addresses:
acl admin { "10.0.0.0/24"; "10.0.1.0/24"; "10.0.2.0/24"; } ... sub vcl_recv { ... # set realIP by trimming CloudFlare IP which will be used for various checks set req.http.X-Actual-IP = regsub(req.http.X-Forwarded-For, "[, ].*$", ""); #Deny wp-login.php access if not in admin ACL if ((std.ip(req.http.X-Actual-IP, "0.0.0.0") !~ admin) && req.url ~ "^/wp-login.php") { return(synth(403, "Access denied.")); } ... }
Success.