Using Keycloak for authentication with Apache Artemis: part 1

Keycloak logo This article describes what I believe is a minimal set-up of the Artemis message broker, to use the Keycloak authentication server for access control. The article covers the authentication of both messaging (JMS) clients and users of the broker's administration console -- in fact, in this trivial example, they are the same users.

Note:
I wrote this article for Keycloak 18.0, which was based on the Wildfly application server. Newer versions are based on Quarkus but, perhaps surprisingly, basic administration is similar. However, when I tested this article on Keycloak 24.0, I found that I had to do a few things differently. It's plausible that the steps I found I needed, I actually needed for 18.0, but forgot to document. If so, I apologise. Please see the end of this article for what's different in 24.0

I will describe the set-up of Keycloak and Artemis from scratch, on bare metal. This example uses 'direct access grants' for authenticating JMS clients. This means that the JMS client will present a user ID and password to the Artemis broker, and Artemis will authenticate these credentials against Keycloak. In the next article I describe an authentication scheme in which a JMS client gets a short-lived 'bearer' token from Keycloak, and passes that to Artemis. This is a more modern way of doing authentication, although the need to fetch a token makes it less common in machine-to-machine interactions.

Limitations

This minimal set-up is only intended to serve as a starting point, and has a number of important limitations.

Installing and setting up Keycloak

1. Obtain the Keycloak bundle. I'm using version 18.0 -- the "legacy" version that is based on Wildfly. Newer versions will be based on Quarkus, but they should work in a very similar way -- at least in simple applications.

2. Unpack the bundle and start the server by running bin/standalone.sh or bin\standalone.bat.

3. Open the Keycloak administration console using a web browser -- it uses port 8080 by default.

4. Enter an initial administrator password when prompted.

5. From the realm name drop-down list at the top left, select "Add realm". Call the new realm "foo", or choose a more imaginative name -- but note that the name will appear in later configuration, and I have assumed "foo" in what follows.

6. From the left-hand side-bar menu select "Clients", and add client "artemis-broker". This name will be used in other parts of the Keycloak configuration, and also by messaging clients when they authenticate. If not changed, the authentication protocol for this client will be OpenID Connect (an extension of OAuth 2.0).

Note:
The term "Client" in Keycloak denotes one or more applications with related authentication requirements; Keycloak itself is not acting as a client in a client/server interaction.

7. In the configuration page for the "artemis-broker" client, select the "Roles" tab, and add an "amq" role. No specific configuration is needed for the role, nor any other configuration for the client. "amq" is the name used in Artemis, by default, for a "superuser" role -- a user that has access to all message queues and to the administration console. Of course, in practice, we would be creating multiple roles with different users.

8. Select "Users" from the left-hand side-bar menu, and create a new user. I'm using "user1". In the "Credentials" tab for the new user, fill in the password (and configuration) and click "Reset Password".

9. In the "Role Mappings" tab for the user, do not select any realm roles. Instead, select "artemis-broker" under "Client Roles" and add the client role "amq" (it should be the only available selection).

The new user should look like this, in the Keycloak console:

Screenshot of Keyclock console

This completes the Keycloak set-up. We have created an authentication role with one "client", one client role "amq", and one user "user1" in that role. The authentication URL, that clients will use, will be http://host:8080/auth", where "host" might be "localhost" in a simple test, and that's what I will assume here. You'll need this URL when setting up the authentication provider in Artemis.

Installing and setting up Artemis

1. Obtain Apache Artemis I'm testing using the May 5 build; the Red Hat productised version, AMQ 7.9, also works fine.

2. Unpack the distribution, and create a new broker instance by running bin/artemis create foo. Again, choose a more imaginative name than "foo" if you prefer. Answer the questions about administrator IDs, but bear in mind that the answers will not be used, as we will be overwriting the log-in configuration files.

Don't make any changes to etc/broker.xml -- the stock configuration creates one user role "amq" with all access to everything. That is sufficient for basic testing.

3. Create the file etc/keycloak.json, which will be used by the Keycloak log-in module in Artemis.

{
  "realm": "foo",
  "resource": "artemis-broker",
  "auth-server-url": "http://localhost:8080/auth",
  "use-resource-role-mappings": true,
  "principal-attribute": "preferred_username",
  "ssl-required": "external",
  "credentials": {
    "secret": "foo"
  }
}

In this configuration, "resource" denotes the Keycloak "client" we created earlier. The "secret" value is arbitrary -- it will be used in the OAuth challenge, but is not actually check with the current configuration. See below for more on this subject.

4. Edit etc/login.config in the broker instance directory, so that it looks like this:

activemq {
    org.keycloak.adapters.jaas.DirectAccessGrantsLoginModule required
      keycloak-config-file="${artemis.instance}/etc/keycloak.json"
      role-principal-class=org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal
    ;
    org.apache.activemq.artemis.spi.core.security.jaas.PrincipalConversionLoginModule required
      principalClassList=org.keycloak.KeycloakPrincipal
    ;
};

