MQ

 View Only

Introducing JWKS in MQ 9.4 for JWT Authentication

By Vasily Shcherbinin posted 13 days ago

  

Introduction

Support for JWT-based authentication has been introduced in MQ 9.3.4, generating significant customer interest. In the new MQ 9.4 LTS release, we have enhanced JWT Support by integrating support for JSON Web Key Sets (JWKS).

JWKS is a standardised approach for managing and distributing cryptographic keys essential for JWT verification. JWKS is important and relevant because it:
    •    Ensures secure key management,
    •    Facilitates public key distribution for JWT verification,
    •    Supports key rotation for improved security,
    •    Offers decentralised and scalable key management.

The inclusion of JWKS support in IBM MQ significantly simplifies the setup process for MQ administrators when utilising the new JWT functionality. It simplifies cryptographic key management and enhances the security and user-friendliness of JWT token authentication in MQ.

In this blog post we will look deeper into how JWKS works in IBM MQ and will be configuring the Queue Manager to use the new JWKS functionality. We will drive a demo to demonstrate JWT authentication using the extjwtexit channel exit. Please note that this functionality is currently only supported on Unix.

How JWKS works

When a queue manager is configured to work with JWKS, on start-up and security refresh it retrieves the JWKS from a URL provided by the token issuer (such as an authorization server). The JWKS contains one or more JWKs, each with metadata like key type (e.g., RSA or EC), key usage (e.g., signature), and a unique key ID (kid). The QM matches the key ID in the JWT header with the corresponding key in the JWKS. It then uses this public key to verify the JWT’s signature, ensuring the token has not been tampered with and is indeed issued by a trusted source. This process allows secure and dynamic key management, as keys can be rotated or revoked without changing the clients' implementation. 


Prerequisites

An assumption is made that you already have a running authentication server that would be able to issue JWT tokens. The authentication server used in this blog was Keycloak, an open-source identity and access management solution aimed at modern applications and services.

HTTPS is used to create a secure connection between the queue manager and the Keycloak server.

If you do not have a Keycloak server available, you can proceed with the following steps:

