Using encrypted passwords with Mule ESB

A good practice in the Mule ESB is to supply properties by using a property file. Most of the time you will end up adding passwords to the properties file. In that case you might want to encrypt the passwords so it is not visible for every one who has access to the property file. Mulesoft described how to do this in combination with the Mule ESB. Although it is a good starting point I thought it may help to create a complete example so I put all the steps in this post. There are two environments that have to be modified: the development environment and the runtime environment.

The development environment is where you create your Mule applications with your Java IDE. You will need to take the following steps:

  • Add dependencies to your projects pom
  • In the Maven pom file the following dependencies are added:

    <dependency>
      <groupId>org.jasypt</groupId>
      <artifactId>jasypt-spring3</artifactId>
      <version>1.9.1</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.jasypt</groupId>
      <artifactId>jasypt</artifactId>
      <version>1.9.1</version>
      <scope>provided</scope>
    </dependency>
    
  • Configure Mule to make use of the encrypted properties file
  • To make the Mule ESB able using the encrypted properties in the properties file I created a Spring config file that is loaded (imported) by the Mule configuration of my Mule applications. The Spring config file ‘my-properties-config.xml’ looks like:

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:encryption="http://www.jasypt.org/schema/encryption"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
           http://www.jasypt.org/schema/encryption http://www.jasypt.org/schema/encryption/jasypt-spring3-encryption-1.xsd" >
     
        <!--password-sys-property-name="MULE_ENCRYPTION_PASSWORD" -->
        <encryption:encryptor-config id="eConf" password-env-name="MULE_ENCRYPTION_PASSWORD" algorithm="PBEWithMD5AndDES"/>
        <encryption:string-encryptor id="stringEnc" config-bean="eConf"/>
        <encryption:encryptable-property-placeholder encryptor="stringEnc" location="classpath:my-environment.properties"/>
    </beans> 
    
  • Set up your Unit test
  • Of course you want to unit test this functionality πŸ™‚
    This step is divided into three substeps. The first step is to create the Unit Test class. The one I used is the following:

    import org.junit.Assert;
    import org.junit.BeforeClass;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
     
    import javax.annotation.Resource;
    import java.lang.reflect.Field;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
     
    /**
    * Tests the JASYPT encryption in a property file.
    * Expects the environment variable 'MULE_ENCRYPTION_PASSWORD' to be filled
    * with the correct Master password. The setEnv method adds this variable to the
    * env in memory.
    */
    @ContextConfiguration(locations = "classpath:test-config.xml")
    @RunWith(SpringJUnit4ClassRunner.class)
    public class PropertyTest {
     
        @Resource
        private java.util.Map myMap;
     
        public void setMyMap(Map myMap) {
            this.myMap = myMap;
        }
     
        @Test
        public void myTest() {
            Assert.assertNotNull("myMap should be initialized", myMap);
            Assert.assertEquals("The decrypted password should match the expected one", "password", myMap.get("pass1"));
        }
     
        @BeforeClass
        public static void setEnv()
        {
            Map newenv = new HashMap();
            newenv.put("MULE_ENCRYPTION_PASSWORD","mulesoft");
            try
            {
                Class processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
                Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment");
                theEnvironmentField.setAccessible(true);
                Map env = (Map) theEnvironmentField.get(null);
                env.putAll(newenv);
                Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment");
                theCaseInsensitiveEnvironmentField.setAccessible(true);
                Map cienv = (Map)     theCaseInsensitiveEnvironmentField.get(null);
                cienv.putAll(newenv);
            }
            catch (NoSuchFieldException e)
            {
                try {
                    Class[] classes = Collections.class.getDeclaredClasses();
                    Map env = System.getenv();
                    for(Class cl : classes) {
                        if("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
                            Field field = cl.getDeclaredField("m");
                            field.setAccessible(true);
                            Object obj = field.get(env);
                            Map map = (Map) obj;
                            map.clear();
                            map.putAll(newenv);
                        }
                    }
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }
    }
     

    The most complex part in this unit test is the code that adds the ‘MULE_ENCRYPTION_PASSWORD’ variable to the runtime environment. I found this code on the internet and as far as I can tell it works on both Windows 7 and Linux Debian (at least these are the environments where my unit test runs successfully). Another option could be to demand that the environment variable is set in every environment that runs the unit test (developers machines and Jenkins) but I think sooner or later you will run into the issue the variable is not set.
    The Java class uses the Spring configuration file ‘test-config.xml’ that looks like this:

    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:util="http://www.springframework.org/schema/util"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:spring="http://www.springframework.org/schema/beans"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
        <spring:import resource="classpath:my-properties-config.xml" />
        <util:map id="myMap" key-type="java.lang.String" value-type="java.lang.String">
            <entry key="key1" value="${property.1}" />
            <entry key="key2" value="${property.1}" />
            <entry key="pass1" value="${password.1}" />
        </util:map>
    </beans>    
    

    The last step is adding the test property file to the unit test. This property file ‘my-environment.properties’ contains the encrypted passwords to test with:

    property.1=value1
    property.2=value2
    password.1=ENC(2ekUF20x8zP79ydA2d9yo1yhtXnKMxmG)

    With all this in place the uit tests creates a Map with the values of the properties from the property file. The value for ‘password.1’ in the property file is the encrypted value for the string ‘password’ with the master password ‘mulesoft’. In the Unit test I get the value and check that it matches the decrypted value.

So that is for the development environment. Now the runtime environment. This is where you run your standalone Mule ESB. You will need to add the necessary Jasypt libraries here and use them to generate the encrypted passwords. Here are the steps:

  • Set the environment variable
  • This environment variable will hold the master password that is used to decrypt the encrypted password. Please note that this might be another possible security breach.
    To set the environment variable automatically at a Linux server you can create a script in the ‘/etc/profile.d/’ directory so it is executed when a user logs in like this:
    sudo nano /etc/profile.d/mule-env.sh
    In the file we put the command to set the environment variable:
    export MULE_ENCRYPTION_PASSWORD=mulesoft
    After saving the file load it for the current user:
    source /etc/profile
    If you don’t like the idea that the master password is set for each user and is saved in a file you might want to try to set the environment variable manually for the user just before you start the Mule ESB. I am pretty sure there might be better ways to do this but I’m just showing what worked for me.

  • Add the necessary jars to the Mule ESB libraries
  • I put the Jasypt jars that corresponds with the dependencies in my project pom file in the directory ‘/opt/mule/lib/shared/default’ where ‘/opt/mule/’ is the installation directory of my standalone Mule ESB. This way the Jasypt libraries are available to all Mule applications deployed to this server.

  • Generate the encrypted passwords
  • Now generate the necessary encrypted passwords with the following command (Make sure the password matches the one that is used in the environment variable!):
    java -cp jasypt-1.9.1.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="XXXX" password=mulesoft algorithm=PBEWithMD5AndDES

    mulesoft/mulesoft gives:
    nBB7bPmx93lNmenRrriyjtt8zujZ/imH

  • Put the passwords in the property file
  • The output has to be placed in the Mule property file of your application as follows:
    my.jms.password=ENC(ZPeoYBBmGi/Zq7MD0qnVo8dm4MjvVJMJ)

That’s it. No more readable passwords in your properties file. You could increase the security further by not adding the master password to the environment variables (where it can be accessed by anyone who has access to the environment) but by supplying the master password as a startup parameter when running Mule ESB.

Advertisement

About Pascal Alma

Pascal is a senior IT consultant and has been working in IT since 1997. He is monitoring the latest development in new technologies (Mobile, Cloud, Big Data) closely and particularly interested in Java open source tool stacks, cloud related technologies like AWS and mobile development like building iOS apps with Swift. Specialties: Java/JEE/Spring Amazon AWS API/REST Big Data Continuous Delivery Swift/iOS
This entry was posted in Mule3, Spring Framework and tagged , , , . Bookmark the permalink.