OpenID Connect is an authentication protocol that enables applications to authenticate users with external authentication services, referred to as OpenID Providers, such as: email, social networks or IAM systems without the user needing to send credentials to the application. It also allows applications to query information about a user to create an identity that the resource server can use for authorization. With OpenID Connect, how the user authenticates is controlled by the OpenID Providers and so enables applications to support multiple different authentication mechanisms without modifying the application code to implement them.
In part one of this blog post series, we looked at how you can configure the IBM MQ Console to authenticate users with the IBM Cloud App ID service. If you have not read the first part you can find it here: https://community.ibm.com/community/user/integration/blogs/robert-parker1/2022/08/17/authenticating-to-the-ibm-mq-console-with-the-open
An issue with the current configuration is that any user that can authenticate with the App ID service now can access the MQ Console as a full administrator. In this blog post we will look at how we can adjust the configuration to support a finer grain control over what users from the App ID service can and cannot administrate via the MQ Console.
Contents
Introduction
To have greater control over what users can access the MQ Console we have two options:
- We can use the MQUser role and IBM MQ Authority records
- We can use groups within the App ID Service
Both options have advantages and disadvantages which will need to be carefully weighed to determine which is the best option to use.
Before we look at the options, we should gain a better understanding of how the MQ Console works.
When you start the MQ Console via the strmqweb command a liberty server running the MQ Console is started, at this point the MQ Console will discover locally running queue managers and open a local bindings connection to them as the user it is running as. As you run the strmqweb command as mqm meaning the MQ Console has full administrative access to perform any task.
When a user connects to the MQ Console, authentication is performed, and access is granted. Whenever they perform any action within the MQ Console, the MQ Console performs that action against the queue manager on the user’s behalf.
However, when performing the action against the queue manager the MQ Console has two choices on who it performs the action as, either the user the MQ Console is running as (mqm) or the authenticated user.
Which option it chooses depends on which security role the user in the MQ Console has been granted. In our previous blog post we added every authenticated user to the MQWebAdmin security role. This role tells the MQ Console to perform each action as the user the MQ Console is running as. The alternative is the MQWebUser security role.
Allowing fine grain access control
Using the MQ Console MQUser role
By using the MQWebUser security role we can authenticate our users in the App ID service but then defer authorisation decisions to the queue manager and its authority records. To support this, we need to do the following:
- We need to choose a field to adopt as a user’s identity from the App ID Service’s token.
- We need to modify the console configuration to use the MQWebUser security role.
- We need to ensure that the queue manager can recognise the users.
- We need to configure the queue manager authority records for the users.
Looking at the App ID token
If we want to use the MQUser role, then we need to find a user id that we can present as our identity for when the MQ Console performs an action against the queue manager. After the App ID service performs the authentication, it redirects the user back to the MQ Console with a token showing that it has been authenticated. There are several distinct kinds of OIDC flows that can happen but liberty will be able to get some identity information that it can trust for the user that has been authenticated. It is from this information that we will get the identity we will be adopting.
The information that App ID returns to liberty is stored within a JWT, the payload of which looks as such:
{ "iss": "https://eu-gb.appid.cloud.ibm.com/oauth/v4/<tenant ID>", "aud": [ "<ClientID>" ], "exp": 1660138957, "tenant": "<tenant ID>", "iat": 1660135357, "email": "parrobe@uk.ibm.com", "name": "Rob Parker", "sub": "666fbf05-b302-454e-9b9f-a93cdf4428f8", "email_verified": true, "preferred_username": "parrober", "given_name": "Rob", "family_name": "Parker", "identities": [ { "provider": "cloud_directory", "id": "d9fa6290-88a5-441f-97e3-a532b418ba8d" } ], "amr": [ "cloud_directory" ] }
|
Here you can see a few different fields (or claims as they are called) which we could use. Because IBM MQ has a limit on userid size, twelve characters, we are limited to a couple of them. sub, email and name are all too long, given_name and family_name are not unique.
The preferred_usename claim should be unique for our purposes and is less than 12 characters, as such we will use this for obtaining an identity.
Note: While here the username is short enough for IBM MQ it is the responsibility of the OIDC administrators to ensure that future users added also conform to this limit otherwise the MQ Console will reject the user for being too long.
Modifying the MQ Console configuration
Now that we have the identified the claim that we will be adopting we now need to modify the MQ Console configuration to use that identity. Luckily, the changes we need to make are quite small.
First, we need to tell the MQ Console that any users that liberty authenticated should now be authorized per the queue manager’s authority records:
<enterpriseApplication id="com.ibm.mq.console"> <application-bnd> <security-role name="MQWebUser"> <special-subject type="ALL_AUTHENTICATED_USERS"/> </security-role> </application-bnd> </enterpriseApplication>
|
Next, we need to update the openidConnectClient settings to tell liberty to pass the right user identity to the MQ Console after authentication, here we use the userIdentifier setting to point to the correct claim:
<openidConnectClient id="mqclient" clientId="<ClientID>" clientSecret="<ClientSecret>" discoveryEndpointUrl="<DiscoveryURL>" userIdentifier="preferred_username" > </openidConnectClient>
|
At this point if we restart the MQ Console to pick up the new settings we can try connecting to the MQ Console. You will find that on connect we get redirected to the App ID Service as before and can authenticate there and get returned to the MQ Console. On the surface it will seem that everything is normal, however if you try to look at any queue managers you will see an error:
This is the queue manager blocking us as it does not recognise the user, in the queue manager error logs you will see many error messages for:
10/08/2022 15:39:47 - Process(14600.4) User(MUSR_MQADMIN) Program(amqzlsa0.exe) Host(IBM-PF3VVAS4) Installation(Installation1) VRMF(9.3.0.0) QMgr(qm1) Time(2022-08-10T14:39:47.073Z) CommentInsert1(parrober)
AMQ8075W: Authorization failed because the SID for entity 'parrober' cannot be obtained.
EXPLANATION: The Object Authority Manager was unable to obtain a SID for the specified entity. This could be because the local machine is not in the domain to locate the entity, or because the entity does not exist. ACTION: Ensure that the entity is valid, and that all necessary domain controllers are available. This might mean creating the entity on the local machine.
|
Adjusting the machine and queue manager configuration
To fix the error from before we need to do two things:
- We need the queue manager to recognise the user ‘parrober’
- We need to provide authorities to ‘parrober’
There are several options for us to create users and assign it appropriate authorities:
For windows I can either create a copy of each user locally, add them to groups and then assign authorities to that group.
I can also do the same for Linux, alternatively I could use the UserExternal functionality to remove the need to create the users. However, when enabled I would then need to assign authorities to each user individually.
For both Linux and Windows, I could also configure the queue manager to use LDAP as it is user registry.
Once I have made the queue manager recognise the user, I then need to provide appropriate authorities. If I want to elevate a user to administrator I can either add them to the mqm group or alternatively I can grant them the necessary authorities manually.
This IBM Documentation page details the necessary authorities to use MQ Explorer as an administrator which we can use this as a starting point to manually grant a user authority. I provided the following authorities to my parrober user:
- OUTPUT (put) authority to the queue, SYSTEM.REST.REPLY.QUEUE
- INPUT (get) authority to the queue, SYSTEM.REST.REPLY.QUEUE
- INPUT (browse) authority to the queue, SYSTEM.REST.REPLY.QUEUE
- INQUIRE authority to the queue, SYSTEM.REST.REPLY.QUEUE
- OUTPUT (put) authority to the queue, SYSTEM.ADMIN.COMMAND.QUEUE
- DISPLAY authority to the target queue manager object
At this point the MQ Console stopped generating errors in the queue manager logs, however if I tried to do anything I still got an error due to missing authorities. For example, I tried to list the queues but lacked display authorities for each queue. From here I followed this IBM Documentation page to work out and grant the authorities that I expected the parrober account to have.
Conclusions
While this option allows us a greater control over what users can access each queue manager, it does add the extra overhead of needing to manage the authority records for each user to access the queue manager. Additionally, because the queue manager needs to recognise the user, we must configure a user registry the queue manager can search for group membership information against (or disable this ability using the UserExternal functionality). In theory, if your OIDC server also supports LDAP queries you could configure the queue manager to use LDAP and point it at the OIDC server. Another issue is that your OIDC server must issue a token that has a field which can be used for identity and that identity must be twelve characters or less in length. IBM MQ cannot adopt an identity of longer than this due to limits within the message header sizes which are included in the PCF messages that perform the actions against the queue manager.
With this mechanism you do get the benefit of being able to audit which administrators are performing each action against your queue managers. By enabling event messages every action, the MQ Console performs against the queue manager will generate command and configuration events; these events will detail what action was performed and which user performed them in the MQCACF_EVENT_USER_ID field of the event message.
Using groups within App ID
While the previous example enabled you to track which users were performing which actions against the queue manager and control exactly which users could access each queue manager, it creates additional requirements both for your queue manager (authority record management) and the user registry that the queue manager was configured to use (creating users, groups). If you do not need to audit your administrators via the IBM MQ Event messages, then a simpler option could be to use the App ID service for both authentication and authorization.
Most OIDC services support the ability to provide group or role membership information for the users registered with it and App ID is no exception. To support the group membership information in the App ID service we need to make the following changes:
- We need to add each user a group in the App ID service.
- We need to modify the App ID token to provide this information.
- We need to modify the MQ Console configuration to only provide users in certain groups the MQWebAdmin security role.
Adding the users to a group in the App ID Service
First, we need to add the users in the App ID Service into a role that we can authorize as an administrator in the MQ Console. In the App ID Service main menu go to the Roles menu under profiles and Roles and click Create Role.
Here I have created a Role called “AllLibertyAdmins”
After creating the role, we need to go back to each of the users and add them to the role. Go to the User Profiles menu and select each user adding the Roles to the ones you want:
At this point our users in the App ID service have been assigned to roles that we can use for authorization. However, if we look at the token, we will notice that it does not have this information in it. To get the role information into the token so we can use it for our identity we need to customize the App ID service.
Customizing the App ID token
By default, the App ID service tokens do not contain the role information, so the MQ Console cannot assign authorities based on those groups. Luckily customizing the token is well documented on the App ID Service documentation and even shows an example of how to add the roles to the access token in the ‘Configuring Tokens’ section.
I added the "source": "roles" to both my access tokens and ID tokens which ended up with my tokens looking like this:
{ "iss": "https://eu-gb.appid.cloud.ibm.com/oauth/v4/<tenant ID>", "aud": [ "<ClientID>" ], "exp": 1660138957, "tenant": "<tenant ID>", "iat": 1660135357, "email": "parrobe@uk.ibm.com", "name": "Rob Parker", "sub": "666fbf05-b302-454e-9b9f-a93cdf4428f8", "email_verified": true, "preferred_username": "parrober", "given_name": "Rob", "family_name": "Parker", "identities": [ { "provider": "cloud_directory", "id": "d9fa6290-88a5-441f-97e3-a532b418ba8d" } ], "amr": [ "cloud_directory" ], "roles": [ "AllLibertyAdmins" ] }
|
Here you can see the new claim “roles” has been added and contains the “AllLibertyAdmins” role. We are now ready to update the MQ Console configuration to use that information.
Configuring MQ Console for groups
Finally, we now need to update the MQ Console configuration to act on the roles that we have assigned each user in the App ID Service.
First, we will update the openidConnectClient feature configuration:
<openidConnectClient id="mqclient" clientId="<ClientID>" clientSecret="<ClientSecret>" discoveryEndpointUrl="<Discovery URL>" userIdentifier="preferred_username" groupIdentifier="roles" realmName="robRealm" > </openidConnectClient>
|
Here we use the groupIdentifier setting to indicate that group information is stored in the roles claim. I have also included the userIdentifier setting to choose which claim to pull a username from, which will be obvious why later. Finally, I also indicated that any user information generated should be assigned to the robRealm realm. The reason I need this is because Liberty needs to be able to match the user’s groups to an existing group in a realm.
Because we are now asking liberty to adopt some identity information, we need to define the group in a registry:
<basicRegistry id="basic" realm="robRealm"> <group name="AllLibertyAdmins" /> </basicRegistry>
|
Here you can see I have created a basic registry in the realm “robRealm” that contains the “AllLibertyAdmins” group. This will be used by liberty to be able to tie together a user and the MQ Console roles, which we now need to configure:
<enterpriseApplication id="com.ibm.mq.console"> <application-bnd> <security-role name="MQWebAdmin"> <group name="AllLibertyAdmins" realm="robRealm"/> </security-role> <security-role name="MQWebUser"> <special-subject type="ALL_AUTHENTICATED_USERS"/> </security-role> </application-bnd> </enterpriseApplication>
|
Finally, we assign any users that are in the “AllLibertyAdmins” group to the MQWebAdmin security role. Thereby granting them admin access to the MQ Console and queue managers. If a user authenticates who is not in this role, then we default to using the MQWebUser security role instead.
At this point, after a restart of the console, any users who are in the AllLibertyAdmins role in the App ID service can authenticate and administrate queue managers. Users who are not in that role can authenticate but can only administrate queue managers if they have been granted the necessary authorities on the queue manager.
Conclusions
Apart from the MQWebUser part, this mechanism proves simpler to manage as you do not need to create users and authorities for the queue manager to be able to recognise. Instead, you could offload all the user and group membership into the OIDC server managing it there. New users could be quickly added to the necessary roles to grant them administrative actions to all the queue managers.
You do also gain the benefit of being able to audit what actions each administrator takes, but the principal of the user is stored in the MQCACF_EVENT_APPL_IDENTITY field instead. Additionally, this field has a limit of thirty-two characters which may not be enough for your auditing needs.
Final thoughts
In this blog series we looked at integrating an OIDC server into our MQ infrastructure as the mechanism for authenticating and authorizing our MQ Administrators who use the MQ Console. While we focused on the App ID Service as our OIDC provider, in theory any OIDC compliant provider should be able to be used with tweaks to the configuration to match their expectations and output.
By using OIDC servers to authenticate administrators we can control the flow of sensitive userids and passwords to just the security servers instead of needing to send them to the resource servers (MQ Console). Additionally, because the mechanism of authentication is not monitored by the MQ Console, the OIDC server can issue as many challenges as it wants to users before authenticating them. This means you could enable Multi-factor authentication on your OIDC server and use that to provide a greater level of security.
Resources
Below I have included links to various pages that were mentioned throughout the blog series as well as some additional useful ones: