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:
- ActiveMQ Documentation for LDAP (NOTE: I used very little information from this page.)
- ActiveMQ Web console using LDAP Active Directory authentication (highly useful document)
- ActiveMQ Locking down Web Console via LDAP (good document to help me understand some differences)
- Securing the ActiveMQ 5.8.0 web console using LDAP based authentication with Ldaptive (good starting document for Ldaptive)
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.
- Download Ldaptive 1.2.4, unpack it, and copy jars/ldaptive-1.2.4.jar to $ACTIVEMQ_HOME/lib.
- 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.