1

Currently, I am using mod_proxy_ajp to load balance requests to multiple Tomcat servers. Callers are authenticated using client certificates and an ACL. This has been working fine, callers are routed to either server at an even rate. Now I want to pre-fetch and cache data per user, so each Tomcat server will serve a known set of users. So I'm thinking it would be fine to split caller's alphabetically; user's with a names (CN or DN) starting with A-M go to server 1, users with names starting with [N-Z] go to server 2. But I don't know how to make this work.

Currently I have:

<Proxy balancer://lb-service>
   BalancerMember ajp://svr1:8009 loadfactor=1 
   BalancerMember ajp://svr2:8009 loadfactor=1 
</Proxy>

<Location /lookup>
   ProxyPass balancer://lb-service/lookup
</Location>

I'm considering using mod_rewite to change the URL to include the user's CN but I can't seem to make that work.

I'm pretty much stuck using apache mod_proxy_ajp and Tomcat for load balancing. I can change the services running the Tomcat to accept different requests.

Anyone have a good way to do this?

John in MD
  • 221
  • 2
  • 9

1 Answers1

1

Interesting idea for caching! Let's see if we can get it working..

I've got a couple of options: one that would be more efficient but I really have no idea whether it will work, and one that's less efficient but I'm reasonably sure will work just fine.

The issue with the more efficient option (and, more generally, using something like the client cert to determine load balancer assignment) is that as far as I know there's no way to change mod_proxy_balancer's mind on node assignment after the request has come in - there's probably some creative hackery that could make it happen, but I'm not coming up with any better ideas off the top of my head. The crux of this is that it's doing a redirect to the same location that it's at, in order to have the cookie in place for the first request that gets proxied, to get the right route - and I'm not sure if that will work. Worth a shot:

RewriteEngine On

RewriteCond %{ENV:BALANCER_ROUTE_CHANGED} 1
RewriteCond %{SSL:SSL_CLIENT_S_DN} ^[a-m] [NC]
RewriteRule ^(.*)$ /.$1 [R,CO=ROUTEID:route.1:.yourdomain.com]

RewriteCond %{ENV:BALANCER_ROUTE_CHANGED} 1
RewriteCond %{SSL:SSL_CLIENT_S_DN} ^[n-z] [NC]
RewriteRule ^(.*)$ /.$1 [R,CO=ROUTEID:route.2:.yourdomain.com]

<Proxy balancer://lb-service>
    BalancerMember ajp://svr1:8009 loadfactor=1 route=1
    BalancerMember ajp://svr2:8009 loadfactor=1 route=2
    ProxySet stickysession=ROUTEID
</Proxy>

<Location /lookup>
   ProxyPass balancer://lb-service/lookup
</Location>

If that doesn't work, then here's plan B; just match the client certificate's DN every time. Adding extra overhead to each request, but it shouldn't be too bad of a performance impact overall.

<Proxy */lookup>
    RewriteEngine On

    RewriteCond %{SSL:SSL_CLIENT_S_DN} ^[a-m] [NC]
    RewriteRule ^/lookup((?:/.*|))$ ajp://svr1:8009/lookup$1 [P,L]

    RewriteCond %{SSL:SSL_CLIENT_S_DN} ^[n-z] [NC]
    RewriteRule ^/lookup((?:/.*|))$ ajp://svr2:8009/lookup$1 [P,L]

    # This will take care of any other orphan requests (say, those with no client
    # cert) - we'll just lump those on server 1; if there's a lot then we can do
    # something else to balance randomly (like just leave the current config there)
    RewriteRule ^/lookup((?:/.*|))$ ajp://svr1:8009/lookup$1 [P,L]
</Proxy>

I've got this matching for a trailing slash after /lookup to avoid a nasty security vulnerability where an attacker could get to your internal network - if that won't work for your application then we can work around it.

John in MD
  • 221
  • 2
  • 9
Shane Madden
  • 112,982
  • 12
  • 174
  • 248
  • I'm trying to make option #2 work but so far I keep getting a Not_Found (404) when I try to access /lookup. I've turned on some logging (RewriteLog) and it seems like the RewriteCond is not matching. It's like the SSL_CLIENT_S_DN variable is not set, yet I'm sure the client cert is being passed because I check it in the Tomcat app. – John in MD Jan 03 '12 at 20:40
  • @JohninMD Do you access just `/lookup` or is there anything after it? I've got the rules in option 2 set up to look for `/lookup/*` instead of `/lookup`, to avoid a security vulnerability. But if you do need `/lookup` working, I can adjust the rules. – Shane Madden Jan 03 '12 at 20:46
  • just /lookup. One change I've made is to use SSL_CLIENT_S_DN_CN to get just the common name and not the entire DN. – John in MD Jan 03 '12 at 21:01
  • In my error_log, I'm seeing the message "File /data/www/html/lookup does not exist" in the error log. Seems to be the source of the 404 but I don't understand why it's looking there. – John in MD Jan 03 '12 at 21:29
  • @JohninMD Just needed an uglier regex to allow for that path. Edited, try it now. – Shane Madden Jan 03 '12 at 21:45
  • I tried your changes but I'm still getting a 404. I don't think the RewriteCond at hitting but I am seeing the caller's SSL_CLIENT_S_DN_CN in the ssl_log output. I think I must be missing something. I'm heading out but will try again tomorrow. +1 for your help. – John in MD Jan 03 '12 at 22:33
  • @JohninMD Sounds good - let's try turning up some logging to see what's going on. Set a `RewriteLog` directive to a file location, and set `RewriteLogLevel 9`, then restart Apache and attempt a request. That log should give us all the info we need. – Shane Madden Jan 03 '12 at 22:36
  • let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/2141/discussion-between-john-in-md-and-shane-madden) – John in MD Jan 04 '12 at 16:20
  • @ShareMadden I could not get option #1 to work. It turned out that option #2 was better for what we wanted to do anyway. I made small edits to option #2 to get it to work. Thanks. John – John in MD Jan 06 '12 at 15:42