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.