Wednesday, September 2, 2009

Enabling Security for ActiveMQ web apps

Recently I was working with for a company to harden their set up of Active MQ. Though not the ultimate monitoring solution for Avtive MQ, they wanted to use the web console for providing some kind of ad hoc visibility on key properties. The ActiveMQ web page describes the set up that is pretty straight forward when an external servlet engine is used, but configuring the embedded jetty is a bit different.

From the ActiveMQ web console documentation we could see that we need 3 things:


  1. An User Realm that can be plugged into the Jetty engine.

  2. Credentials definitions.

  3. A security aware web application.



Let's have a look at each of these things:

We start with rewriting the jetty bean definition in the default activemq.xml with the following activemq.xml:






<!--
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.
-->
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:jetty="http://mortbay.com/schemas/jetty/1.0"
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-2.0.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd
http://activemq.apache.org/camel/schema/spring http://activemq.apache.org/camel/schema/spring/camel-spring.xsd">

<!-- Allows us to use system properties as variables in this configuration file -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>file:///${activemq.base}/conf/credentials.properties</value>
</property>
</bean>

<broker xmlns="http://activemq.apache.org/schema/core" brokerName="TRT_INT" dataDirectory="${activemq.base}/data">

<!-- Destination specific policies using destination names or wildcards -->
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry queue=">" producerFlowControl="true"/>
<policyEntry topic=">" producerFlowControl="true"/>
</policyEntries>
</policyMap>
</destinationPolicy>

<!-- Define Virtual Destinations for Topics -->
<!-- All topics will have a corresponding queue corresponding to the name pattern below
where consumers can connect to in order to act as subscription groups. -->
<destinationInterceptors>
<virtualDestinationInterceptor>
<virtualDestinations>
<virtualTopic name=">" prefix="VTopic.*." />
</virtualDestinations>
</virtualDestinationInterceptor>
</destinationInterceptors>

<!-- predefine Queues corresponding to Virtual topics so that no messages get lost due
to unregistered consumers. -->
<destinations>
<!-- The Subscriber group App1 is the name of a queue that ALL instances of App1
would use to consume messages from the topic "X". All
consumers using the same queue name act as ONE subscriber and the messages
will be load balanced across the group. -->
<!-- queue physicalName="VTopic.App1.X" / -->
</destinations>

<!-- Use the following to configure how ActiveMQ is exposed in JMX -->
<managementContext>
<managementContext createConnector="true"/>
</managementContext>

<!-- The store and forward broker networks ActiveMQ will listen to -->
<networkConnectors>
<!-- by default just auto discover the other brokers -->
<!--<networkConnector name="default-nc" uri="multicast://default"/>-->
<!-- Example of a static configuration:
<networkConnector name="host1 and host2" uri="static://(tcp://host1:61616,tcp://host2:61616)"/>
-->
</networkConnectors>

<!-- Using the Kaha DB store for persistence now -->
<persistenceAdapter>
<kahaDB directory="${activemq.base}/data/kahadb" />
</persistenceAdapter>

<!-- Don't have ssl
<sslContext>
<sslContext keyStore="file:${activemq.base}/conf/broker.ks" keyStorePassword="password" trustStore="file:${activemq.base}/conf/broker.ts" trustStorePassword="password"/>
</sslContext -->

<!-- The maximum about of space the broker will use before slowing down producers -->
<systemUsage>
<systemUsage>
<memoryUsage>
<memoryUsage limit="350 mb"/>
</memoryUsage>
<storeUsage>
<storeUsage limit="20 gb" name="foo"/> <!-- used for persistent messages -->
</storeUsage>
<tempUsage>
<tempUsage limit="100 mb"/> <!-- used for non persistent messages -->
</tempUsage>
</systemUsage>
</systemUsage>


<!-- The transport connectors ActiveMQ will listen to -->
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61616"/>
</transportConnectors>
</broker>

<jetty:jetty>
<connectors>
<jetty:nioConnector port="8161"/>
</connectors>
<handlers>
<jetty:webAppContext contextPath="/admin" resourceBase="${activemq.base}/webapps/admin" logUrlOnStart="true"/>
</handlers>
<userRealms>
<bean class="org.mortbay.jetty.security.HashUserRealm">
<property name="name" value="ActiveMQ Realm" />
<property name="config" value="${activemq.base}/conf/webconsole.properties" />
</bean>
</userRealms>
</jetty:jetty>
</beans>



Next you need to create the file webconsole.properties in the conf directory of ActiveMQ.The file contains one line per user with the format






useName: password, [roleName]*



For example:






myAdmin: THESECRET, amqAdmin



Finally you need to amend the web.xml files of the ActiveMQ web applications (${activemq.base}/webapps/*/WEB-INF/web.xml) with the following xml fragment:






<security-constraint>
<web-resource-collection>
<web-resource-name>A Protected Page</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>amqAdminrole-name>
</auth-constraint>
</security-constraint>

<login-config>
<auth-method>BASIC</auth-method>
<realm-name>ActiveMQ Realm</realm-name>
</login-config>



Make sure, the Realm name matches the one you have set in the Realm definition and the role name match roles you have named in the properties file.

5 comments:

James said...

Your post is just what i was looking for. Could you post your full activemq.xml? I'm having trouble with xml namespaces and could use further help,
Thanks
!

Andreas Gies said...

James,

sorry to react late, but my blog didnt notify me of your message.

I have updated the post with a working version. You were right about the namespaces.

Best regards
Andreas

RituShishir said...

Thank you very much for this helpful post.

Unknown said...
This comment has been removed by the author.
Unknown said...

This is great and helpful post. Thank you.
Cybergraphix