varnishvarnish-vcledge-side-includes

Conditionally removing authorization header in Varnish Edge-side includes (ESI)


I'm using varnish with Edge-side includes for endpoints for which the primary endpoint requires authorization, but the ESIs usually do not... but sometimes they do. I am handling it now by adding a header I call Keep-Authorization to the main request when I want all ESI requests to retain the Authorization headers. Then I have

vcl_recv {
  ...
  if (req.esi_level > 0 && !req.http.Keep-Authorization) {
    unset req.http.authorization;
  ...
  }
}

This works OK, but is pretty inelegant--it requires the requester to know whether to add that Keep-Authorization header. While the API is currently only used by us, that may change.

In truth, the level 0 response knows whether its ESI children will require authorization, and should be able to set a header to indicate that. If I could read it in the appropriate part of the varnish lifecycle, I could remove (or not) the authorization header appropriately before ESI's are resolved base on what I get from the level 0 response.

This seems like something I would have to do in vcl_backend_response, but I'm not really sure. Or if in vcl_recv there were something like req_top for the top level response (which should be available by the time ESIs are being resolved) that would also do the trick.

In short, I'd like the origin to be able to determine whether the authorization header is retained for resolution of the ESIs in its response. Is this doable?


Solution

  • There's no such thing as beresp_top unfortunately. And because client logic and backend logic are split up in VCL, there is on real way to have that information in vcl_recv using native VCL.

    A possible solution is leveraging vmod_var. This is an open source Varnish module that is part of https://github.com/varnish/varnish-modules.

    There are currently no packages for it, so you'll have to compile it from source. Depending on the version of Varnish, you need to download the right branch.

    The var.global_set() function allows you to set a global variable in vcl_backend_response, which you can retrieve in vcl_recv using var.global_get()

    You could then use the following VCL code to conditionally strip off the Authorization header:

    vcl 4.1;
    
    import var;
    
    sub vcl_recv {
        if(var.global_get(req.url + "-remove-auth") == "true" || var.global_get(req_top.url + "-remove-auth") == "true") {
            unset req.http.Authorization;
        }
    }
    
    sub vcl_backend_response {
        if(beresp.http.X-Remove-Auth) {
            var.global_set(bereq.url + "-remove-auth", "true");
        }
    }