Tuesday, April 30, 2019

Securing the Apache Camel REST DSL

Recently I put together a simple test-case for Apache Camel's REST DSL and realised that it illustrated quite a few security concepts, as well as various Camel components, that might be interesting to blog about. The test-case is a simple spring-based project, which is available on github here:
  • camel-jetty: A test-case for the Camel Jetty component, TLS, the REST DSL + Jasypt.
In particular, the Camel spring configuration is here. Let's take a look at the different pieces one by one.

1) The Apache Camel REST DSL

Apache Camel offers a REST DSL which makes it really easy to create a simple REST service.
Here we are creating a simple REST service on the "/data" path that produces an XML response when invoked with a GET request. The actual functionality is delegated to a Camel route called "direct:get":

Here we are reading in some files from a directory using the Camel File component and using "pollEnrich" to include the contents of that directory into the message that is returned to the user. Finally we need to tell Camel how to create the REST DSL. Camel supports a wide range of components, but for the purposes of this example we are using the Camel Jetty component:

Note the port is not hard-coded, but instead retrieved from a randomly generated property in the pom using the "reserve-network-port" goal of the "build-helper-maven-plugin".

2) Getting TLS to work with the Camel REST DSL

To support TLS with the Camel REST DSL, we need to set the scheme to "https" as above in the "restConfiguration". The REST configuration also refers to a property called "sslContextParameters", which is where we obtain the keys required to support TLS. See the Camel JSSE documentation for more information on this property.

The sslContextParameters bean definition allows us to define the key managers and trust managers for the TLS endpoint by referring to Java keystore files with the relevant passwords. If we are not supporting client authentication, the trustManagers portion can be omitted.

3) Using Jasypt to decrypt keystore passwords for use in TLS

Note above that we have not hard-coded the TLS keystore passwords in our Camel spring configuration, but are instead retrieving them from a property. Camel offers the ability to store the passwords in encrypted form, by using the Camel Jasypt component to decrypt them given a master password. The encrypted passwords themselves are stored in a passwords.properties file:

These encrypted passwords are obtained using the camel-jasypt jar (shipped for example in the Camel distribution):
  • java -jar camel-jasypt-2.23.1.jar -c encrypt -p master-secret -i storepass
To decrypt the passwords at runtime, we define the following bean in our Camel spring configuration:
This retrieves the master password from a system property. For the purposes of this demo, the password is set as a system property in the "maven-surefire-plugin" defined in the pom.

4) Invoking on our secured REST service using the Camel HTTP4 component

The demo also includes a client route which invokes on the secured REST service we have created. We use the Camel HTTP4 component for this:

We start the route using the Camel Timer component, before calling the HTTP4 component. As we have included a query String in the request URI, the http4 component will issue a GET request. As for the REST service, we need to configure the TLS keys using the "sslContextParameters" parameter.

On the client side we only need the trustManagers configuration, unless of course we want to support client authentication. For the purposes of this demo, we also need to configure a custom x509HostnameVerifier property. This is because the TLS certificate the service is using will not be accepted by the client by default, as the common name of the certificate does not match the domain name of the service. We can circumvent this  (for testing purposes only, it is not secure!) by using the following hostname verifier:

Finally we log the service response to the console so we can see the test-case working.

Friday, April 5, 2019

Performance gain for web service requests in Apache CXF

In this post I want to talk about a recent performance gain for JAX-WS web service requests I made in Apache CXF. It was prompted by a mail to the CXF users list. The scenario was for a JAX-WS web service where certain requests are secured using WS-SecurityPolicy, and other requests are not. The problem was that the user observed that the security interceptors were always invoked in CXF, even for the requests that had no security applied to the message, and that this resulted in a noticeable performance penalty for large requests.

The reason for the performance penalty is that CXF needs to convert the request into a Document Object Model to apply WS-Security (note there is also a streaming WS-Security implementation available, but the performance is roughly similar). CXF needs to perform this conversion as it requires access to the full Document to perform XML Signature verification, etc. on the request. So even for the insecure request, it would apply CXF's SAAJInInterceptor. Then it would iterate through the security headers of the request, find that there was none present, and skip security processing.

However when thinking about this problem, I realised that before invoking the SAAJInInterceptor, we could check to see whether a security header is actually present in the request (and whether it matches the configured "actor" if one is configured). CXF makes the message headers available in DOM form, but not the SOAP Body (unless SAAJInInterceptor is called). If no matching security header is available, then we can skip security processing, and instead just perform WS-SecurityPolicy assertion using a set of empty results.

This idea is implemented in CXF for the 3.3.2 release via the task CXF-8010. To test what happens, I added a test-case to github here. This creates a war file with a service with two operations, one that is not secured, and one that has a WS-SecurityPolicy asymmetric binding applied to the operations. Both operations contain two parameters, an integer and a String description.

To test it, I added a JMeter test-case here. It uses 10 threads to call the insecure operation 30,000 times. The description String in each request contains the URL encoded version of the WS-Security specification to test what happens with a somewhat large request.

Here are the results using CXF 3.3.1:
and here are the results using the CXF 3.3.2-SNAPSHOT code with the fix for CXF-8010 applied:
Using CXF 3.3.1 the throughput is 1604.25 requests per second, whereas with CXF 3.3.2 the throughput is 1795.26 requests per second, a gain of roughly 9%. For a more complex SOAP Body I would expect the gain to be a lot greater.