Wednesday, October 30, 2013

On Security: From mod-jk to mod-proxy

Mod_jk is fast, binary protocol which has allowed us to scale Tomcat server java applications for years. However to encrypt the traffic mod-jk configuration will get messy.

Mod_proxy on the other hand is a little bit slower because it works directly on HTTP connectors but being a proxy to regular HTTP(s) it supports encrypted traffic in an easier to configure way.

And since the overhead is really nothing nowadays with the high speed LAN there is no reason that would stop you from migrating from an insecure mod_jk configuration to an encrypted mod_proxy configuration.

Here is what it takes to get your tomcat servers to get the traffic (very simple but fully functional example). Of course as usual there are tons of configuration options for mod_proxy you might need for your needs. All I am presenting here is how to get sticky sessions work. Note that certificates need to be hosted in tomcat (besides apache) as well. In addition note the jvmRoute attribute is still needed the same way it is for modjk.

I am assuming you are using the Apache Portable Runtime (APR) library. Keep in mind that you must not have any warnings about APR. This for example would be a no-no (solve that first installing the library and making sure tomcat library path can reach it):
Oct 31, 2013 8:40:47 PM org.apache.catalina.core.AprLifecycleListener init
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
Read more about this issue here.

In tomcat server.xml
       <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
               maxThreads="200" scheme="https" secure="true"
               SSLCertificateFile="/opt/tomcat/certs/sample.com.crt"
               SSLCertificateKeyFile="/opt/tomcat/certs/sample.com.key"
               clientAuth="false" sslProtocol="TLS" />
        
       <Engine name="Catalina" defaultHost="localhost" jvmRoute="sample-app1">
       ...
          <Host name="sample.com"  appBase="sample-app"
          unpackWARs="true" autoDeploy="true"
          deployonstartup="true"/>
        </Engine>        
    
Enable the necessary modules in apache:
$ sudo a2enmod proxy_http
$ sudo a2enmod proxy_balancer
In apache virtual host few things change like everything related to "proxy" and the elimination of any jk directive. Note for example how for static resources we make exceptions through the use of an exclamation mark so those are served directly from Apache instead of Tomcat (instead of the Alias/SetEnvIf Request_URI + no-jk sentences needed for mod_jk).
<VirtualHost sample.com:443>
 ...
 #Make sure Proxy errors are not served back to the user (I use a common page for all 500 errros here)
 ErrorDocument 500 /html/error/503.html
 ...
 SSLProxyEngine on
 ProxyPreserveHost on
 ProxyRequests off
 <Proxy balancer://sample-app>
    ProxySet lbmethod=bybusyness
    BalancerMember https://tomcat1.sample.com:8443 route=sample-app1
    BalancerMember https://tomcat2.sample.com:8443 route=sample-app2
 </Proxy>
ProxyPass /images !
ProxyPass /js !
ProxyPass /css !
ProxyPass /html !
ProxyPass / balancer://sample-app/ stickysession=JSESSIONID|jsessionid scolonpathdelim=On
...
</VirtualHost>

Do not forget to restart apache. To test this configuration you can sniff the traffic now in Apache:
$ sudo tcpdump -i eth0 -X port 8443 > /tmp/del
Then hit the service from curl targettimg a particular cluster node (using -k in case you use a self-signed certificate):
$ curl -i -b "JSESSIONID=.sample-app1" -k "https://sample.com?password=cleartext"
Now inspect the results in apache and you should see no hits for password:
$ grep password /tmp/del

No comments:

Followers