Camel Security Integration

Camel provides several forms and levels of security capabilities. These various forms of security may be used in conjunction with each other or separately.
The Camel security levels can be divided into:

For more information visit Camel Security page .

Route Security

Camel provides Policy driven security capabilities that may be wired into routes or route segments. A Policy in Camel utilizes a strategy pattern for applying interceptors on Camel Processors. It offering the ability to apply cross-cutting concerns (for example. security, transactions etc) on sections/segments of a Camel route.
The components offering authentication and authorization Services utilizing Route Policies are :

Shiro Security Component

The shiro-security component in Camel is a security focused component, based on the Apache Shiro security project. Apache Shiro is a powerful and flexible open-source security framework that cleanly handles authentication, authorization, enterprise session management and cryptography. The objective of the Apache Shiro project is to provide the most robust and comprehensive application security framework available while also being very easy to understand and extremely simple to use.
The component needs to be declared in the pom.xml as the following:

<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-shiro</artifactId>
    <version>x.x.x</version>
    <!-- use the same version as your Camel core version -->
</dependency>

To use Shiro security on a Camel route, a ShiroSecurityPolicy object must be instantiated with security configuration details (including users, passwords, roles etc). This configuration is contained on an Ini file (properties file) or an Ini object. This object is then applied to a Camel route in order to check the user's authentication and authorization. This ShiroSecurityPolicy Object may also be registered in the Camel registry (JNDI or ApplicationContextRegistry) and then utilized on other routes in the Camel Context.

Shiro Configuration File

The configuration Ini file is a standard Shiro configuration file containing user/role details as shown below :

[users]
# user 'ringo' with password 'starr' and the 'sec-level1' role
ringo = starr, sec-level1
george = harrison, sec-level2
john = lennon, sec-level3
paul = mccartney, sec-level3

[roles]
# 'sec-level3' role has all permissions, indicated by the 
# wildcard '*'
sec-level3 = *

# The 'sec-level2' role can do anything with access of permission 
# readonly (*) to help
sec-level2 = zone1:*

# The 'sec-level1' role can do anything with access of permission 
# readonly   
sec-level1 = zone1:readonly:*

Shiro Security Policy

