best practices request: spring-security-core plugin behind a load balancer?
short version: how do people run apps / plugins that reply on session storage behind a load balancer? Does everyone use some sort of shared session management (tomcat pools or memcache or mempool or terra-cotta or…) or use sticky-session?
This may just be an architectural flaw in our design, but I thought Id see how other people handle this.
At the moment, we're using 4 servers running tomcat behind a load balancer. I'm using grails-1.3.7 and spring-security-core-220.127.116.11 to manage our auth. Most of the time, this works fine, as all our authentication and webflow checkout flows happens over SSL, which is sticky-sessioned by the load balancer onto the same server.
The problems started when we implemented a portion of the site to trigger a custom authentication page for a 'members only' portion of the site (SSL not required, but login required). I'm branching on which login pageview I render based on the url that spring-security-core stashes in the DefaultSavedRequest, which works great in dev and on single machine installations. (the DefaultSavedRequest is stashed in session storage).
I think our problem is manifesting because of how grails does channel security + load balancers + session storage.
+ HTTP -- I make a request for /appname/restricted, 302'd to index.html
+ HTTP -- I make a request for /appname/restricted/index.html,
+ HTTP -- @Secured bounces it to /appname/login/auth
+ HTTPS - channel filters kick in and 302 it to https /appname/login/auth
I'm thinking that the DefaultSavedRequest gets set and stashed one of the original HTTP requests for /appname/restricted/index.html, and when spring-security-core plugin's Channel filter fires ( 'REQUIRES_SECURE_CHANNEL') , the rewrite to SSL happens, which engages the load balancer's sticky behavior. But at that point it's too late, the DefaultSavedRequest has been stashed on one of the 4 random servers and by the time I'm serving up an SSL'd login page, I'm likely to not be on the machine with the my session info. So i end up serving a default login instead of the customized one.
if i originally request the /appname/restricted over SSL -- all is well, as we're stickied onto the same machine.
I'd originally wondered how spring-security-core plugin auth worked at all behind a load-balancer, as the authentication struct seems to be stored in session. But I'm thinking I'm being "saved" by our usage of the 'remember-me' cookie, which is probably bootstrapping the user into whatever session they happen to hit.
+ it'd be nice if createLink(absolute:true, mapping:"restricted_section") could be told that generate an SSL link if I happen to know that its going to an SSL portion of the site. workaround is createLink().replace("http://","https://") , but that feels sleazy. Ideally, I think I'd like some tie-in with the 'REQUIRES_SECURE_CHANNEL definitions and createLink() .. tho that's just a workaround for the real "problem", auth info being tied-to/stored-in the session.
+ is there a way to configure spring-security-core plugin to store it's "session" information somewhere else? e.g.: If I'm daydreaming, i'd love to see
grails.plugins.springsecurity.sessionstorage = session // default. might also be "local", "cookie", "jdbc", etc
maybe that's safer to do with tomcat ,(using shared session pool, or memcache or terra-cotta or something) rather than expecting a plugin to be session independent.
Is this just pain from a (sub-optimal) architectural decision , or is there some established best practice for spring-security-core plugin behind a load-balancer?
short version: how do people run apps / plugins that reply on session
storage behind a load balancer? Does everyone use some sort of shared
session management (tomcat pools or memcache or mempool or terra-cotta or…)
or use sticky-session?
I've been load balancing with sticky session that actually hashes on jsessionid in query string or cookie (functionality is built-in to nginx), so users are sticky to their session as soon as there is a session to be sticky to, regardless of channel. And yes, enabling remember-me with persistent storage to the db does cover you nicely in the event that you drop a server so users suddenly arrive at a new server. It also allows me to throw away idle sessions after a relatively short period of time. I've never needed more than that.