Monthly Archives: September 2014

JBoss 7.x remoting + security-domain

I just recently updated an application to Red Hat’s EAP 6.2.0 platform, which uses the JBoss 7.3 Application Server under the hood. I’ve been a JBoss user for a long time now. In fact, this application started life on JBoss 3.something, and I (sometimes with the help of other devs) have seen it upgraded to every major version of JBoss since. The migration from 6 to 7 is hands down the most difficult to perform. Well, not that it’s really all that difficult, so we’ll just say time consuming. It took a good week to get everything right.

I’ll have more to say about the migration path soon, but one specific area that I think warrants special attention is the security subsystem.

The application has several components to it, one being a Java Swing client that uses Remote Method Invocation. The client has its own log in screen and used the UsernamePasswordHandler class and the ClientLoginModule from JBoss Security packages. Something like this:


UsernamePasswordHandler handler = new UsernamePasswordHandler(username, MD5.hash(password).toCharArray());
LoginContext lc = new LoginContext("MYCONTEXT", handler));
lc.login();

That’s all pretty simple really. The UsernamePasswordHandler is, well, a callback handler that handles NameCallbacks and PasswordCallbacks by setting the values to what you passed into the constructor. We just pass that handler into the LoginContext, which is backed by the default ‘ClientLogniModule’, which just sets the security principal and credential to what’s handed to it by the handler. When ‘login’ is invoked the username and (hashed) password are authenticated against whatever login-module is configured on the server side, which in my case is a DatabaseServerLoginModule.

Unfortunately this became a bit more complicated as a result of this migration. For reasons I don’t understand neither the UsernamePasswordHandler callback nor the ClientLoginModule are available in JBoss 7, nor can I find any classes with similar functionality. It’s not very difficult to implement your own, and in fact that’s what I had to do, but the fact that I had to is … annoying!

Another big change in JBoss 7 is that you can’t even get an InitialContext to do any JNDI lookups without authentication. In other words, you can’t simply just do ‘new InitialContext()’ any longer. I’m not talking about executing remote methods on an EJB here, I mean at the transport layer – the remoting connector itself requires authentication. Here is the configuration :


<subsystem xmlns="urn:jboss:domain:remoting:1.1">
<connector name="remoting-connector" socket-binding="remoting" security-realm="ApplicationRealm"/>
</subsystem>

See that bit about the ‘ApplicationRealm’ ? That’s the security realm. Within the security realm you must supply a configuration for user authentication. As far as I can tell you can either authenticate against what is called a ‘local user’ (an account you create with the JBoss ‘add-user.sh or add-user.bat’ scripts, that applies to the entire server), or you hand it all off to a JAAS module. Since I have multiple applications running on the server that all have different sets of users, I opted for the latter. Here is the configuration for the ApplicationRealm security realm:


<security-realm name="ApplicationRealm" >
<authentication >
<jaas name="MySecurityDomain"/>
</authentication>
</security-realm>

Now, this is where it gets really good. As I just said, I have multiple applications that all have a their own sets of users. However, the remoting connector is bound to a single security realm, and the security realm to a single JAAS module. I do not believe it’s an option to create multiple remoting connectors, each on its own port, which means that a single JAAS module needs to handle authentication for all applications that run on the server. I’m really hoping I’m just missing something here, but if I am then it’s due to inadequate documentation.

Anyway, I had one last hurdle to jump through. Since a single JAAS module was going to have to authenticate all users, I needed a way to deal with the possibility that there might be different users with the same name in different applications, or , the same user might exist in different applications, have the same password in each, but have different roles. In other words, I need to be sure we’re authenticating against the correct database for the application the user is logging into! So, it’s not enough to just pass over a user name and password any longer — we need the application (or context, or domain if you like) in addition. And then we need to use that context to query against the proper database.

To get the context I just appended it to the user name. So, instead of ‘james.swafford’ the principal is now ‘james.swafford@somedomain’. Easy enough. Now, how to do the authentication itself? I can think of two ways to do this.

I’ve always used the ‘out of the box’ Database Login Module on the server side. If we wanted to use that and had just one application to worry about, the configuration would look something like this:


<security-domain name="MySecurityDomain">
<authentication>
<login-module code="Database" flag="required">
<module-option name="dsJndiName" value="java:jboss/SomeDS"/>
<module-option name="principalsQuery" value="select md5passwd from users where login = ? and active=TRUE"/>
<module-option name="rolesQuery" value="select securityroles.role,'Roles' from securityroles inner join users_roles on securityroles.roleid=users_roles.role_id inner join users on users_roles.user_id=users.user_id where login =?"/>
<module-option name="unauthenticatedIdentity" value="guest"/>
<module-option name="password-stacking" value="useFirstPass"/>
</login-module>
</authentication>
</security-domain>

Since I have multiple applications, I had to do something a little different. One trick would be to chain multiple login modules together, making each of them ‘sufficient’ instead of required. To do that you’d just have to change the queries to take the context into account.


<authentication>
<login-module code="Database" flag="sufficient">
<module-option name="dsJndiName" value="java:jboss/App1DS"/>
<module-option name="principalsQuery" value="select md5passwd from users where login || '@app1' = ? and active=TRUE"/>
<module-option name="rolesQuery" value="select securityroles.role,'Roles' from securityroles inner join users_roles on securityroles.roleid=users_roles.role_id inner join users on users_roles.user_id=users.user_id where login || '@app1' =?"/>
<module-option name="unauthenticatedIdentity" value="guest"/>
<module-option name="password-stacking" value="useFirstPass"/>
</login-module>
<login-module name="Database-2" code="Database" flag="sufficient">
<module-option name="dsJndiName" value="java:jboss/App2DS"/>
<module-option name="principalsQuery" value="select md5passwd from users where login || '@app2' = ? and active=TRUE"/>
<module-option name="rolesQuery" value="select securityroles.role,'Roles' from securityroles inner join users_roles on securityroles.roleid=users_roles.role_id inner join users on users_roles.user_id=users.user_id where login || '@app2' =?"/>
<module-option name="unauthenticatedIdentity" value="guest"/>
<module-option name="password-stacking" value="useFirstPass"/>
</login-module>
</authentication>

That works OK and is easy to do, but the drawback is that the app server is going to cycle through all of those modules until one successfully authenticates or they all fail. That is potentially a lot extra querying. Another, probably better solution would be to create a CustomLoginModule that is clever enough to use the context to determine which data source to query.

None of this was all that difficult but it is different and took a little time to learn. Hopefully this will help someone out there save a little time.