Am 23.07.2014 um 17:59 schrieb Christopher Schultz:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Rainer,

On 7/23/14, 11:23 AM, Rainer Jung wrote:
On 23.07.2014 16:37, Christopher Schultz wrote:

On 7/18/14, 12:13 PM, Rainer Jung wrote:

On 17.06.2014 16:43, Christopher Schultz wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256

All,

I've been using sticky sessions with mod_jk and I can see
that there is a bit of a problem when attempting to take a
backend Tomcat server out of load-balanced rotation: a user
who never (or rarely) restarts their web browser will keep
the same JSESSIONID cookie forever and therefore end up with
the same backend server whether it has been disabled or not.

Quick series of events:

1. User visits load-balancer and gets a randomly-assigned
backend server/route. We'll call this route "X". The
JSESSIONID cookie set by the backend server is therefore
foo.X.

2. User's requests are routed by mod_jk to route X.

3. Route X is disabled using mod_jk's status worker

4. User's session on server X expires.

[Technically, 3 and 4 can happen in either order]

5. User makes a new request to the load-balancer, and mod_jk
sees the JSESSIONID cookie still set to foo.X. mod_jk sends
the request to route X which allows the user to login, etc.

Thus, it takes more time than necessary to bleed all the
traffic from route X for maintenance, etc.

Is there a way for mod_jk to ask route X if the session is
*still* valid? It seems that mod_jk will not re-route a
request that looks like it's got a valid session id to a new
(active) backend server unless the backend server X is
actually down.

Any ideas?

Not exactly what you want, but you can build something around
it:

1) Switch off stickyness for specific URLs

If you know that users will come via specific URLs, like a
login page, and you want that page to be handled non-sticky to
optimize load balancing even if users have an old cookie, you
can do that by setting the Apache envvar JK_STICKY_IGNORE. Look
for JK_STICKY_IGNORE on:

http://tomcat.apache.org/connectors-doc/reference/apache.html

This seems like a reasonable way to do things, except that we
still want to support requests to protected resources being saved
and redirected to the login page. If we did this
(JK_STICKY_IGNORE), then we'd end up "forgetting" the saved
request (because the client would be re-balanced to another node
for the login page) and ending up with a (useless) session on the
node we are trying to take down.

We'd like to retain the request-saving capabilities of the
container.

Whatever "saved" exactly means: if you can identify that situation
in terms of any part of the request (e.g. something in the referer
header etc.), you can add that as a positive or negative condition
via RewriteCond (or the 2.4 unified expression parser) and set
JK_STICKY_IGNORE in a RewriteRule that does not change the URI,
only sets the variable and also only of the RewriteConds apply.
You'd have to analyze the request-response stream e.g. with a
browser plugin to look for a useful header or similar which can be
used to distinguish the "saved" and the "normal" case.

When I say "saved" I mean this workflow:

1. User requests a protected resource
2. Container saves the request (which creates a new session), presents
the login screen
3. User authenticates
4. User is redirected to request saved in step #1

So where's the interaction with putting a JK_STICKY_IGNORE on the login page if directly retrieved? The above sequence of steps does not retrieve the login page from the outside via its real URL, e.g. /login, does it? That env var is only useful for URIs for which you know that they either don't need a session (unprotected static content) or will start a new session (login page accessed by URI).

The other case, a request with an invalid session ID accessing a tomcat instance with activation disabled can IMHO be handled by a filter that

- checks whether the request has a valid session using
  getSession(false), if it has one, let the request proceed

- checks activation state, if "active", let the request proceed

- checks the request method, if not GET, let the request proceed

- otherwise:

  - set the session cookie, e.g. JSESSIONID the an empty value
  - issue an external redirect to the same request URL
- optional redirect loop detection: add a query string param or cookie that gets the local jvmRoute appended during each redirect. Before doing the redirect, check that the local jvmRoute is not already part of that token (we have already been here before)

This would not really interfere with your saved requests: they would get a redirect which the browser follwos automatically and after that you will observe the normal behavior.

One option we have here is to provide separate URLs for
"regular" logins versus the saved-request logins mentioned above:
that would probably solve both problems.

2) Improve handling of sessions for node with activation
"disabled"

If you switch a node to activation "disabled", mod_jk will not
send requests there, that have no session id (and of course
also not when the session route points to another node). But
the old cookie requests might still go there.

Yes, this is what we would normally do.

For that you can use the feature, that mod_jk forwards the
activation state to the Tomcat node. The node can then decide
on itself, whether it will handle a request coming in with an
invalid session id, or for example clears the session cookie
and does a self-referential redirect, which then by mod_jk is
balanced on the fully enabled nodes.

This sounds like a promising technique. I was thinking we'd just
tell the Tomcat node directly (e.g. set a context-scoped flag)
that it was "disabled", but having AJP forward that information
would be even better, so the state can be managed entirely by the
status worker on the httpd side.

This logic can be put into a servlet filter.

I'm not sure it can be in a Filter because of the interaction
with the saved-request features described above. If in a Filter,
the request would be saved before the Filter has a chance to see
it, then authentication would take place, etc.

I think this has to be in a Valve, and it has to happen before
the AuthenticatorValve sees the request. Do you see a way around
using a Valve? Assuming that such a Valve would be required, I
think we should provide one with Tomcat. I'd be happy to write
such a Valve.

Can't comment because I don't know what exactly the "saved
request" feature means, resp. how it works.

Well, the lb of course doesn't know if the session is valid. If it's
valid, the request *should* go to the requested node (sticky). If the
session is invalid, it should be re-balanced. Only the node itself can
determine the validity of the session.

Since the container's authenticator Valve intercepts requests to
protected resources in the above step #1 and takes-over, no Filter can
be used to intercede and redirect (to cause a re-balance to occur)
before step 2 occurs which defeats the whole process.

I'm working on a Filter that will work in our environment (we use
SecurityFilter, which, being a Filter, can be subverted by an
earlier-executing Filter). Once I have that proof-of concept, I'll
propose it as a new Valve available but not enabled by default.

Regards,

Rainer

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to