1. Create a local keystore (with password 'password’) in /tmp using the runmqakm command :

/opt/mqm/bin/runmqakm -keydb -create -db /tmp/auth_serv.p12 -pw password -type pkcs12 -stash

2. Create a local HTTPS certificate with label keycloakhttps:

/opt/mqm/bin/runmqakm -cert -create -db /tmp/auth_serv.p12 -stashed -label keycloakhttps -dn "CN=localhost:32030,O=MYORG,L=MYCITY,ST=MYSTATE,C=GB" -size 4096 -expire 365

3. You can check if the certificate was successfully created by running:

/opt/mqm/bin/runmqakm -cert -list -db /tmp/auth_serv.p12 -pw password

4. Start the Keycloak container using Podman:

podman run -p 32030:32030 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=passw0rd -v /tmp:/tmp quay.io/keycloak/keycloak:latest start --hostname-strict=false --https-key-store-file=/tmp/auth_serv.p12 --https-key-store-password=password --https-port=32030

You can check that your server works and serves both JWT and JWKS by running the following commands in the terminal.

To fetch a JWT Token:

curl -k -X POST "https://localhost:32030/realms/master/protocol/openid-connect/token" -H "Content-Type: application/x-www-form-urlencoded" -d "username=<username>" -d "password=<password>” -d "grant_type=password" -d "client_id=admin-cli"


To fetch a JWKS response:

curl -k -X GET https://localhost:32030/realms/master/protocol/openid-connect/certs


Note that your keycloak server address and credentials will be different, so make sure to use the correct values.


Setting up the C exit

To provide token authentication capability, we will be using the extjwtexit channel exit published on Github in the mq-exits repo (Link to Github repo). When called as a security exit on a Client-Connection channel, the exit will connect to an external token issuer server, retrieve a JWT Token, and adopt a pre-configured user from the Token to authenticate to the IBM MQ Queue Manager.

You can also try JWKS by using the IBM MQ samples directly. The MQ sample C programs amqsput/amqsputc and amqsget/amqsgetc were updated to demonstrate the use of JWTs, but this involves ‘manual cut-and-pasting’ of tokens. Using the C client channel exit is the alternative way to obtain the token. The exit adds the token to the MQCSP for MQCONNX call. This avoids the need to manually paste tokens when testing with amqsputc or amqsgetc.

Follow the instructions on Github (steps 1 & 2) to compile the exit, and steps 13 to set the extjwtexit environment variables.

Setting up JWKS on the Queue Manager

We can now begin with the Queue Manager creation and setup. Create the Queue Manager and create the necessary Queue Manager objects. For simplicity’s sake, we can disable CHLAUTH rules and set the SSLCAUTH rules on the SVRCONN as OPTIONAL.


crtmqm DEMO
strmqm DEMO
runmqsc DEMO
      ALTER QMGR CHLAUTH(DISABLED)
      DEFINE LISTENER(LIST1) TRPTYPE(TCP) PORT(1513) CONTROL(QMGR)
      START LISTENER(LIST1)
      DEFINE CHANNEL(CHAN1) CHLTYPE(SVRCONN) SSLCAUTH(OPTIONAL)
      DEFINE QLOCAL(Q1)


Proceed with exporting the Queue Manager environment variables so that our sample programs can connect:

export MQCHLLIB=/var/mqm/qmgrs/DEMO/@ipcc
export MQCHLTAB=AMQCLCHL.TAB


We are now ready to setup the JWKS credentials that we will need to verify an incoming JWT token. The setup is made inside the Queue Managers qm.ini file located in your Queue Manager installation directory. If you installed MQ in default location, it will be in /var/mqm/qmgrs/DEMO/.

In the SSL stanza, add the following line:

   HTTPSKeyStore=<path-to-HTTPS-keystore>


This is required to let the queue manager know the location of the HTTPS Key store that we have created earlier. This will allow us to do secure HTTPS communication with the authentication server.

To supply JWKS credentials in the qm.ini file, add a new JWKS stanza to the end of the file. For example:

JWKS:
   IssuerName=https://localhost:32030/realms/master
   Endpoint=https://localhost:32030/realms/master/protocol/openid-connect/certs
   UserClaim=preferred_username


It is essential that these credentials are correct. To verify what the values for each field should be, you can use the jwt.io website and investigate the values stored in the incoming token. The IssuerName field value should correspond to the JWT token “iss” claim value, the Endpoint should correspond to the Endpoint that serves JWKS credentials and the UserClaim should match that of the Keycloak user – the preferred_username value is an easy way to reference the user’s name without hardcoding it into the config.

After you’re done configuring the queue manager qm.ini file, restart the queue manager by issuing endmqm/strmqm, or issue REFRESH SECURITY via runmqsc. This step is crucial so the new qm.ini config is picked up and acter on by the Queue Manager.

Finally, set the correct authority records on the queue manager. The Principal should be set to that of the incoming user from the JWT Token. The SCYDATA attribute when defining the CLNTCONN channel can be used to specify, how chatty the output should be - if set to DEBUG, the output will be more chatty.

SET AUTHREC OBJTYPE(QMGR) PRINCIPAL('admin') AUTHADD(CONNECT)
SET AUTHREC PROFILE(Q1) OBJTYPE(QUEUE) PRINCIPAL('admin') AUTHADD(ALLMQI)
DEFINE CHANNEL(CHAN1) CHLTYPE(CLNTCONN) CONNAME('127.0.0.1(1513)') QMNAME(DEMO) SCYEXIT('extjwtexit(ChlExit)') SCYDATA(DEBUG)


Run the exit and confirm it works

To run the exit you can leverage the amqsputc sample program.

/opt/mqm/samp/bin/amqsputc Q1 DEMO


You should now be able to put a message on the queue Q1.

Notice that a new folder was created in /var/mqm/qmgrs/DEMO – tokens. In this folder, we keep the list of issuers that we have set up on our queue manager – index.txt, as well as the latest JWKS response from a particular issuer – inside issuer1.json file. This information is then used to keep the system running in problematic situations, for example if the specified endpoint cannot be reached due to a network problem, etc. 

If something goes wrong:

If something goes wrong and the configuration is not quite right, you can find the appropriate error logs in the queue manager error directory. Scroll to the bottom of the error log and you will see error messages that explain what the problem was. Refer to this document in the Knowledge Centre for more information on what the different error codes mean and how to resolve the issue: Token authentication error codes .

1 comment
18 views

Permalink

Comments

9 days ago

I see that the exit you mention is only written for use on Unix, and does not support Windows. When will this be available on Windows? And is it worth mentioning this restriction on platform support in your post?