The Varnish VCL match operator (~
) will currently only match an ACL (access control list) to an IP address data type. Since there’s no way to cast a string to an IP in VCL (although it is planned), you can’t match a forwarded-for header against an ACL without using inline C. You can only match client.ip
, which can be a problem depending on the cluster configuration.
The following hack works by using VCL’s inline C functionality to do the cast. Varnish compiles the ACL into a C function that you can call directly: match_acl_named_paybarrier_bypass()
. If there is a match, it sets a header for the backend.
It’s useful to play around with the command varnishd -C
which prints out the compiled VCL code.
C{
#include <netinet/in.h>
#include <string.h>
}C
// We use inline C to do the equivalent of this string to IP cast:
// if (IP(req.http.X-Forwarded-For) ~ paybarrier_bypass)
// This syntax should be supported in Varnish 3.0.
C{
struct sockaddr xff_ip;
// Make a complete copy of client.ip.
xff_ip = *(VRT_r_client_ip(sp));
// Need to cast a pointer to sockaddr_in which is the struct required by
// inet_pton.
struct sockaddr_in *xff_ip_in = (struct sockaddr_in *) &xff_ip;
// X-Real-Forwarded-For may contain two IP addresses, so grab the first.
char *rxff_ip_str = strtok(VRT_GetHdr(sp, HDR_REQ, "\025X-Real-Forwarded-For:"), ",");
if (rxff_ip_str == NULL)
rxff_ip_str = VRT_GetHdr(sp, HDR_REQ, "\025X-Real-Forwarded-For:");
// Copy the ip address into the struct's sin_addr.
inet_pton(AF_INET, rxff_ip_str, &((*xff_ip_in).sin_addr));
// Run the match.
if (match_acl_named_paybarrier_bypass(sp, &(xff_ip))) {
VRT_SetHdr(sp, HDR_REQ, "\025X-Our-Header:", "X", vrt_magic_string_end);
VRT_done(sp, VCL_RET_PASS);
}
}C