The Blog

Tomcat secure sessions with AJP and HTTP protocols using Apache proxy balancer

published May 20, 2011

During this week I have been digging into Apache AJP protocol and how it communicates with Tomcat. Especially, I have been interested in knowing how SSL works in different Tomcat connector setups. Once again in hindsight everything is so clear but had many desperate moments during these days :) Anyways, you should first check mod_proxy balancer configurations, and ajp protocol.
Basically, I have tried two different solutions:

1) Apache proxy balancer using HTTP


<Proxy balancer://liferaycluster>
BalancerMember http://node1 route=node1
BalancerMember http://node2 route=node2
ProxySet stickysession=ROUTEID
</Proxy>

2) Apache proxy balancer using AJP


<Proxy balancer://liferaycluster>
BalancerMember ajp://node1 route=node1 ping=3
BalancerMember ajp://node2 route=node2 ping=3
ProxySet stickysession=ROUTEID
</Proxy>

Benefit of using AJP is the “ping” setting which Apache uses to check whether Tomcat is up or down and does more sophisticated load balancing based on that information. However, things become more tricky in Tomcat connector configurations when checking whether JSESSION will end up to be “secure” or “not”. In HTTP connector you can define secureness of your connector like so:


<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="false" scheme="https" secure="true" proxyPort="443"/>

But if you define connector with AJP protocol:

<Connector port="8443" protocol="AJP/1.3" SSLEnabled="false" scheme="http" secure="false" proxyPort="443"/>

“Secure” setting will not have any effect to JSESSIONID cookie. However, it does have an effect to function call “ServletRequest.isSecure()”. Go figure.

It seems that AJP protocol contains boolean value wheter initial connection to Apache was secure or not and that value is passed to Tomcat which uses that information to create user session cookie.

proxy-ajp

If you need an AJP setup where JSESSIONID should not be secure even if initial connection was through HTTPS you can do Apache haxing to remove Secure setting from cookie like so:

Header edit "Set-Cookie: JSESSIONID=" Secure " "

This will replace word Secure with empty string from JSESSIONID cookie. This is not the most clever thing to do because now your session is open for hijacking.

Samuli @ 10:53 (No Comments)

tags: ,

Trackback URL

One click deployment to clustered Liferay with Jenkins

published April 27, 2011

During last year, we have built pretty cool one click deployment system with Jenkins. Environment has been created little by little, but when decision was made to cluster Liferay and in addition to serve our 15.000 lines of  JavaScript code from Apache, it was pretty clear that manual installations would be a pain.  So we built environment like this:

one-click-install

Developer can initiate (1) portlet build from the Jenkins CI GUI. Jenkins runs (2) Ant build, unit tests,  selenium tests and builds multiple war packages. If build is successful Jenkins first copies (3) our JavaScript framework to Apache and then deploys (4) portlet wars to clustered liferay environment. All this with one click.

Configuration is pretty simple, each copy step is run as a shell script like so:

./deployFrameworkToApache.sh;
./deployPortletsToNode1.sh;
./deployPortletsToNode2.sh;

I have to give credit to Jenkins, it is really easy to configure.

Samuli @ 19:04 (No Comments)

tags: ,

Trackback URL

Liferay clustering checklist

published April 21, 2011

Yesterday our team finished clustering of our Liferay portal environment. In case I have to do this again, I decided to write a small checklist what should be taken into a consideration when clustering. Official Liferay clustering guide can be found here.

Updated May 6, 2011: added ajp configurations.

Updated May 20, 2011: Check also how Apache AJP protocol works.

1. Ensure that you have same JRE/JDK on both cluster nodes.

2. Check that all Liferay nodes can access your database (or database cluster) and it allows connections from these hosts.

3. Ensure that load balancer can forward requests to both nodes and firewalls won’t block access.

4. Check that all of  cluster nodes are able to send email using your configured SMTP server (e.g. with telnet).

5. Install same version of Liferay to all cluster nodes.

6. If you need session fail over, configure that.

7. Configure tomcat connectors “secure” and “unsecure” to use ajp protocol like so:

<Connector address="xx.xx.xx.xx" port="8009" protocol="AJP/1.3" ... />
<Connector address="xx.xx.xx.xx" port="8443" protocol="AJP/1.3" ... />

8. Configure properties “net.sf.ehcache.configurationResourceName” and “ehcache.multi.vm.config.location” correctly to portal-ext.properties.