The following source code shows the ShiroSecurityPolicy class instanciation using the Shiro configuration file, the passPhrase and a specified permission list. And finally, adds the ShiroSecurityPolicy object in the Camel Registry.

	// Shiro configuration file path
   	private static final String iniResourcePath = "classpath:shiro/securityPolicy.ini";
   	// Object used to encrypt/decrypt the token in the message.
   	private static final byte[] passPhrase = {
         (byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0B, (byte) 0x0C, (byte) 0x0D, (byte) 0x0E, (byte) 0x0F,
         (byte) 0x10, (byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15, (byte) 0x16, (byte) 0x17};

   	...
   	
   public static void main(String[] args)
   {
      
      // Creation of the permission list supported by the policy.
      List<Permission> permissionsList = new ArrayList<Permission>();
      Permission permission = new WildcardPermission("zone1:readwrite:*");
      permissionsList.add(permission);
      
      ShiroSecurityPolicy policy = new ShiroSecurityPolicy(iniResourcePath, passPhrase, true, permissionsList);
      
      // Add the policy to the Camel Registry.
      SimpleRegistry reg = new SimpleRegistry();
      reg.put("securityPolicy",policy);
      ...
    }

This policy will be applied to a specified route and determine if the message can pass through it. The policy object is retrieved directly from the Camle Registry.


from("direct:secureEndpoint").to("log:Incoming payload").policy(
                     (Policy) context.getRegistry().lookup("securityPolicy")).to("log:success");

Shiro Security Token

In this case, the message needs to contain the user credential that will be checked in the policy. This user credential is contained in an Security Token object. A TokenInjector object is created using this token and encrypts it using the passPhrase. The message is processed using the TokecnInjector object to inject the encrypted token on it.
The following source code shows this operation:

// The ShiroSecurityToken constructor receives the userName and the password as parameters.
ShiroSecurityToken shiroSecurityToken = new ShiroSecurityToken("paul", "mccartney");
// Encrypting the token using the passPhrase in the TokenInjector object.
ShiroSecurityTokenInjector shiroSecurityTokenInjector = new ShiroSecurityTokenInjector(shiroSecurityToken, passPhrase);
...
final CamelContext context = new DefaultCamelContext(reg);
      try
      {
         context.addRoutes(new RouteBuilder()
         {
            public void configure() throws Exception
            {

               ...
               from("direct:client").process(shiroSecurityTokenInjector).to("direct:secureEndpoint");
               ...
            }
         });

Camel Exception Management for Shiro

We can the Camel Exception management to catch exceptions thrown by The ShiroSecurityPolicy object as follow :

onException(UnknownAccountException.class).handled(true).to("log:UnknownAccountException").to(
                     "file:Error/UnknownAccountException");
onException(IncorrectCredentialsException.class).handled(true).to("log:IncorrectCredentialsException")
                     .to("file:Error/IncorrectCredentialsException");
onException(LockedAccountException.class).handled(true).to("log:LockedAccountException").to(
                     "file:Error/LockedAccountException");
onException(AuthenticationException.class).handled(true).to("log:AuthenticationException").to(
                     "file:Error/AuthenticationException");
onException(CamelAuthorizationException.class).handled(true).to(
                     "log:CamelAuthorizationException ${in.header}").to("file:Error/CamelAuthorizationException");
onException(NullPointerException.class).handled(true).to("log:Message not secure").to(
                     "file:Error/NotSecureMessage");

Shiro Full Example

public class CamelShiroExample
{

   // Shiro configuration file path
   private static final String iniResourcePath = "classpath:shiro/securityPolicy.ini";
   // Object used to encrypt/decrypt the token in the message.
   private static final byte[] passPhrase = {
         (byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0B, (byte) 0x0C, (byte) 0x0D, (byte) 0x0E, (byte) 0x0F,
         (byte) 0x10, (byte) 0x11, (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15, (byte) 0x16, (byte) 0x17};

   // list of token injectors used in this example
   private static ShiroSecurityTokenInjector shiroSecurityTokenInjector;
   private static ShiroSecurityTokenInjector shiroSecurityTokenInjector2;
   private static ShiroSecurityTokenInjector shiroSecurityTokenInjector3;

   /**
    * @param args
    * @throws Exception
    */
   public static void main(String[] args)
   {

      // Creation of the permission list supported by the policy.
      List<Permission> permissionsList = new ArrayList<Permission>();
      Permission permission = new WildcardPermission("zone1:readwrite:*");
      permissionsList.add(permission);

      ShiroSecurityPolicy policy = new ShiroSecurityPolicy(iniResourcePath, passPhrase, true, permissionsList);

      // Add the policy to the Camel Registry.
      SimpleRegistry reg = new SimpleRegistry();
      reg.put("securityPolicy", policy);

      // Instanciating the Shiro tokens
      // The ShiroSecurityToken constructor receives the userName and the password as
      // parameters.
      ShiroSecurityToken shiroSecurityToken = new ShiroSecurityToken("paul", "mccartney");
      ShiroSecurityToken shiroSecurityToken2 = new ShiroSecurityToken("ringo", "starr");
      ShiroSecurityToken shiroSecurityToken3 = new ShiroSecurityToken("ringo", "stirr");

      // Instanciating the Shiro TokenInjectors objects
      shiroSecurityTokenInjector = new ShiroSecurityTokenInjector(shiroSecurityToken, passPhrase);
      shiroSecurityTokenInjector2 = new ShiroSecurityTokenInjector(shiroSecurityToken2, passPhrase);
      shiroSecurityTokenInjector3 = new ShiroSecurityTokenInjector(shiroSecurityToken3, passPhrase);

      final CamelContext context = new DefaultCamelContext(reg);
      try
      {
         context.addRoutes(new RouteBuilder()
         {
            public void configure() throws Exception
            {

               onException(UnknownAccountException.class).handled(true).to("log:UnknownAccountException").to(
                     "file:Error/UnknownAccountException");
               onException(IncorrectCredentialsException.class).handled(true).to("log:IncorrectCredentialsException")
                     .to("file:Error/IncorrectCredentialsException");
               onException(LockedAccountException.class).handled(true).to("log:LockedAccountException").to(
                     "file:Error/LockedAccountException");
               onException(AuthenticationException.class).handled(true).to("log:AuthenticationException").to(
                     "file:Error/AuthenticationException");
               onException(CamelAuthorizationException.class).handled(true).to(
                     "log:CamelAuthorizationException ${in.header}").to("file:Error/CamelAuthorizationException");
               onException(NullPointerException.class).handled(true).to("log:Message not secure").to(
                     "file:Error/NotSecureMessage");

               from("direct:client").process(shiroSecurityTokenInjector).to("direct:secureEndpoint");
               from("direct:client1").process(shiroSecurityTokenInjector2).to("direct:secureEndpoint");
               from("direct:client2").process(shiroSecurityTokenInjector3).to("direct:secureEndpoint");

               from("direct:secureEndpoint").to("log:Incoming payload").policy(
                     (Policy) context.getRegistry().lookup("securityPolicy")).to("log:success");
            }
         });

         context.start();

         ProducerTemplate template = context.createProducerTemplate();

         Endpoint endpoint = context.getEndpoint("direct:client");
         Exchange exchange = endpoint.createExchange();
         exchange.getIn().setBody("Data");
         template.send(endpoint, exchange);

         Endpoint endpoint1 = context.getEndpoint("direct:client1");
         Exchange exchange1 = endpoint1.createExchange();
         exchange1.getIn().setBody("Data1");
         template.send(endpoint1, exchange1);

         Endpoint endpoint2 = context.getEndpoint("direct:client2");
         Exchange exchange2 = endpoint2.createExchange();
         exchange2.getIn().setBody("Data2");
         template.send(endpoint2, exchange2);

         Endpoint endpoint3 = context.getEndpoint("direct:secureEndpoint");
         Exchange exchange3 = endpoint3.createExchange();
         exchange3.getIn().setBody("Data3");
         template.send(endpoint3, exchange3);

      }
      catch (Exception e)
      {
         e.printStackTrace();
      }

   }

}

Spring Security Component

The camel-spring-security component provides role-based authorization for Camel routes. It leverages the authentication and user services provided by Spring Security (formerly Acegi Security) and adds a declarative, role-based policy system to control whether a route can be executed by a given principal.
The component needs to be declared in the pom.xml as the following:

<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-spring-security</artifactId>
    <version>x.x.x</version>
    <!-- use the same version as your Camel core version -->
</dependency>

Spring Security Configuration File

A Spring Security AuthenticationManager and AccessDecisionManager are required to use this component. Here is an example of how to configure these objects in Spring XML using the Spring Security namespace:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:spring-security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
			http://www.springframework.org/schema/beans/spring-beans.xsd
			http://www.springframework.org/schema/security
			http://www.springframework.org/schema/security/spring-security.xsd">

    <bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
        <property name="allowIfAllAbstainDecisions" value="true"/>
        <property name="decisionVoters">
            <list>
                <bean class="org.springframework.security.access.vote.RoleVoter"/>
            </list>
        </property>
    </bean>
    
    <spring-security:authentication-manager alias="authenticationManager">
    	<spring-security:authentication-provider user-service-ref="userDetailsService"/>
    </spring-security:authentication-manager>
   
    <spring-security:user-service id="userDetailsService">
        <spring-security:user name="jim" password="jimspassword" authorities="ROLE_USER, ROLE_ADMIN"/>
        <spring-security:user name="bob" password="bobspassword" authorities="ROLE_USER"/>
    </spring-security:user-service>

</beans>

Camel Spring Security Component Configuration File

we have to import the Spring Security configuration in your Spring configuration file in order to configure an authorization policy and use that policy to control access to a route:

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:spring-security="http://www.springframework.org/schema/security"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://camel.apache.org/schema/spring
          http://camel.apache.org/schema/spring/camel-spring.xsd
          http://camel.apache.org/schema/spring-security
          http://camel.apache.org/schema/spring-security/camel-spring-security.xsd
          http://www.springframework.org/schema/security
		  http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">

	<!-- import the spring security configuration  -->
	<import
		resource="classpath:META-INF/spring/security/springSecurity-configuration.xml" />

	<authorizationPolicy id="admin" access="ROLE_ADMIN"
		authenticationManager="authenticationManager" accessDecisionManager="accessDecisionManager"
		xmlns="http://camel.apache.org/schema/spring-security" />

	<authorizationPolicy id="user" access="ROLE_USER"
		authenticationManager="authenticationManager" accessDecisionManager="accessDecisionManager"
		xmlns="http://camel.apache.org/schema/spring-security" />

	<bean id="bobProcessor"
		class="com.sungard.isb.camel.security.springsecurity.MyAuthService">
		<property name="userName" value="bob" />
		<property name="password" value="bobspassword" />
	</bean>

	<bean id="jimProcessor"
		class="com.sungard.isb.camel.security.springsecurity.MyAuthService">
		<property name="userName" value="jim" />
		<property name="password" value="jimspassword" />
	</bean>

	<camelContext id="myCamelContext" xmlns="http://camel.apache.org/schema/spring">
		<route>
			<from uri="direct:client" />
			<process ref="jimProcessor"/>
			<to uri="direct:secureEndpoint" />
		</route>

		<route>
			<from uri="direct:client1" />
			<process ref="bobProcessor"/>
			<to uri="direct:secureEndpoint" />
		</route>

		<route>
			<from uri="direct:client2" />
			<to uri="direct:secureEndpoint" />
		</route>

		<route>
			<from uri="direct:secureEndpoint" />
			<policy ref="user">
				<to uri="log:success user role" />
			</policy>
			<policy ref="admin">
				<to uri="log:success admin role" />
			</policy>
			<onException>
				
				<exception>
					org.apache.camel.CamelAuthorizationException
				</exception>
				<handled>
					<constant>true</constant>
				</handled>
				<choice>
					<when>
						<simple>${exception.policyId} == 'user'</simple>
						<to uri="log:Fail user role" />
					</when>
					<when>
						<simple>${exception.policyId} == 'admin'</simple>
						<to uri="log:Fail admin role" />
					</when>
					<when>
						<simple>${exception.policyId} == 'null'</simple>
						<to uri="log:Fail user/admin role" />
					</when>
				</choice>
				<to uri="log:AccessDeniedException" />
			</onException>
		</route>
	</camelContext>
</beans>

The MyAuthService class injects the user credentials in the message header:

public class MyAuthService implements Processor {
   
   private String userName;
   private String password;
      
   public void process(Exchange exchange) throws Exception {
      // create an Authentication object
      UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userName, password);

      // wrap it in a Subject
      Subject subject = new Subject();
      subject.getPrincipals().add(authToken);

      // place the Subject in the In message
      exchange.getIn().setHeader(Exchange.AUTHENTICATION, subject);

      // you could also do this if useThreadSecurityContext is set to true
      // SecurityContextHolder.getContext().setAuthentication(authToken);
  }

   public String getUserName()
   {
      return userName;
   }

   public void setUserName(String userName)
   {
      this.userName = userName;
   }

   public String getPassword()
   {
      return password;
   }

   public void setPassword(String password)
   {
      this.password = password;
   }
}

Camel Spring Security Component Client

public class SpringSecurityExample
{

   public static void main(String[] args)
   {
      ApplicationContext context = new ClassPathXmlApplicationContext(
            "classpath:META-INF/spring/camel-context-springSecurity.xml");

      SpringCamelContext camelContext = (SpringCamelContext) context.getBean("myCamelContext");

      ProducerTemplate template = camelContext.createProducerTemplate();

      Endpoint endpoint = camelContext.getEndpoint("direct:client");
      Exchange exchange = endpoint.createExchange();
      exchange.getIn().setBody("Data");
      template.send(endpoint, exchange);
      
      Endpoint endpoint1 = camelContext.getEndpoint("direct:client1");
      Exchange exchange1 = endpoint1.createExchange();
      exchange1.getIn().setBody("Data1");
      template.send(endpoint1, exchange1);
      
      Endpoint endpoint2 = camelContext.getEndpoint("direct:client2");
      Exchange exchange2 = endpoint2.createExchange();
      exchange2.getIn().setBody("Data2");
      template.send(endpoint2, exchange2);

   }
}

Payload Security

Camel offers encryption/decryption services to secure payloads or selectively apply encryption/decryption capabilities on portions/sections of a payload.
These capabilites are offered by the following components :

We will focus on the Crypto component.

Camel Crypto Component

The Crypto Data Format integrates the Java Cryptographic Extension into Camel, providing simple and flexible encryption and decryption of messages using the Camel marshall and unmarshal formatting mechanism. It assumes marshalling to mean encryption to cyphertext and unmarshalling decryption back to the original plaintext. At its most basic all that is required to encrypt/decrypt an exchange is a shared secret key. If one or more instances of the Crypto data format are configured with this key the format can be used to encrypt the payload in one route (or part of one) and decrypted in another.

Encryption/Decryption Example

The following example shows how to encrypt and decrypt Camel messages into the Camel route:

KeyGenerator generator = KeyGenerator.getInstance("DES");

CryptoDataFormat cryptoFormat = new CryptoDataFormat("DES", generator.generateKey());

from("direct:basic-encryption").to("log:Initial message")
                   .marshal(cryptoFormat)
                   .to("log:Encrypted message")
                   .unmarshal(cryptoFormat)
                   .to("log:Unencrypted message");

Crypto Full Example

public class CryptoExample
{

   public static void main(String[] args)
   {

      final CamelContext context = new DefaultCamelContext();
      try
      {
         context.addRoutes(new RouteBuilder()
         {
            public void configure() throws Exception
            {

               KeyGenerator generator = KeyGenerator.getInstance("DES");

               CryptoDataFormat cryptoFormat = new CryptoDataFormat("DES", generator.generateKey());

               from("direct:basic-encryption").to("log:Initial message")
                   .marshal(cryptoFormat)
                   .to("log:Encrypted message")
                   .unmarshal(cryptoFormat)
                   .to("log:Unencrypted message");
            }
         });

         context.start();
         
         ProducerTemplate template = context.createProducerTemplate();

         Endpoint endpoint = context.getEndpoint("direct:basic-encryption");
         Exchange exchange = endpoint.createExchange();
         exchange.getIn().setBody("Data");
         template.send(endpoint, exchange);

      }
      catch (Exception e)
      {
         e.printStackTrace();
      }

   }
}

Endpoint Security

Some components in Camel offer an ability to secure their endpoints (using interceptors etc) and therefore ensure that they offer the ability to secure payloads as well as provide authentication/authorization capabilities at endpoints created using the components. Some such components are

For more information refer to Camel Official Website

Configuration Security

Camel offers the Properties component to externalize configuration values to properties files. Those values could contain sensitive information such as usernames and passwords. Those values can be encrypted and automatic decrypted by Camel.

Jasypt

Jasypt is a simplified encryption library which makes encryption and decryption easy. Camel integrates with Jasypt to allow sensitive information in Properties files to be encrypted. By dropping camel-jasypt on the classpath those encrypted values will automatic be decrypted on-the-fly by Camel. This ensures that human eyes can't easily spot sensitive information such as usernames and passwords.
The component needs to be declared in the pom.xml as the following:

<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-jasypt</artifactId>
    <version>x.x.x</version>
    <!-- use the same version as your Camel core version -->
</dependency>

Example with Java DSL

In Java DSL you need to configure Jasypt as a JasyptPropertiesParser instance and set it on the Properties component as show below:

// create the jasypt properties parser
JasyptPropertiesParser jasypt = new JasyptPropertiesParser();
// and set the master password
jasypt.setPassword("secret");

// create the properties component
PropertiesComponent pc = new PropertiesComponent();
pc.setLocation("classpath:org/apache/camel/component/jasypt/myproperties.properties");
// and use the jasypt properties parser so we can decrypt values
pc.setPropertiesParser(jasypt);

// add properties component to camel context
context.addComponent("properties", pc);

The properties file myproperties.properties then contain the encrypted value, such as shown below. Notice how the password value is encrypted and the value has the tokens surrounding ENC(value here)

# refer to a mock endpoint name by that encrypted password
cool.result=mock:{{cool.password}}

# here is a password which is encrypted
cool.password=ENC(bsW9uV37gQ0QHFu7KO03Ww==)

Example with Spring DSL

In Spring XML you need to configure the JasyptPropertiesParser which is shown below. Then the Camel Properties component is told to use jasypt as the properties parser, which means Jasypt have its chance to decrypt values looked up in the properties.

<!-- define the jasypt properties parser with the given password to be used -->
<bean id="jasypt" class="org.apache.camel.component.jasypt.JasyptPropertiesParser">
    <property name="password" value="secret"/>
</bean>

<!-- define the camel properties component -->
<bean id="properties" class="org.apache.camel.component.properties.PropertiesComponent">
    <!-- the properties file is in the classpath -->
    <property name="location" value="classpath:org/apache/camel/component/jasypt/myproperties.properties"/>
    <!-- and let it leverage the jasypt parser -->
    <property name="propertiesParser" ref="jasypt"/>
</bean>

The Properties component can also be inlined inside the <camelContext> tag which is shown below. Notice how we use the propertiesParserRef attribute to refer to Jasypt.</camelContext>

<!-- define the jasypt properties parser with the given password to be used -->
<bean id="jasypt" class="org.apache.camel.component.jasypt.JasyptPropertiesParser">
    <!-- password is mandatory, you can prefix it with sysenv: or sys: to indicate it should use
         an OS environment or JVM system property value, so you dont have the master password defined here -->
    <property name="password" value="secret"/>
</bean>

<camelContext xmlns="http://camel.apache.org/schema/spring">
    <!-- define the camel properties placeholder, and let it leverage jasypt -->
    <propertyPlaceholder id="properties"
                         location="classpath:org/apache/camel/component/jasypt/myproperties.properties"
                         propertiesParserRef="jasypt"/>
    <route>
        <from uri="direct:start"/>
        <to uri="{{cool.result}}"/>
    </route>
</camelContext>