terça-feira, 6 de dezembro de 2011

User-based authentication using Orbited + ActiveMQ


User-based authentication for the Orbited and ActiveMQ duo

(this was originally posted to Google Groups under Orbited Users on June 27th, 2010)



Some time ago I promised I'd give the details on how to setup basic (user & password) authentication for STOMP connections to ActiveMQ proxied through Orbited once I had figured that out. So here it is.

Before starting, I want to say I have performed this on Unix-based OSs (Linux and OSX) so if you're on Windows, you may find some issues (or not). So I am not sure as to what you might encounter.

You probably already have Python 2.5+ installed, but if not, do so. I have 2.6.
First, install Orbited and ActiveMQ if you haven't done so already. I suggest creating a single directory and then placing both ActiveMQ and Orbited inside for testing purposes, but it's up to you.

For Orbited, download at http://orbited.org/wiki/Download and find installation instructions at http://orbited.org/wiki/Installation. I am using version 0.7.10.

For ActiveMQ, download at http://activemq.apache.org/download.html and find installation instructions at http://activemq.apache.org/version-5-getting-started.html. I am using version 5.3.0 (since it's the one I have installed on a production box). I am no Java expert, neither am acquainted to it. Bear in mind it requires Java JDK 1.5 or later installed and accessible. There are some notes about that on the ActiveMQ website.

Let's first configure your Orbited proxy, shall we? Create a folder for Orbited's config file. Create an empty text file there named orbited.cfg and paste the following contents inside:

# Configuration script for Orbited + ActiveMQ
# ActiveMQ will be listening to STOMP on 61613
# Orbited will listen on 9000
[listen]
http://:9000 #port where Orbited will listen
[access]
#since we are using a single MQ (and this Orbited instance will serve only for this testing),
#we'll proxy everything on port 9000 to ActiveMQ's  STOMP port
* -> localhost:61613 
[logging]
debug=STDERR,debug.log
info=STDERR,info.log
access=STDERR,info.log
warn=STDERR,error.log
error=STDERR,error.log
[static]
#Not used
[loggers]
Proxy=debug,info,access,warn,error # turn all output on for the Proxy component
[global]
user=orbited
session.ping_interval = 40

Bear in mind the Orbited installation gives you a predefined config file for Orbited (a download from bitbucket). The code above is that very same file, with a few changes. If you don't know this already, the log files will be created on the directory you run Orbited from.

Head over to the very directory you've created your orbited.cfg file and issue the command (via terminal)

$(which orbited) -c $(pwd)/orbited.cfg


Or /path/to/orbited -c /path/to/your/orbited/config/file/orbited.cfg as you (and your OS) see fit.