9. Move your Lucene index to database or if that is not possible, to shared NFS mount. In case of shared NFS mount, linux UIDs for Liferay user must be equal in all nodes so that Linux file permissions work correctly.  Liferay user must be able to write to Lucene directory.

10. Move your document library to shared NFS mount or to database. Same rules apply as above.

11. Ensure that your configured Lucene index and document library paths point to correct location in portal-ext.properties.

12. Start your cluster nodes.

13. Check that mod_proxy_ajp module is loaded by apache.

14. Configure Apache  load balancer like so:
Proxy balancers:

<Proxy balancer://liferaycluster>
BalancerMember ajp://node1:8009 route=node1 ping=5 loadfactor=1
BalancerMember ajp://node2:8009 route=node2 ping=5 loadfactor=1
ProxySet stickysession=ROUTEID
</Proxy>


<Proxy balancer://secureliferaycluster>
BalancerMember ajp://node1:8443 route=node1 ping=5 loadfactor=1
BalancerMember ajp://node2:8443 route=node2 ping=5 loadfactor=1
ProxySet stickysession=ROUTEID
</Proxy>

This inside your :80 virtual host:

Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
ProxyPass /group balancer://secureliferaycluster/group/
ProxyPassReverse /group balancer://secureliferaycluster/group/
ProxyPass / balancer://liferaycluster/
ProxyPassReverse / balancer://liferaycluster/

This inside your :443 virtual host:

ProxyPass /group balancer://secureliferaycluster/group/
ProxyPassReverse /group balancer://secureliferaycluster/group/
ProxyPass / balancer://liferaycluster/
ProxyPassReverse / balancer://liferaycluster/

That’s it!

Samuli @ 20:31 (No Comments)

tags:

Trackback URL

Liferay – Deployment will start in a few seconds

published October 7, 2010

And then nothing happens. I Run into this situation today, where first deployment of portlet works just fine but then following ones do nothing. Tomcat catalina.out just says:

11:07:47,534 INFO  [PortletAutoDeployListener:87] Portlets for [..war..] copied successfully. Deployment will start in a few seconds.

No files copied, no nothing. This is how I got it working (try step 5. first as it seems to solve problem):

1. Turn on Liferay debugging from admin console
Now log keeps saying:
11:58:09,542 DEBUG [BaseExplodedTomcatListener:138] [..war..] does not have a matching extension

2. Turn on Tomcat logging by setting “org.apache.catalina.level = ALL” to Tomcat /conf/logging.properties
There is loads of stuff, did not find anything interesting.

3. See what files are modified after deploy
-bash-3.2$ find . -mmin -2
./liferay/deploy
./liferay/tomcat-6.0.18/logs/catalina.out
./liferay/tomcat-6.0.18/logs/catalina.2010-10-07.log

Now that is weird. “Deployed” war just vanishes after copying.

4. Start removing stuff from war
Removed web.xml, liferay-portlet.xml, portlet.xml etc. from war file. Nothing helped.

5. Made a complete copy of a war and deployed that
Now this seems to work! After this I deployed old version. Also that worked now fine!?
Really weird, but that copy seemed to somehow solve the problem.

Environment:
Red Hat Enterprise Linux Server release 5.5 (Tikanga)
Java(TM) SE Runtime Environment (build 1.6.0_13-b03)
Liferay 5.2.2 CE
Tomcat 6.0.18

Is someone knows why it works this way, please comment…

Samuli @ 13:00 (No Comments)

tags:

Trackback URL

New version of Paikkatietoikkuna.fi released

published June 4, 2010

Yesterday, National Land Survey of Finland released a new version of paikkatietoikkuna.fi. Service contains (amongst other things) a google-maps-like rich internet application built on top of Liferay. This portal relies heavily on JavaScript frameworks such as OpenLayers, Ext JS and JQuery.  There are some important things to consider when building a RIA and I am planning to write a separate blog about it later. In the mean time, go check:

http://www.paikkatietoikkuna.fi/ and
http://www.paikkatietoikkuna.fi/web/fi/kartta

Samuli @ 18:15 (No Comments)

tags: ,

Trackback URL

mod_rewrite 101

published May 11, 2010

