blue and orange picture of programming screen

Securing ActiveMQ Console with LDAP

Securing ActiveMQ Console with LDAP seems to have limited documentation. It could be the version that I installed or the fact that I am not an LDAP guru, though I have learned more in the past three months about connecting to and querying LDAP than I thought I ever would. So, here are some basics. I installed Apache ActiveMQ 5.15.10 on a CentOS 7 server that is running Java 8 r221. The LDAP configuration is actually talking to an Active Directory server, though I do not know the specifics, other than the connection URL, bind DN, and bind password. The following information was gathered from several articles, Apache’s documentation, and sheer stupid luck. This was my second attempt at implementing ActiveMQ and AD, so timing is everything. The AMQ configuration is fairly basic. We configured Virtual Topics, DLQ (dead letter queue) handling, and TLS/SSL, but there is no detail regarding those topics in this article. We have three AMQ nodes in the network for high availability, but are running in-memory only, outside of our DLQ’s. I had the broker talking to LDAP at one point, which was relatively easy; however, I stripped it out when I could not get the Web Console app configured. At some point very soon, we will reconfigure the broker for LDAP as well.

Configuration

References:

Libraries

I used Ldaptive to integration LDAP, mainly because it was the configuration that was the best documented. To support Ldaptive, two Jetty JAAS libraries were downloaded and installed in $ACTIVEMQ_HOME/lib.

  1. Download Ldaptive 1.2.4, unpack it, and copy jars/ldaptive-1.2.4.jar to $ACTIVEMQ_HOME/lib.
  2. Download Jetty JAAS, unpack it, and copy **ONLY** the following files to $ACTIVEMQ_HOME/lib:
    • jetty-jaas-9.4.19*.jar
    • jetty-security-9.4.19*.jar

I found some conflicts at one point, but it might have been an older version of Jetty JAAS that I was using. I did not go back and copy all of the JAR files from Jetty JAAS.

Configuration

Only two files need modification, jetty.xml and login.config. Below are the files, with some specifics masked to protect the identities of the guilty (the client). I left almost all of the default AMQ configuration intact, in case we needed to revert. There are a number of useless beans that are defined, but will most likely help in the end.

$ACTIVEMQ_HOME/conf/jetty.xml (partial)

[fusion_code]

... <!-- LDAP Configuration -->
    <bean id="defaultIdentityService" class="org.eclipse.jetty.security.DefaultIdentityService" />
    <bean id="ldapSecurityLoginService" class="org.eclipse.jetty.jaas.JAASLoginService">
        <property name="name" value="LdapRealm" />
        <property name="loginModuleName" value="activemq-ldap" />
        <property name="roleClassNames" value="org.ldaptive.jaas.LdapRole" />
        <property name="identityService" ref="defaultIdentityService" />
    </bean>
    <bean id="ldapSecurityConstraint" class="org.eclipse.jetty.util.security.Constraint">
        <property name="name" value="BASIC" />
        <property name="roles" value="user,roleIntegrationDev" />
        <property name="authenticate" value="true" />
    </bean>
    <bean id="ldapAdminSecurityConstraint" class="org.eclipse.jetty.util.security.Constraint">
        <property name="name" value="BASIC" />
        <property name="roles" value="roleIntegrationDev" />
        <property name="authenticate" value="true" />
    </bean>
    <bean id="ldapSecurityConstraintMapping" class="org.eclipse.jetty.security.ConstraintMapping">
        <property name="constraint" ref="ldapSecurityConstraint" />
        <property name="pathSpec" value="/api/*,/admin/*,*.jsp" />
    </bean>
    <bean id="ldapAdminSecurityConstraintMapping" class="org.eclipse.jetty.security.ConstraintMapping">
        <property name="constraint" ref="ldapAdminSecurityConstraint" />
        <property name="pathSpec" value="*.action" />
    </bean>
<!-- End of LDAP Configuration -->
...
    <bean id="securityHandler" class="org.eclipse.jetty.security.ConstraintSecurityHandler">
        <property name="loginService" ref="ldapSecurityLoginService"/>
        <property name="identityService" ref="defaultIdentityService" />
        <property name="authenticator">
            <bean class="org.eclipse.jetty.security.authentication.BasicAuthenticator" />
        </property>
        <property name="constraintMappings">
            <list>
                <ref bean="ldapAdminSecurityConstraintMapping" />
                <ref bean="ldapSecurityConstraintMapping" />
            </list>
        </property>
        <property name="handler" ref="secHandlerCollection" />
    </bean>
...

[/fusion_code]

$ACTIVEMQ_HOME/conf/login.config

[fusion_code]

/**
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License.  You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

activemq-ldap {
  org.ldaptive.jaas.LdapLoginModule required
      debug=true
      storePass="true"
      ldapUrl="ldaps://<<<URL for the AD server>>>/"
      bindDn="<<<the user DN that will perform the search>>>"
      bindCredential="<<<the password for the user DN that will perform the search>>>"
      baseDn="DC=dev,DC=local"
      useStartTLS="false"
      subtreeSearch=true
      userFilter="(sAMAccountName={user})";
  org.ldaptive.jaas.LdapRoleAuthorizationModule required
      useFirstPass="true"
      ldapUrl="ldaps://<<<URL for the AD server>>>/"
      bindDn="<<<the user DN that will perform the search>>>"
      bindCredential="<<<the password for the user DN that will perform the search>>>"
      baseDn="DC=dev,DC=local"
      useStartTLS="false"
      subtreeSearch=true
      roleFilter="(&(objectClass=group)(sAMAccountName=<<<the group ID>>>)(memberUid={user}))"
      defaultRole="user"
      roleAttribute="cn";
};
/** Default configuration **/
activemq {
    org.apache.activemq.jaas.PropertiesLoginModule required
        org.apache.activemq.jaas.properties.user="users.properties"
        org.apache.activemq.jaas.properties.group="groups.properties";
};

[/fusion_code]

The roleFilter value was tricky, because I am not that familiar with LDAP. In my opinion, roleFilter and userFilter, are just a matter of trial and error. Use ldapsearch from openldap-clients to perform some initial queries or you will be wasting time restarting AMQ.

Let me know if you have any questions about my process.

Work Horse Integrations

Newsletter Sign-Up

Don’t miss out on the latest updates, insights, and exclusive offers from Work Horse Integrations.