Problem/Motivation
In an attempt to make authcache with anonymous sessions (SESS cookie present on anonymous access) work with fastly, I started to face several different issues, quite a bit were discussed on this issue and its evolved into an ongoing discussions on a best practice to use an upstream reverse proxy (CDN or something else.). The site is an ecommerce site.
Proposed resolution
I created the following module: https://www.drupal.org/project/authcache_upstream
Following its configuration recipe should allow any upstream (fastly, varnish, nginx) to work nicely with authcache.
Remaining tasks
- Something I found out is that it makes it slightly hardish to make this work properly with mixed http/https sessions, I don't think there's much way around it and the suggested contributed module attempts to cope a bit with this particular case, but I am simply favouring forcing HTTPS.
- There's also an ongoing discussion on the issue, if the cookie should be present in the module, but after adding slightly more logic to the contributed module, maybe it's fine that it's maintained elsewhere.
- Another byproduct of this is that there's maybe the possibility of using this upstream module with another cache, like builtin on something, having two different layers of cache, this is complex and probably very use case specific, but worth noting it looks possible.
Fastly
A lot of references are for fastly, but only because that's the CDN I am working with right now. It really should apply to any reverser proxy, but if someone working on fastly finds this issue, worth looking at the following:
- https://www.fastly.com/blog/best-practices-for-using-the-vary-header
- #2792955: Simplify the module by removing unnecessary and conflicting logic
| Comment | File | Size | Author |
|---|
Comments
Comment #2
znerol commentedThis is more or less what the Authcache Varnish module does, so you might want to study that (and also the VCL).
The big problem with something like
Vary: X-Authcache-Rolesis that the respective header needs to be on the request. I.e., if theX-Authcache-Rolesheader is not on every request, then there is nothing to vary... Hence, it is necessary to somehow add the custom request header server-side. That's the exact reason for the non-trivial vcl.Comment #3
hanoiiComment #4
hanoiiWell, not exactly, at least I think so. In the way this patch adds it, which is the same of what you do in
If authcache is busted, it means that no "caching is being done", so the no-cache headers are also being set, so in those cases, it's fine not to have anything to vary on, because you are also sending the no-cache header, so there's nothing to attempt to vary on.
Or am I understanding this in the wrong way?
I tried it on a setup, So I am getting and varying on x-authcache-roles, on anon with sessions. The minute I log in with a different role in which no x-authcache-roles is set, I am still getting the proper logged in page. I think because an empty Vary it's still a valid Vary combination.
Comment #5
hanoiiI edited the previous comment with more info, sorry i didn't add another comment, just in cased you missed it. Thanks for getting back so quickly (before I got to submit the patch:).
Comment #6
znerol commentedIf you want to
Varyon an customX-Authcache-Rolesheader, then that header needs to be on the request (browser->server) not on the response (server->browser). This patch addsX-Authcache-Rolesto the response. This is hardly of any use.Comment #7
hanoiiI am pretty sure it's the other way around.
That's why the vary is set server side by Drupal and your module.
See
- https://tools.ietf.org/html/rfc7231#section-7.1.4
- https://www.fastly.com/blog/best-practices-for-using-the-vary-header
- https://varvy.com/mobile/vary-user-agent.html
Comment #8
znerol commentedYour patch adds an
X-Authcache-Rolesresponse header. This is completely useless wrt to a customVaryheader.The
Varyresponse header holds a list of request headers. Request headers do originate on the user-agent, not on the server. There is no way to enforce a custom header in the browser, except when using JavaScript XHR.Comment #9
hanoiiyou are so very right. I got this wrong. Thanks for pushing the explanation. It should be a cookie with the authcache roles then with some transformation in the vcl t convert that into a custom request header. I will see what the varnish module does but I believe at least the cookie should be present regardless of the backend module cache.
Comment #10
znerol commentedNote that fastly seems to allow user supplied VCL. However, it seems that they are using a customized version of Varnish 2. The Authcache Varnish module ships with VCL for Varnish 3 and 4.
Maybe it is possible to port the relevant bits to fastly VCL. E.g., the docs about Authenticating before returning a request describe a similar mechanism as the one Authcache Varnish uses to retrieve the authcache key.
I do not find any indication whether or not they support ESI, but that's optional anyway.
Comment #11
hanoiiThanks! I just looked properly on the varnish module, and you are right that they are doing something similar, however, the logic is complex and really applies to something more out of the box, but it's just too custom for implementing it on mostly any cdn as reverse proxy as they normally have their own things and limitations.
I think fastly does support ESI but I am not very interested at the moment as we are sorting the dynamic bits of the page on our own, so I don't really need it. What I would like to have, is something general and relatively simple that I could use with fastly (and really any other reverse proxy for that matter).
The authcache_varnish module generates a callback to get the key, it's a bit too troublesome, as the key, imho, could be simply sent on a cookie and then transformed on the cache side.
I noticed a lot of checks that happen on
Are those really necessary, is showing the cache key to the outside world any security flaw? It doesn't look like that at a first thought.
So doing something in the like of:
I think having that for regardless of the backend (as in the main authcache module) would help.
I can do this on a custom module of my own I just think it's a nice addition to authcache, if you agree and unless you have a strong security concern on leaking the authcache key to the outside world, I can submit a patch with this approach.
Only having this information in the cookie, allows to do what fastly explains on the Vary: Cookie section.
Basically it's:
1. transforming the cookie value into a custom http request header
2. adding the custom header to the vary so that the cache use it
3. remove the vary header before sending the response back to the browser.
There in fastly there are other bits that's done although I wouldn't do all of that.
The above vcl is a very short addendum to fastly and probably any other varnish configuration, so it gets really simple unless I am missing something else out.
Comment #12
znerol commentedThere is nothing wrong with exposing the cache-key to the outside world. In fact the Authcache Boost backend does exactly that.
It would be definitely great if you could put together a special backend module in the spirit of Authcache Varnish or Authcache Boost with a sample configuration ready to be deployed. I think it would be better to create a new project for that (cf. Authcache Boost), such that the module can be maintained independently of the main Authcache project (by people which are familiar with and do have access to Fastly services).
Comment #13
hanoiiI think I am starting to understand what I really want to do and how/where to do it and there was a bit of confusion on this issue as how I understood the design of authcache, still definitely not following everything but getting there. Caching is definitely a complex subject.
The main thing I notice is that i wasn't clear in what I was really going for. I don't want to create a new backend for fastly, because it's really not needed. I underestand now that what the varnish module does is leaving the storing of the cache pages entirely to varnish and that's why it has that many logic.
However what I like is to still use the builtin cache, which works very good, but also use fastly for its cdn and a second caching layer.
They could even have different configuration as far as ttl goes and all.
The only thing I see needing for this is to have the builtin cache expose the key in a cookie so that it can be used by fastly. And eventually any other varnish upstream or any other really. I wonder if there might be extra things needed for this to work properly, but I am not quite sure.
I thought this was meant to be globally, like my attempt with X-Authcache-Roles before, but I now see it has to be done within the builtin cache.
I do wonder where is the proper place to add such a cookie. I mean simply adding a cookie that holds the key.
Would adding it in
be enough, or that's only for when the key changes?
If so, where else do I need to add it and is there a proper way of adding such a cookie?
I see boost use
autcache_add_cookie, is that what I should be using?Comment #14
hanoiiOn second thoughts, doing an simplish authcache_fastly, or even and authcache_cookie or authcache_generic could be simple and not a bad idea, although adding it to builtin could be harmless/useful and help me in building this other posible module.
Comment #15
hanoiiHave a look at https://www.drupal.org/sandbox/hanoii/2786375 if you can.
Basically is mostly we discussed here. A dumb backend simply exposing a cookie and a general recipe that can be replicated on serveral upstream reverse proxies.
I still wonder if something so simple shouldn't be on authcache core.
Like adding the cookie regardless of the backend, and maybe having a blank or dummy backend you can enable.
I will see if I come up with some useful authcache_debug info addition, although not sure if possible.
Comment #16
znerol commentedWow! Congratulations. I cannot comment on the VCL, I'm not familiar with fastly dialect. Regarding the PHP part, I suggest adding the
httponlydirective to the cookie in order to make it inaccessible from JavaScript. Also why do you check for the cookie when you actually want to use a custom request header? I'd expect that something similar (or identical) toauthcache_varnish_boot(), but maybe I did misread something.Frankly, I'm really torn on this. It is true that the Drupal/Authcache part is really simple. But in my opinion there is nothing wrong with minimalistic modules - more features will creep in over time anyway. I guess the bigger challenge for users is to set up the reverse proxy software/service, so the size of the PHP part might be not so relevant when trying to take a decision on whether or not this should be a separate project.
Maybe it helps to switch perspective and try to figure out which variant is best for site builders.
Authcache can be quite complex to set-up. One of the design goals was to make it possible to easily swap backends in order to simplify the development/staging/production workflow and also to better support adapting sites to growing needs. Developers may work with the builtin backend while production runs on Varnish. Also when traffic grows, sites can switch from builtin SQL backed cache to whatever they fancy. I guess a fastly integration module might fit into this picture really well.
I feel that the current content of the sandbox tries to be two things: A simple generic backend for reverse proxies (which must be carefully configured though) and at the same time an example on how to integrate fastly. I argue that it would be more useful for site builders if it did one thing only and if it did that without any compromise (and maybe even with nearly zero configuration). I think it would also be easier to add new/more advanced/fastly specific features if that module would be a separate project moving at its own pace.
Also maybe there is a chance that they accept an authcache integration patch over at the fastly project?
Comment #17
hanoiiThe custom request header is nearly transparent, and I wanted to make it so that it works the same regardless of that. By using cookie it also allows me to debug the module better than the header. Was planning on making it httponly, although I thought maybe the key is eventually useful to be accesible through JS?
It is two things, but to be honest, is really the former, a generic one.
With this, if I were to set up a local varnish, I wouldn't use the varnish backend, I would use this module and add the vcl property.
All of the fastly references is because I am using fastly for it. And I am also using the fastly module, but it's not a fastly integration. I do provide a fastly vcl example, but even that is tricky, because it depends on a fastly vcl template with specific tags. The minute they change anything on those tags, the boilerplate my change.[1]
With the ever growing list of CDNs and its different caching techniques, something like this makes it really standard.
Besides the fastly.vcl, I will also add a varnish.vcl, which will be basically the vcl example on the project page. It's not fastly dialect, it's simple varnish dialect. The recipe in the project page works the same for any reverse proxy.
By adding this into core (just setting and maintaining the cookie) you are allow to do this generic setup, and you get one more benefit, which is that you can also do so regardless of the backend you use. So you can set a builtin cache, and also use fastly with the same recipe. In that case you would have two different caches working at the same time. It's odd, but allows you to do nifty things. I'd reconsider. It's just so simple that it's almost a pity not to do it. And adding a cookie is really backend agnostic. You can use it or not, but it's there.
I am still working out a few issues with mixed http/https sessions, and yes, sorting it out with fastly, but the beauty of this cdn is that it's making you respect a standard to vary on the Vary header, so whatever you do, works the same for anything else.
Will soon to submit another patch for the cookies part on authcache.
[1]: https://docs.fastly.com/guides/vcl/mixing-and-matching-fastly-vcl-with-c...
Comment #18
znerol commentedHaving two server side page level cache layers is a bad idea IHMO and will lead to weird effects (e.g., see #2771847: Module doesn't negate internal page cache over at fastly), especially if you add purging / expire or stuff like geoip / device detection to the mix.
It only would make sense to add the cookie if the majority of use cases / deployments rely on that. I do not think that this is currently the case. Managing the authcache key is clearly backend specific and thus should be done in the backend module, not in core.
How about an Authcache CDN module which tries to integrate with any CDN service?
Comment #19
hanoiiFair enough.
The Authcache CDN is really Authcache upstream, my (probably poor) choice of name is only because CDN is not really the case. The upstream module can be used with a local varnish, a local nginx, anything really as long as you are allow to modify with headers and Vary.
Will continue shaping out this module and release it as such.
Thanks a lot for the ongoing discussions. As you said is a complex subject :).
Comment #20
wim leersVery interesting stuff here! Sent this issue link to Fabian Franz and my colleague Josh Waihi. Josh has worked on something almost identical: https://github.com/fiasco/authcache-acquia.
Comment #21
znerol commentedNice. I like the idea with the signature. I suggest including the session id as well into the HMAC. Otherwise you only have to capture a key and the signature once and then reuse it with another session.
Comment #22
hanoii@Wim Leers glad and happy you find the issue interesting!!
Updated the issue summary a bit, still working on it but I think it's pretty stable atm.
Comment #23
hanoiiComment #24
hanoiiComment #25
hanoiiJust promoted the module to https://www.drupal.org/project/authcache_upstream
Comment #26
hanoiiAnd I guess we can close this issue
Comment #27
znerol commentedThanks a lot! I've added a link to the Authcache project page.