Skip to main content

Maven Note: Securing a temporary Jetty instance in the jetty-maven-plugin

One of my tasks for the current iteration was to add security constraints to the J2EE web service that we are currently developing. This is the easy part. Simply define the appropriate security-constraint, login-config, and security-role elements in the project's web.xml.

web.xml

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd">
  <display-name>...</display-name>
  <servlet>...</servlet>
  <servlet-mapping>...</servlet-mapping>

  <security-constraint>
    <display-name>deny unauthorized users</display-name>
    <web-resource-collection>
      <web-resource-name>global</web-resource-name>
      <url-pattern>/</url-pattern>
      <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>AUTHORIZED_USER</role-name>
    </auth-constraint>
    <user-data-constraint>
      <!-- All access to this area will be SSL protected -->
      <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
  </security-constraint>

  <login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>APP</realm-name>
  </login-config>

  <security-role>
    <description>authorized user for application</description>
    <role-name>AUTHORIZED_USER</role-name>
  </security-role>
</web-app>

From the above, you can see that I defined an expected role, AUTHORIZED_USER, an expected realm for http basic authentication, APP, and a set of resources, / and /*, which can only be accessed through SSL by a user who is a member of the AUTHORIZED_USER role. This is the easy part and should work for most application servers which are worth their salt.

Enter jetty-maven-plugin

This project is already using the jetty-maven-plugin to run a test instance of the application. I thought it would be a good idea to make sure that the security on the localhost instance for testing would work in the same manner as the WebSphere server to which the application is to be deployed. This would help me to ensure that I, as a lazy programmer, would not have to change the functional tests (SoapUI) between localhost and dev.

By default, the jetty instance created by executing mvn jetty:run-war does not include a user realm for defining users and groups, a login service for handling attempted logins, or even the capability for handling SSL connections. In order to bring these components into the localhost instance, I had to make some changes to the configuration of my project's jetty-maven-plugin. First, the changes for enabling the user realm and login service.

pom.xml (version 1)

<project xmlns="http://maven.apache.org/POM/4.0.0"
  ...
  <build>
    <plugins>
      ...
      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>7.3.0.v20110203</version>
        <configuration>
          ...
          <userRealms>
            <userRealm implementation="org.mortbay.jetty.security.HashUserRealm">
              <name>APP</name>
              <config>${project.build.directory}/test-classes/jetty-users.properties</config>
            </userRealm>
          </userRealms>
          <loginServices>
            <loginService implementation="org.eclipse.jetty.security.HashLoginService">
              <name>APP</name>
              <config>${project.build.directory}/test-classes/jetty-users.properties</config>
            </loginService>
          </loginServices>
        </configuration>
      </plugin>
      ...
    </plugins>
    ...
  </build>
  ...
</project>

/src/test/resources/jetty-users.properties

test_user=test_user,AUTHORIZED_USER

Using the keytool-maven-plugin for generating a certificate for SSL

In order to enable server authentication, the Jetty instance needs to have access to a server certificate to be sent out in the SSL handshake. I did some investigation and found that there was a plugin, keytool-maven-plugin, which would allow you to automate self-signed certificate generation in the maven execution. I modified the project's pom as follows:

pom.xml (version 2)

<project xmlns="http://maven.apache.org/POM/4.0.0"
  ...
  <build>
    <plugins>
      ...
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>keytool-maven-plugin</artifactId>
        <executions>
          <execution>
            <phase>generate-resources</phase>
            <id>clean</id>
            <goals>
              <goal>clean</goal>
            </goals>
          </execution>
          <execution>
            <phase>generate-resources</phase>
            <id>genkey</id>
            <goals>
              <goal>genkey</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <keystore>${project.build.directory}/jetty-ssl.keystore</keystore>
          <dname>cn=active-active.blogspot.com</dname>
          <keypass>jetty6</keypass>
          <storepass>jetty6</storepass>
          <alias>jetty6</alias>
          <keyalg>RSA</keyalg>
        </configuration>
      </plugin>
      ...
    </plugins>
    ...
  </build>
  ...
</project>

Adding SSL support to Jetty

Now, we have a way for the client to authenticate itself, a realm for assigning that client's roles, and a keystore. We just need to tell Jetty how to expose an SSL port to the world.

pom.xml (version 3)

<project xmlns="http://maven.apache.org/POM/4.0.0"
  ...
  <build>
    <plugins>
      ...
      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>7.3.0.v20110203</version>
        <configuration>
          ...
          </userRealms>
          <connectors>
            <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
              <port>8080</port>
              <maxIdleTime>60000</maxIdleTime>
            </connector>
            <connector implementation="org.eclipse.jetty.server.ssl.SslSocketConnector">
              <port>8443</port>
              <maxIdleTime>60000</maxIdleTime>
              <keystore>${project.build.directory}/jetty-ssl.keystore</keystore>
              <password>jetty6</password>
              <keyPassword>jetty6</keyPassword>
            </connector>
          </connectors>
          <loginServices>
          ...
        </configuration>
      </plugin>
      ...
    </plugins>
    ...
  </build>
  ...
</project>

A big thank-you to mrhaki and his article Configure Maven Jetty Plugin for SSL Communication for the assist.

Comments

Popular posts from this blog

Using MonoDevelop to Create an ASP.NET Web Service

NOTE: instructions below are for MonoDevelop 2.6 Beta 2 - built on 2011-04-06 03:37:58+0000Getting StartedCreate a new ASP.NET Web Application in MonoDevelop: From the menu, select: File → New → Solution…Expand C#.Select ASP.NET → Web Application.Enter a name for the ASP.NET project that will be created in the solution in Name:.Change the root location for the solution in Location:, if desired.Change the name of the root solution in Solution Name:, if desired.The Results – IWhat you have after executing the new ASP.NET Web Application project wizard is a solution containing one ASP.NET Web Application project. In the default project view in MonoDevelop, you'll find the following items: Default.aspx – This is the default web form rendered and presented in the browser when http://<server>:<port>/ is accessed. Default.aspx.cs – This C# file contains the developer-created common code and event handlers which can be used to affect the processing of the form.Default.aspx.des…

Testing Toolbelt: SpringJUnit4ClassRunner

The org.springframework.test.context.junit4.SpringJUnit4ClassRunner class is another implementation of the JUnit TestRunner class which is used to enable various features of Spring for every run of the test class and every test within it. To use the features provided by the SpringJUnit4ClassRunner class, you need to mark the class using the RunWith annotation using SpringJUnit4ClassRunner as its parameter.In addition to the custom test runner, you will want to mark the class with the ContextConfiguration annotation. The ContextConfiguration annotation is used to mark classes which will automatically read a Spring configuration file and use it to create an ApplicationContext. By default, this file located at <package path>/<test class name>-context.xml. Use the locations argument to over-ride.The ApplicationContext used by the Spring-integrated test will only be loaded once for the whole test class. This behavior can be over-ridden by annotating a test method with the Dirti…