You now have a nice Comet-enabled proxy running on your machine. Leave that terminal window open (or send it to the background via Ctrl-Z and then bg. But again, it's between you and your OS.

Open a browser window and point it to http://localhost:9000/static/demos/stomp/ (or wherever you've setup your Orbited server). Open a second window on the same address (or rather, another tab). Leave them be.

Off to the ActiveMQ configuration. Go to the place where you have unpacked or installed the ActiveMQ files. Open the conf directory, make a backup of the existing activemq-security.xml file. Create a new activemq-security.xml file (or name it whatever you want and don't backup the first one - just remember to adapt the next steps). Place the following inside the new XML file:

<?xml version="1.0" encoding="UTF-8"?>
<beans
  
  <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
      <property name="locations">
          <value>file:${activemq.base}/conf/credentials.properties</value>
      </property>      
  </bean>
  <broker useJmx="true" persistent="false" xmlns="http://activemq.apache.org/schema/core">
    <plugins>
        <!-- Configure authentication; Username, passwords and groups -->
        <simpleAuthenticationPlugin>
            <users>
                <authenticationUser username="system" password="manager" groups="users,admins"/>
                <authenticationUser username="webreader" password="web" groups="users"/>
                <authenticationUser username="writer" password="writerpass" groups="producers,admins"/>
                <authenticationUser username="reader" password="readerpass" groups="consumers,admins"/>
            </users>
        </simpleAuthenticationPlugin>
      <!--  Lets configure a destination based authorization mechanism -->
      <authorizationPlugin>
        <map>
          <authorizationMap>
            <authorizationEntries>
              <authorizationEntry queue="bid"           read="consumers"                write="producers"   admin="admins" />
              <authorizationEntry topic="auction_data"  read="users"                    write="producers"   admin="admins,users" />
              <authorizationEntry topic="timer"         read="users,consumers"          write="producers"   admin="admins,users" />
              
              <authorizationEntry topic="ActiveMQ.Advisory.>" read="guests,users" write="guests,users" admin="guests,users,admins"/>
            </authorizationEntries>
          </authorizationMap>
        </map>
      </authorizationPlugin>
      
    </plugins>
    
    <!-- This is where you'll get your ActiveMQ to listen to STOMP on 61613 -->
    <transportConnectors>
       <transportConnector name="default" uri="stomp://0.0.0.0:61613"/>
    </transportConnectors>
    
  </broker>
</beans>

Before anyone asks, yes, this is a file for a penny auction system I created in Brazil. Of course the usernames and passwords are not the very ones used.
And that's all there is to it. Save (obviously) and start your ActiveMQ instance by going to the place where you installed/unpacked it and adding xbean:/path/to/your/activemq-security.xml after the activemq executable call. Note that if you don't add the bean file you want to use the configurations from, ActiveMQ will automatically load the configurations that exist on /path/to/activemq/conf/activemq.xml, so make sure you load your generated security configurations file as mentioned above. Adapt as needed.
Time for testing, I'll give some explanations later.

Go to the browser(s) where you have the STOMP demo from Orbited open. Let's have one connect as the producer/writer for the auction_data channel and the other as the consumer/webreader. On the first window, enter writer as Name and writerpass as the Password on the boxes that follow the Connect and Disconnect buttons. ClickConnect. You should get a Connected as user writer message on the message box atop. As the first test, click on the Subscribe button right below the Connect button. You'll get a nasty message from Javascript dumping a lot of data from the Java Security exception you've just managed to generate, something on the lines of

onerrorframe: java.lang.SecurityException: User serverwriter is not authorized to read from: topic://home
at org.apache.activemq.security.AuthorizationBroker.addConsumer(AuthorizationBroker.java:110)
at org.apache.activemq.broker.MutableBrokerFilter.addConsumer(MutableBrokerFilter.java:93)
at org.apache.activemq.broker.TransportConnection.processAddConsumer(TransportConnection.java:530)
at org.apache.activemq.command.ConsumerInfo.visit(ConsumerInfo.java:349)
at org.apache.activemq.broker.TransportConnection.service(TransportConnection.java:297)
(...it goes on...)

The important piece of it is User serverwriter is not authorized to read from: topic://home, where you get a confirmation ActiveMQ is blocking unauthorized users - so it understands what users are allowed to perform on which channel, right? Right.

On the second window, let's connect again, but this time using the webreader user (password web). Enter the credentials the same way we did before and hit Connect. You should get a Connected as user webreader message on the box atop. Next, off to the second test. Remember you got a failure on the first attempt to subscribe? Now, modify the destination channel (usually /topic/home) to /topic/auction_data (this is the box right below the Name input box) and hit Subscribe. This time, you'll get NO messages at all.

So now you have connected the writer and the webreader users to ActiveMQ through Orbited. Neat. To prove it, go back to the first window, and post a message to the channel by typing any message on the input box immediately to the right of the Send with Transaction Id button. Modify the input box to the right of this one, labeleddestination, to /topic/auction_data. Hit the Send button.

Go to the second window. You'll be able to see the message you've just sent to the /topic/auction_data channel on the message box.

Congratulations. You have just configured your ActiveMQ instance to obey to some simple security settings that enable certain users to perform certain actions based on the channels they're connected to (or trying to connect to). In my case, this is vital. Sending messages to one of the queues that is only used internally could heavily compromise the auction processing.

If you want to see my comments on the authentication settings, read on. This authentication method is quite simple.

ActiveMQ allows you to configure permissions (read, write and create) to groups against channels and assign users to these groups. The permissions are defined as properties of the authorizationEntry tag:

read identifies which groups can read from that channel,
write identifies which groups can write to that channel and
admin identifies which groups can create that channel on the message broker.

Create, you say?
Yep. Every message that is first sent to any channel in ActiveMQ actually creates the channel on the broker. The admin property for the specified channel specifies what group can send that first message. You can find (a lot) more on this on the ActiveMQ website.

Adding users to group is done when you add the authenticationUser tag (in this case, the user webreader was added to the users group and has been assigned the web password):

<authenticationUser username="webreader" password="web" groups="users"/>

If you want to add a user to other groups, separate them with commas:

<authenticationUser username="writer" password="writerpass" groups="producers,admins"/>

The channel permissions are set through the authorizationEntry tags: 

<authorizationEntry topic="auction_data" read="users" write="producers" admin="admins,users" />

In this particular case, we are stating that users that belong to the users group can read from the /topic/auction_data channel, users from the producers group can write and users that belong to the admins and users groups can create the channel. There is also no other place where you need to first refer to these groups. Just use them as needed on the tags mentioned above and it'll work.

I've had issues with ampersands in the passwords, so I just leave these characters out. Try it out. It's fun.

And that's about it.

Really?
For this first take on ActiveMQ's security for Orbited, non-Java users, yes. There's a whole lot more you can perform with ActiveMQ, but this is far beyond my comprehension at this moment (June 27th 2010, 17:20 GMT+0300) and completely out of this article's scope. With the information above, you'll be able to setup a simple authentication model for your ActiveMQ channels while sharing all Orbited goodness available.


Let there be Comet, and Push away.

Nenhum comentário:

Postar um comentário