ActiveMQ + JAAS Custom Login Module

It is pretty easy to find how to run built-in JAAS plugin (jaasAuthenticationPlugin), but what shall you do when you want to your own JAAS LoginModule implementation for ActiveMQ broker authentication (for instance when you have the module already written)?

This article will help with this task.


ActiveMQ + JAAS

ActiveMQ messaging broker has several options how to deal with authentication and authorization.

The easiest one is to use the simpleAuthenticationPlugin, but it is not very flexible.

Another option is the jaasAuthenticationPlugin using two property files (users and groups), quite effective solution but still very clumsy when you consider that all the information about users and groups are stored into files.

Following text will describe how to use customer-defined JAAS LoginModule as an ActiveMQ plugin.

JAAS Custom Login Module for ActiveMQ

Authentication

Once we have developed a JAAS login module we can build it and pack as a JAR library. To use it in the broker we need to put the JAR on the classpath of a running ActiveMQ instance. There are two ways how to do it:

  1. Copy the JAR into lib folder of the broker -ACTIVEMQ_HOME/lib/extra or ACTIVEMQ_HOME/lib/optional
  2. Put a path to the JAR to the java classpath of the broker - edit ACTIVEMQ_HOME/bin/activemq (Unix) or ACTIVEMQ_HOME/bin/activemq.bat (Win) and extend the ACTIVEMQ_CLASSPATH parameter:
    • ACTIVEMQ_CLASSPATH="${ACTIVEMQ_CLASSPATH};/var/lib/auth-test.jar" (Unix)
    • set ACTIVEMQ_CLASSPATH=%ACTIVEMQ_CONF%;%ACTIVEMQ_BASE%/conf;%ACTIVEMQ_HOME%/conf;%ACTIVEMQ_CLASSPATH%;c:/Develop/Java/lib/auth-test.jar (Win)

Broker Setting to use the Plugin

Edit the ACTIVEMQ_HOME/conf/login.config property file to use the plugin as following (consider the plugin class is cz.net21.ActiveMqLoginModule):

MyLoginModule { 
  cz.net21.ActiveMqLoginModule  required debug=true;
};

Edit the ACTIVEMQ_HOME/conf/activemq.xml configuration file as following:

<broker … > 
  …
  <plugins>
    <jaasAuthenticationPlugin configuration="MyLoginModule" />
  …
  </plugins>
  …
</broker>

Running the java code

Now we can run the java to see that the JAAS plugin is working:

ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection connection = connectionFactory.createConnection(username, password);
connection.start();

If the method login() from the module class cz.net21.ActiveMqLoginModule returns false for added credentials, the code will return by en exception:

java.lang.SecurityException: User name [testuser] or password is invalid.
Caused by: javax.security.auth.login.LoginException: Login Failure: all modules ignored

Authorization

In this part we will look at a simple way how to authorize an user for broker's resources (queues/topics).

Via authorizationPlugin we can setup all the rights for queues/topics and users groups.

<authorizationPlugin>
  <map>
    <authorizationMap>
      <authorizationEntries>
        <authorizationEntry queue=">"
          read="admins"  write="admins"  admin="admins" />
  …

We can leave this settings and control the access via our custom JAAS module. All we need is to add an UserPrincipal object for the broker user and a GroupPrincipals object for the broker groups.

Let's extend our module to consume a file with groups used in the authorizationPlugin:

MyLoginModule { 
  cz.net21.ActiveMqLoginModule required 
    debug=true
    org.apache.activemq.jaas.properties.group="groups.properties";
};

Here we use org.apache.activemq.jaas.properties.group as a name of an option and groups.properties as a name of the property file with a list of groups.

public void initialize(Subject subject, CallbackHandler handler, Map<String, ?> state, Map<String, ?> options) {
  this.subject = subject;
  groupsFile = options.get("org.apache.activemq.jaas.properties.group") + "";
  …

But we can use for instance groupListFile as the name and instead of the property file use a comma-separated file with just a list of group names.

We will process the file and bind the logged user with one of the groups.

public boolean login() throws LoginException {
  try {
    File f = new File(baseDir, groupsFile);
    groups.load(new java.io.FileInputStream(f));
  } catch (IOException e) {
    throw new LoginException("Unable to load group properties file " + groupsFile);
  }
  …

Then we need to put the UserPrincipal and GroupPrincipals objects into the subject's principals:

principals.add(new UserPrincipal(user));
principals.add(new GroupPrincipal(groupName));
subject.getPrincipals().addAll(principals);

If the groupName match the destination we will get the access, otherwise the code will return by en exception:

java.lang.SecurityException: User guest is not authorized to read from: queue://TestQ


Congratulation, we have a JAAS plugin for authentication and authorization of ActiveMQ broker!

Appendix

I am working with Java 7, ActiveMQ 5.9.0

Please see the discussed code in the attachment.