Note that this file references the keycloak.json created earlier. The JAAS realm name "activemq" is a predefined, default name. Artemis can use different JAAS realms for different subsystems, and it will usually be appropriate to do so -- see below. In this trivial example, however, all authentication will use the same settings.

5. Start the Artemis broker (bin/artemis run). Look for a message in the log similar to the following:

2022-05-24 14:11:07,937 INFO  [io.hawt.web.auth.AuthenticationConfiguration]
Starting hawtio authentication filter, JAAS realm: "activemq" authorized
role(s): "amq" role principal classes:
"org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal"

6. Verify that you can log into the Artemis web console (localhost:8161/console) using the credentials user1/user1, and no others.

7. To test that messaging clients are being authenticated correctly, you can use the built-in artemis utility. For example:

$ ./bin/artemis producer --user user1 --password user1
Connection brokerURL = tcp://localhost:61616
Producer ActiveMQQueue[TEST], thread=0 Started to calculate elapsed time ...
...
Producer ActiveMQQueue[TEST], thread=0 Elapsed time in milli second : 7022 milli seconds

Extending the configuration

Configuring which users can do what on which message queues is not specifically a Keycloak issue -- all that Keycloak does is supply the user roles; Artemis will use its own configuration (in broker.xml) to decide whether an operation is allowed.

In this trivial example, I've used a single user to test both messaging clients and administration -- this is unlikely to be a satisfactory arrangement in practice. Artemis does not really have fine-grained control of the console: if a user can access the console, that user can do everything that the console allows. To grant access to the console only to specific users, create a new "console" role (again, this is a "client" role) in Keycloak, and assign that role to the relevant users. Then, to configure Artemis to allow only that role to access to console, edit etc/artemis.profile and change HAWTIO_ROLE=amq to HAWTIO_ROLE=console.

If you want to use a completely different authentication configuration for the console -- not just a different role -- then you can create a new JAAS realm in login.config. Set the Java system property hawtio.realm to indicate the new realm. This realm can reference completely different Keycloak configuration files, if necessary.

Closing remarks

It's not difficult to configure Artemis to use Keycloak for authentication -- so long as the requirements are very simple. In this article, I've presented what I believe to be the simplest possible, working configuration.

What I've described in this article is a "direct access" authentication scheme. By that I mean that the client has no authentication user interface, and just provides credentials that Keycloak checks. A client that has a web interface will more usually redirect users to the Keycloak log-in screen, and complete authentication that way, before being directed back to the application. This requires a somewhat more complicated set-up.

"Direct access" clients typically have an additional layer of authentication, being asked to provide a "secret" value known to Keycloak, as well as its regular credentials. A Keyclock client has an "Access Type" setting that defaults to "public", meaning that no secret value is required. Thus we were able to set the secret to "foo" in the AMQ configuration.

To assign a secret to a specific Keyclock client, set the Access Type to "confidential", and then go to the "Credentials" page of the client. There you will be able to see, or edit, the secret value, which can then be copied to the AMQ configuration.

Later versions of Artemis contain an example, examples/features/standard/security-keycloak/, that demonstrates a more complex set-up, in which messaging clients and console administrators have completely different Keycloak configurations.

Addendum -- what I had to do differently in Keycloak 24.0

1. A few of the user interface elements are in slightly different places in the web console in 24.0. I hope these differences are slight, but it might be necessary to hunt around in the UI a little, to find the settings I mention.

2. In 18.0, I configured AMQ to use the authentication URL /auth. This doesn't work in 24.0, and you'll see this error message in AMQ:

WARN  [org.keycloak.adapters.jaas.DirectAccessGrantsLoginModule] Login failed. 
Invalid status: 404, OAuth2 error. Error: Unable to find matching target 
resource method, Error description: For more on this error consult the server 
log at the debug level.

That's a genuine 404 error, because the URL /auth doesn't go anywhere. I found that I didn't need a path in the URL at all with 24.0, so in keycloak.json I have:

  "auth-server-url": "http://localhost:8080/",

What happens is that the keycloak adapter makes a request on /realms/foo/.well-known/openid-configuration to find the OAuth endpoints, so we don't need to set one explicitly.

3. With 24.0 I found that I couldn't use a minimal user, with nothing but a username and password. When I set this up, I got the following error message in AMQ:

WARN  [org.keycloak.adapters.jaas.DirectAccessGrantsLoginModule] Login failed. 
Invalid status: 400, OAuth2 error. Error: invalid_grant, Error description: 
Account is not fully set up

I think the user needs at least a verified email address, or we need to disable email verification completely. Since a direct grant user has no user interface, we can't expect the user to verify the email address. In the settings page for the user, there is a checkbox for "Email verified". However, that doesn't work on its own -- the user actually needs an email address, even if it's a bogus one.

Alternatively, select the 'Authentication' menu for the realm (leftmost pane in the UI), Select the 'Required actions' tab, and turn off 'Verify Email'. Of course, this method affects all users.