mod_rewrite is a popular apache module for rewriting urls and making redirects. I decided to write small introduction about it, since I had to configure it on project test server. Reason why we are using it, is our upcoming release in which we will need to forward our customers to new liferay community instead of default /web/guest.

So, after rewrite engine is turned on from apache configuration, it will filter all matched requests and apply special rules for it. Request is passed through this chain of rules and if match is found, redirect is sent by apache.

mod_rewrite’s work horses are RewriteRule and RewriteCond directives which are used to build rule chain. Processing works so that

1) config files and .htaccess files are parsed for RewriteRules from top to bottom.
2) First RewriteRule is selected and request is compared against RewriteRule’s pattern.
3) If match is found and there are RewriteCond elements introduced, they are tested against the request.
4) If conditions match, redirect to new rewritten url is made.
5) If there was no match, request is passed onwards to next RewriteRule in chain, if such exist.

rewrite

Configurations
Below is a simple configuration for redirect

# Turn on rewrite engine processing
RewriteEngine On
# Write log here
RewriteLog /var/log/httpd/rewrite.log
# Log everything
RewriteLogLevel 9
# Redirect context root to new community /web/new/
RewriteRule ^/$ http://%{SERVER_NAME}/web/new/

# Do not allow access to old /web/guest
RewriteRule ^/web/guest(.*)$ http://%{SERVER_NAME}/web/new/

There is loads of stuff you can do with this module, check it out.

Samuli @ 19:59 (One Comment)

tags: ,

Trackback URL

Liferay front-end performance tuning

published February 1, 2010

As we are getting over 200 000 unique visitors on a busy day, we really must take a full advantage of the Liferay caching. In order to understand caching in Liferay 5.2.3, I had to dig pretty deep. There are multiple ways to increase the portal performance but the focus here will be the Liferay front-end.

The Liferay front-end caching is based on servlet filters and it basically provides four ways to enhance performance:

  • HTTP header based caching
  • Ehcache based caching
  • HTTP response Minifier
  • HTTP response gzipping

liferay-filters

HTTP Header based caching

Liferay provides “com.liferay.portal.servlet.filters.header.HeaderFilter”, which can be used to manipulate HTTP cache-control. This your first line of defence.

Ehcache based caching

This one is the most tricky one. By default, Liferay provides “com.liferay.portal.servlet.filters.cache.CacheFilter” for caching requests in the Ehcache. Unfortunately, Ehcache configurations are buried deep. If you have Liferay source code, you should take a peek of files under “/portal/portal-impl/src/ehcache/”, especially files with a “liferay” prefix. Usage of these configuration files are configured in a portal.properties file using keys “ehcache.single.vm.config.location” and “ehcache.multi.vm.config.location”.

Underneath, The CacheFilter uses named cache “com.liferay.portal.servlet.filters.cache.CacheUtil” to store responses, which has time to live set to a one hour. The CacheFilter constructs cache keys like “HTTP:///WEB/FI/FRONTPAGE?NULL#FI_FI#OTHER#TRUE” from the request and stores rendered bytes into the Ehcache for later use.

It is configured to include specific resources in web.xml. If you need custom caching for your pages, you can do it with the CacheFilter.

HTTP response gzipping

By gzipping the response, round trip response time will be shorter. See a sample on SteveShoulds.com. Gzipping is done using class “com.liferay.portal.servlet.filters.gzip.GZipFilter”.

HTTP response Minifier

Liferay provides “com.liferay.portal.servlet.filters.minifier.MinifierFilter” which can be used to minify responses. It uses Yahoo’s YUI Compressor to compress e.g. css and javascript files.

Configurations

All filters are defined in web.xml and are already pre-configured. In order to change configurations you will need Liferay source code. It is easier to understand configurations by looking at the code.

Samuli @ 21:48 (2 Comments)

tags: ,

Trackback URL

Subscribe to RSS feed

The Tag Cloud
The Blog Archive

February 2012 (1)

January 2012 (1)

November 2011 (1)

June 2011 (2)

May 2011 (1)

April 2011 (2)

March 2011 (2)

February 2011 (1)

January 2011 (1)

December 2010 (1)

November 2010 (1)

October 2010 (3)

September 2010 (3)

August 2010 (5)

July 2010 (2)

June 2010 (3)

May 2010 (4)

April 2010 (2)

March 2010 (6)

February 2010 (7)

January 2010 (3)

December 2009 (7)

November 2009 (6)