Introduction
I’m a great enthusiast of Apache Camel and his capabilities. For our customer cases we’re running Apache Karaf 4.2.8 with Apache Camel 3.2.0 in a Docker container.
One of the common use cases is to provide HTTP endpoints (in Camel’s jargon consumers) that fulfill some security requirements
- Enable TLS (HTTPS)
- Serve multiple URIs on a single port
- Protect each URI independently with authentication (e.g. basic authentication for each URI with different username(s) and password(s)
The endpoints (HTTP consumers) could be
- Common HTTP endpoints (consumers) to start a Camel route
- REST APIs to support multiple paths and RESTful operations
All examples are written in OSGi Blueprint DSL. We have chosen this for simplicity and hot deployment capabilities.
Single HTTP consumer
The foundation is a Vanilla Apache Karaf 4.2.8 with Apache Camel 3.2.0 and the required features. For the initial setup we’ve installed
- webconsole
- camel
- camel-jetty
Let’s start with a simple Camel route like the following code
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
<route>
<from uri="jetty:http://0.0.0.0:8182/hello1" />
<setBody><constant>Hello 1</constant></setBody>
</route>
</camelContext>
</blueprint>
This route is starting a Jetty web server on port 8182 an serves HTTP requests like
curl --location --request GET 'http://localhost:8182/hello1'
Enable TLS on the HTTP consumer
For a TLS setup we need to create a appropriate TLS keystore. For testing purposes we might use a self signed key pair that has been converted into a p12 keystore.
Some guidance could be found here
At next we modify the route above like
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<sslContextParameters id="sslContextParameters" xmlns="http://camel.apache.org/schema/blueprint">
<keyManagers keyPassword="xxxxx">
<keyStore resource="etc/keystore.p12" password="xxxxx"/>
</keyManagers>
</sslContextParameters>
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
<route>
<from uri="jetty:https://0.0.0.0:8444/hello1?sslContextParameters=sslContextParameters" />
<setBody><constant>Hello 1</constant></setBody>
</route>
</camelContext>
</blueprint>
The route is starting a Jetty web server on port 8444 and serves HTTPS requests like
curl --insecure --location --request GET 'https://localhost:8444/hello1'
Enable basic authentication on the HTTP consumer
For basic authentication we need to define and assign a security handler to the HTTP consumer.
Remark: A security handler will be bind to a HTTP port. It is not possible to define another security handler again in another Blueprint DSL file at same HTTP port. This will overwrite the first one.
We extend the route above as following
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<bean id="constraint" class="org.eclipse.jetty.util.security.Constraint">
<property name="name" value="BASIC" />
<property name="authenticate" value="true" />
<property name="roles">
<list>
<value>hello1</value>
</list>
</property>
</bean>
<bean id="constraintMapping" class="org.eclipse.jetty.security.ConstraintMapping">
<property name="constraint" ref="constraint" />
<property name="pathSpec" value="/hello1" />
</bean>
<bean id="securityHandler" class="org.eclipse.jetty.security.ConstraintSecurityHandler">
<property name="loginService">
<bean class="org.eclipse.jetty.security.HashLoginService">
<property name="config" value="/opt/apache-karaf/etc/users.properties" />
<property name="hotReload" value="true" />
</bean>
</property>
<property name="authenticator">
<bean class="org.eclipse.jetty.security.authentication.BasicAuthenticator" />
</property>
<property name="constraintMappings">
<list>
<ref component-id="constraintMapping" />
</list>
</property>
</bean>
<sslContextParameters id="sslContextParameters" xmlns="http://camel.apache.org/schema/blueprint">
<keyManagers keyPassword="xxxxx">
<keyStore resource="etc/keystore.p12" password="xxxxx"/>
</keyManagers>
</sslContextParameters>
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
<route>
<from uri="jetty:https://0.0.0.0:8444/hello1?sslContextParameters=sslContextParameters&handlers=securityHandler" />
<setBody><constant>Hello 1</constant></setBody>
</route>
</camelContext>
</blueprint>
We extend the file {karaf.home}/etc/users.properties like
username1=password1,hello1
The route is now secured. A HTTPS request like
curl --insecure --location --request GET 'https://localhost:8444/hello1'
will return a HTTP 401 error (Unauthorized). A HTTPS request like
curl --insecure --location --request GET 'https://localhost:8444/hello1' \
--header 'Authorization: Basic dXNlcm5hbWUxOnBhc3N3b3JkMQ=='
will return the proper response. It contains the username1 and password1 as HTTP Authorization header.
In the next article we’ll discuss a REST API enabled with TLS and basic authentication.