SyntaxHighlighter

Monday, December 1, 2014

Securing the JBoss A-MQ management interfaces



Recently I had to install and configure a JBoss A-MQ (ActiveMQ) environment where security of the management interfaces (Hawtio console, JMX, CLI) was one of the key requirements. Although not particularly hard to implement, it took some time to find the information on how to to do this. The journey for information included reading documentation, analysing JIRAs and stepping through the Karaf classes with a remote debugger. I figured it would be a good idea to document the steps in a blog-post.

Requirements

The security requirements for the management interfaces were simple:
  • All connections should use one-way SSL.
  • Use the central LDAP as the identity provider.
Security of actual JBoss A-MQ transport connectors was out-of-scope for this task.

We will first look at how to connect the JBoss A-MQ system to an LDAP (in this case Apache Directory) environment. After that, we will secure the interfaces via SSL.

Authentication with LDAP

The JBoss A-MQ user manual has an in-depth section on how to enable LDAP authentication on A-MQ: https://access.redhat.com/documentation/en-US/Red_Hat_JBoss_A-MQ/6.1/html/Security_Guide/files/ESBSecurityLDAPAuthentPlugin.html

In this example we will use the following JAAS configuration, which connects to an Apache Directory Server on my local machine:



  
    
      initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory
      connection.username=uid=admin,ou=system
      connection.password=jboss@01
      connection.protocol=
      connection.url = ldap://localhost:10389
      user.base.dn = ou=users,ou=testunit,dc=example,dc=com
      user.filter = (uid=%u)
      user.search.subtree = true
      role.base.dn = ou=groups,ou=testunit,dc=example,dc=com
      role.filter = (uniqueMember=%dn)
      role.name.attribute = cn
      role.search.subtree = true
      authentication = simple
    
  



Notice the name and rank attributes of the jaas:config element in the configuration. The JAAS domain name of the default Karaf login-module is 'karaf' and uses the usernames, passwords and roles defined in the "{jboss-a-mq}/etc/users.properties" file. By giving our LDAP JAAS configuration the same name as the default configuration, i.e. 'karaf', and by giving it a higher rank, we override the default configuration with our LDAP configuration. This enables LDAP authentication for all modules within the JBoss A-MQ system that use the 'karaf' JAAS domain, i.e. the Hawtio console, the JBoss A-MQ and the JMX interface.

Also note the %dn placeholder in the role.filter configuration. This is somewhat an undocumented feature that allows one to use the Distinguished Name of the user in the role.filter instead of the username (%u, the username used to login to the system). This is required when the LDAP system uses the DN instead of the user-id (uid) for group membership entries (e.g. Microsoft ActiveDirectory).

Karaf Administrator Role

Now that we can authenticate our users via LDAP, we need to make sure that only the users in the admin group have administrator access in JBoss A-MQ. This is done via the karaf.admin.role property defined in "{jboss-a-mq}/etc/system.properties". This is by default set to 'admin'. In enterprise environments, group names will usually have a more descriptive name then 'admin'. I.e., they might include the name of the system, the name of the platform, etc. In our example, the administrator group for JBoss A-MQ is called 'AMQAdmin'. In the LDAPLoginModule we defined earlier, we've already configured the role.base.dn, role.filter, role.name.attribute and  role.search.subtree properties so that the login-module retrieves our user's roles from LDAP (i.e. the group memberships in LDAP are mapped onto JAAS role principals). Therefore, we need to configure our karaf.admin.role to match our AMQAdmin LDAP group's CN (Comman Name), which is 'AMQAdmin'.

karaf.role.admin=AMQAdmin

Hawtio Admin Role verification

Now that we've configured the default JAAS domain to use LDAP as the identity provider, our JBoss A-MQ Hawtio management console also uses LDAP authentication. Out-of-the-box however, Hawtio does not check the user's role for administrator rights. This means that any user that can access the system with an LDAP login will get full administrative accessm, even if he/she is not part of the AMQAdmin group in LDAP. To enable authorization verification in Hawtio, we need to add the following 2 properties to the "{jboss-a-mq}/etc/system.properties" configuration file:

hawtio.role=${karaf.admin.role}
hawtio.rolePrincipalClasses=org.apache.karaf.jaas.boot.principal.RolePrincipal,org.apache.karaf.jaas.modules.RolePrincipal

This configures the Hawtio console to grant access to the same role as we've configured earlier in the karaf.admin.role property: AMQAdmin

We've now configured LDAP authentication and authorization on all management interfaces, so let's test our setup to verify our configuration. We can access the Hawtio console: http://localhost:8181/hawtio Now that we've configured authentication and authorization of our management interfaces, it is time to configure SSL on all interfaces.

PAX Web

PAX Web is the servlet-container system for the Karaf OSGi platform. The servlet-container implementation it uses is Jetty. It is configured via the "{jboss-a-mq}/etc/org.ops4j.pax.web.cfg" configuration file. To disable Jetty's standard HTTP connector and enable HTTPS (without client authentication), we therefore need to set the correct configuration in "org.ops4j.pax.web.cfg".

