ActiveMQ(5.10.0) - Building a custom security plug-in

时间:2023-02-12 08:21:32

If none of any built-in security mechanisms works for you, you can always build your own. Though these features should provide enough functionality for the majority of users, an even more powerful feature is available. As stated previously, the ActiveMQ plug-in API is extremely flexible and the possibilities are endless. The flexibility in this functionality comes from the BrokerFilter class. This class provides the ability to intercept many of the available broker-level operations. Broker operations include such items as adding consumers and producers to the broker, committing transactions in the broker, and adding and removing connections to the broker, to name a few. Custom functionality can be added by extending the BrokerFilter class and overriding a method for a given operation.

Though the ActiveMQ plug-in API isn’t concerned solely with security, implementing a class whose main purpose is to handle a custom security feature is achievable. So if you have security requirements that can’t be met using the previous security features, you may want to consider developing a custom solution for your needs. Depending on your needs, two choices are available:

  • Implement a JAAS login module—There’s a good chance that you’re already using JAAS in your Java applications. In this case, it’s only natural that you’ll try to reuse all that work for securing the ActiveMQ broker, too.
  • Implement a custom plug-in for handling security—ActiveMQ provides a flexible generic plug-in mechanism. You can create your own custom plug-ins for just about anything, including custom security plug-ins. So if you have requirements that can’t be met by implementing a JAAS module, writing a custom plug-in is the way to go.

In this section we’ll describe how to write a simple security plug-in that authorizes broker connections only from a certain set of IP addresses. The concept isn’t complex but is good enough to give you a taste of the BrokerFilter with an angle toward security.

Implementing the plug-in

In order to limit connectivity to the broker based on IP address, we’ll create a class named IPAuthenticationBroker to override the BrokerFilter.addConnection() method. The implementation of this method will perform a simple check of the IP address using a regular expression to determine the ability to connect. The following listing shows the implementation of the IPAuthenticationBroker class.

package org.apache.activemq.customization;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern; import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerFilter;
import org.apache.activemq.broker.ConnectionContext;
import org.apache.activemq.command.ConnectionInfo; public class IPAuthenticationBroker extends BrokerFilter { List<String> allowedIPAddresses;
Pattern pattern = Pattern.compile("([\\d]{1,3}.[\\d]{1,3}.[\\d]{1,3}.[\\d]{1,3})"); public IPAuthenticationBroker(Broker next, List<String> allowedIPAddresses) {
super(next);
this.allowedIPAddresses = allowedIPAddresses;
} public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception { String remoteAddress = context.getConnection().getRemoteAddress(); Matcher matcher = pattern.matcher(remoteAddress);
if (matcher.find()) {
String ip = matcher.group(1);
if (!allowedIPAddresses.contains(ip)) {
throw new SecurityException("Connecting from IP address " + ip
+ " is not allowed");
}
} else {
throw new SecurityException("Invalid remote address " + remoteAddress);
} super.addConnection(context, info);
} }

The BrokerFilter class defines methods that intercept broker operations such as adding a connection, removing a subscriber, and so forth. In the IPAuthenticationBroker class, the addConnection()  method is overridden to create some logic that checks whether the address of a connecting client falls within a list of IP addresses that are allowed to connect. If that IP address is allowed to connect, the call is delegated to the BrokerFilter. addConnection()  method. If that IP address isn’t allowed to connect, an exception is thrown.

One additional item of note in the IPAuthenticationBroker class is that its constructor calls the BrokerFilter’s constructor. This call serves to set up the chain of interceptors so that the proper cascading will take place through the chain. Don’t forget to do this if you create your own BrokerFilter implementation.

After the actual plug-in logic has been implemented, the plug-in must be configured and installed. For this purpose, an implementation of the BrokerPlugin will be created. The BrokerPlugin is used to expose the configuration of a plug-in and also to install the plug-in into the ActiveMQ broker. In order to configure and install the IPAuthenticationBroker, the IPAuthenticationPlugin class is created as shown in the following listing.

package org.apache.activemq.customization;

import java.util.List;

import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerPlugin; public class IPAuthenticationPlugin implements BrokerPlugin { List<String> allowedIPAddresses; public Broker installPlugin(Broker broker) throws Exception {
return new IPAuthenticationBroker(broker, allowedIPAddresses);
} public List<String> getAllowedIPAddresses() {
return allowedIPAddresses;
} public void setAllowedIPAddresses(List<String> allowedIPAddresses) {
this.allowedIPAddresses = allowedIPAddresses;
} }

The IPAuthenticationBroker.installPlugin()  method is used to instantiate the plug-in and return a new intercepted broker for the next plug-in in the chain. Note that the IPAuthenticationPlugin class also contains getter and setter methods used to configure the IPAuthenticationBroker. These setter and getter methods are then available via a Spring beans–style XML configuration in the ActiveMQ XML configuration file (as you’ll see in a moment)

Configuring the plug-in

Now that we’ve implemented the plug-in, let’s see how we can configure it using the ActiveMQ XML configuration file. The following listing shows how the IPAuthenticationPlugin class is used in configuration.

<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}" schedulePeriodForDestinationPurge="3600000">
...
<plugins>
<bean xmlns="http://www.springframework.org/schema/beans" id="ipAuthenticationPlugin"
class="org.apache.activemq.customization.IPAuthenticationPlugin">
<property name="allowedIPAddresses">
<list>
<value>127.0.0.1</value>
</list>
</property>
</bean>
</plugins>
...
</broker>

The <broker> element provides the plugins element for declaring plug-ins. Using the IPAuthenticationPlugin, only those clients connecting from the IP address 127.0.0.1 (the localhost) can actually connect to the broker.

Testing the plug-in

The first and most obvious step is to compile these classes and package them in an appropriate JAR. Place this JAR into the lib/ directory of the ActiveMQ distribution and the policy is ready to be used. Then start up ActiveMQ using the IPAuthenticationPlugin and the IPAuthenticationBroker.

Although this example was more complex, it serves as a good demonstration of the power provided by the BrokerFilter class. Just imagine how flexible this plug-in mechanism is for integrating with existing custom security requirements. This example was focused on a security example, but many other operations can be customized by using the pattern illustrated here.