Componentix logo

Componentix blog

Here we write some random thoughts and articles about software development: Java, Grails, Node.js, iPhone, iPad and more.

Resolving problem with SSL certificates in OpenJDK

When configuring new server at linode hosting I decided to try to use OpenJDK to run Tomcat. The system I installed was Debian Lenny.

It all went smoothly and I didn’t have any problems, until I tried to deploy application using Facebook API. I immediately have faced a problem when trying to login using Facebook – javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated.

Application was based on Grails and used HTTPBuilder to do requests. It uses Apache HttpClient under the hood.

Googling revealed some workarounds for the problem, but they all required hacking some additional code to override SSL certificates validity checks. The matter was that the same exception appeared when one tried to connect to server with self-signed certificate.

However it wasn’t the case for me as Facebook’s certificate wasn’t self-sgned of course :) So I dug further. It turned out that for some reason OpenJDK in Debian Lenny was installed without any CA (certificate authorities). Also it looked like Debian Squeeze solves this problem.

So the solution for Debian Lenny is simple – download and install appropriate packages from Squeeze . Good news is that certificates come in their own packages:

wget "http://ftp.us.debian.org/debian/pool/main/c/ca-certificates/ca-certificates_20090814+nmu2_all.deb"
sudo dpkg -i ca-certificates_20090814+nmu2_all.deb
wget "http://ftp.us.debian.org/debian/pool/main/c/ca-certificates-java/ca-certificates-java_20100412_all.deb"
sudo dpkg -i ca-certificates-java_20100412_all.deb

ca-certificates is available in Lenny, but it needs to be updated as a dependency to ca-certificates-java

Hosting Grails web applications using Tomcat and Nginx (our configuration)

Intro

To host Grails web application Apache Tomcat is quite a natural choice. However a big part of what is served are just static files (images, CSS, javascript code) or rarely changed pages. Using Tomcat to serve all that stuff would be a waste of resources, especially when running on small VPS like we do.
Nginx is a small efficient web server that helps with this task. It can be setup to both serve static files and act as reverse proxy to Tomcat running the Grails application. Even more, it can cache some of the web pages so that it sends less requests to Tomcat.
In this post I’ll highlight important parts of Nginx and Tomcat configuration files needed to achieve setup like this.

Nginx configuration

For Nginx configuration you can start from its default config file. Let’s add following to http section:

# Enable GZip compression
gzip                on;
gzip_http_version   1.1;
gzip_min_length     1000;
gzip_buffers        16 8k;
gzip_disable        "MSIE [1-6] \.";
gzip_types          text/html text/css text/xml application/x-javascript application/atom+xml text/plain
gzip_vary           on;

# Set proxy cache path
proxy_cache_path /var/nginx/cache keys_zone=one:10m;

# Main website Tomcat instance
upstream main {
    server localhost:8080;
}

This will enable GZip compression, set the path which we want to use for cache and set the Tomcat instance to proxy requests into.

Then it is needed to specify the configuration for the particular server. For this example virtual domain names will be used to define server (as it is most common case).

# iPad Sketchbook website server
server {
    listen       80;
    server_name  www.ipadsketchbook.com;

    # Redirect to appropriate language
    location = / {
        set_from_accept_language $lang en ru;
        rewrite ^/$ /$lang redirect;
    }

    location / {
        # Rewrite the URL for logged-in users
        if ($http_cookie ~* "JSESSIONID=([A-Z0-9]*)") {
            rewrite ^(.*)$ /loggedIn$1 last;
        }

        # Proxy all the requests to Tomcat
        proxy_pass	http://main;
        proxy_set_header  Host $http_host;

        proxy_cache one;
        proxy_cache_min_uses 1;
        proxy_cache_valid  200 302 1m;
        proxy_cache_valid  404 1m;
        proxy_cache_use_stale error timeout invalid_header http_500 http_502 http_503 http_504;
    }

    # Content for logged-in users should not be cached
    location /loggedIn {
        # Rewrite URL back before forwarding request to backend
        rewrite ^/loggedIn(.*)$ $1 break;

        proxy_pass	http://main;
        proxy_set_header  Host $http_host;

        if ($http_cookie !~* "JSESSIONID") {
            return 404;
        }
    }

    # Admin panel should not be cached
    location /admin {
        proxy_pass	http://main/admin;
        proxy_set_header  Host $http_host;
    }

    # Serve static resources
    location ~ ^/(images|css|js|google|yandex|y_key) {
        root /var/www/vhosts/ipadsketchbook.com/httpdocs;
    }

    error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }
}

There are several tricks in the aforementioned config:

  • it redirects to appropriate language version based on request headers. It is only possible if you install nginx_accept_language_module.
  • it rewrites request path for users with existing session (based on JSESSIONID cookie), so that the pages served for them aren’t cached
  • it then rewrites back request path (for logged in users), when issuing request to Tomcat
  • there is a list of path prefixes for which static files are served

Of course most likely there should also be redirect from other domains set up. It is quite easy to do:

# Redirect all the iPhone Sketchbook website domains to main domain
server {
    listen          80;
    server_name     ipadsketchbook.com ipad-sketchbook.com www.ipad-sketchbook.com;
    rewrite ^(.*)   http://www.ipadsketchbook.com$1 permanent;
}

Tomcat configuration

Of course in addition to configuring Nginx, Tomcat itself need to be configured. This is quite straightforward though. Note that only single domain name is configured, as other ones are redirected by Nginx.

So in server.xml we have such host configuration:

<Host name="www.ipadsketchbook.com" appBase="vhosts/ipadsketchbook.com"
    unpackWARs="true" autoDeploy="false"
    xmlValidation="false" xmlNamespaceAware="false">
</Host>

Note that autoDeploy is disabled, it is not needed on production server. appBase specifies the path to directory which contains applications' WAR files. Root of the domain would be served by ROOT.WAR.

Further reading

Following e-mail is only for robots (never send anything to it, or you would be blacklisted): botsonly@componentix.com