First we need to create a Java keystore that will be used by Jetty's HTTP connector. In this example we will create a keystore with self-signed keys and certificates. In  an enterprise environment, one would most likely use keys and certificates signed by a(n) (intermediate) CA.

We will use the Java 'keytool' (which is provided with the JDK) to create the keystore. We'll use the following command:

keytool -genkey -noprompt -alias amq -keyalg RSA -validity 365 -keystore amq-server.keystore -storetype JKS -keypass jboss@01 -storepass jboss@01 -dname "CN=Duncan Doyle, OU=GPS, O=Red Hat, L=Rotterdam, ST=Zuid-Holland, C=NL"


This creates the keystore "amq-server.keystore" with a self-signed certificate, with "jboss@01" as the keystore-password and key-password.

We now replace the properties in "org.ops4j.pax.web.cfg" with the following properties:

# Disable the HTTP connector

org.osgi.service.http.enabled=false


# Enable the HTTPS connector on port 8143

org.osgi.service.http.secure.enabled=true

org.osgi.service.http.port.secure=8143


#Configure the keystore

org.ops4j.pax.web.ssl.keystore={karaf.etc}/amq-server.keystore

org.ops4j.pax.web.ssl.keystore.type=JKS

org.ops4j.pax.web.ssl.password=jboss@01

org.ops4j.pax.web.ssl.keypassword=jboss@01

# Do not enable client authentication, we're using one-way SSL

org.ops4j.pax.web.ssl.clientauthwanted=false

org.ops4j.pax.web.ssl.clientauthneeded=false


org.ops4j.pac.web.config.file=etc/jetty.xml


Restart JBoss A-MQ and open the Hawtio console: https://localhost:8143/hawtio . notice that you have to accept the security exception (our keystore is self-signed and therefore not trusted by the browser).

Try to access Hawtio via the HTTP connector: http://localhost:8181/hawtio . Hawtio should no lonlger be accessible on this port.

JMX and SSL

Now that we've secured the Hawtio web-console, we need to secure the JMX interface using SSL. The JMX configuration of the Karaf JMX connector can be found in "{jboss-a-mq}/etc/org.apache.karaf.management.cfg".

As can be seen in the configuration, the JMX system uses the default 'karaf' JAAS domain for authentication (configured in the jmxRealm property), and thus already uses LDAP for authentication. Second, the JMX system by default uses the value of the karaf.admin.role property as the role that is authorized to access Karaf via JMX. We configured this role earlier and have set it to 'AMQAdmin'.

Therefore, the only thing that we need to configure is changing the default transport of the JMX connector to SSL. The JMX connector is configured in the class org.apache.karaf.management.ConnectorServerFactory, which for keystore and truststore management uses the org.apache.karaf.jaas.config.KeystoreManager. To enable SSL on the JMX connector, we first need to deploy the correct keystore and truststore to the Karaf runtime, which will be picked up and made available by the KeystoreManager. Next, we need to configure the ConnectorServerFactory to point to the correct keystore and key.

We're going to reuse the keystore we created for the HTTPS connection. To deploy this keystore to the KeystoreManager, we're going to create a file called "{jboss-a-mq}/etc/jaas-keystore-module.xml" with the following content:


        






Now we need to make sure that the keystore is deployed onto the system before it is required by the ConnnectorServerFactory via the  KeystoreManager. We therefore need to control the deployment order of these modules. This can be done by creating a file called "{jboss-a-mq}/etc/org.apache.felix.fileinstall-jaas-keystore-module.cfg" with the following content:

felix.fileinstall.dir=${karaf.base}/etc
felix.fileinstall.filter = jaas-keystore-module\\.xml
felix.fileinstall.poll = 1000
felix.fileinstall.noInitialDelay = true
felix.fileinstall.log.level = 3
felix.fileinstall.start.level = 25

This filter the files called jaas-keystore-module.xml in the {karaf.base}/etc directory and increases its startlevel so it will be processed first.

Finally, we can configure the JMX ConnectorServerFactory to use SSL by adding the following properties to the file "{jboss-a-mq}/etc/org.apache.karaf.management.cfg":

# Enable SSL on the JMX connector
secured=true
keyStore=jmxKeyStore
trustStore=jmxKeyStore
keyAlias=servercert

Note that we use the keyStore and keyAlias configured in our jaas-keystore-module.xml file.

Restart JBoss A-MQ and connect to the platform using JConsole. Make sure to provide JConsole a truststore that contains the public certificate that belongs to the private key we use for the JMX SSL connection. This can be done by  starting up JConsole with the following command-line:

jconsole -J"-Djavax.net.ssl.trustStore=jconsole.truststore" -J-Djavax.net.ssl.trustStorePassword=jboss@01

Next, connect  with the following connection string:

service:jmx:rmi:///jndi/rmi://localhost:1099/karaf-root

Provide your LDAP credentials and connect to the Karaf/JBoss A-MQ JMX interface. If all is configured correctly you should see the following screen.


Conclusion

This article has shown how to secure a vanilla JBoss A-MQ platform to be used in a production environment. Most of this information can be found in various places (where some info is better hidden than other), but we hope that by documenting all these steps in a single place we've eased to process of setting up a secured JBoss A-MQ environment.