Disclaimer: this project is a Proof of Concept which connects the WebSEAL DSC client interface with a Redis backend. I don’t recommend doing this in a live environment because it may render your ISAM unstable.
Edit 1/02/2021: this concept has now been integrated in WebSEAL natively: https://www.ibm.com/support/knowledgecenter/SSPREK_10.0.1/com.ibm.isva.doc/wrp_config/concept/con_redis_session_cache.html
In traditional IBM Security Access Manager deployments, one would have one or multiple Distributed Session Cache (DSC) servers, but only 1 is ever truly active while the others remain hot-standby. In an elastic environment, this becomes a nuisance because you’d still need 2 or more separate DSC appliances to support distributed sessions.
However, more modern technologies exist, one of which is Redis. Redis is basically a large in-memory data structure store which can scale horizontally, unlike the DSC.
Using Redis, the limit of 4 DSC servers is removed and thus allows the ISAM environment to scale accordingly.
You could choose another backend as well, I took Redis since it’s readily available and is a nice example of the concept.
The idea originated from a discussion which I had with IBM during the IBM MasterSkills University in Munich 2019, which promptly triggered me to reimplement this on a Redis using Python (links below).
My setup is a simple IBM Security Access Manager V9.0.7 Virtual Appliance which has a multi-node cluster running on it. It has its own policy server and hosts several reverse proxies. The fact that the DSC is active, doesn’t really mean anything, we’ll point the reverse proxies to our own interface anyway.
My Redis is an out-of-the-box application installed on a CentOS 7, following is the installation procedure:
Next you’ll need to configure it to listen on a non-localhost socket:
Save the file and start Redis, simply execute:
Open the firewall as well when you’re on a VM:
I just use the built-in web server from PyCharm when you run the project, which binds to 0.0.0.0 (all interfaces) on port 5000, so make sure that port is open as well. A docker image is also provided which makes this even easier.
WebSEAL uses SOAP calls via HTTP to communicate with the DSC, here’s how I decoded the protocol:
I have documented the protocol roughly, which is available in the GitHub repository as well, but here are some details:
I assume there’s one more which would be something like “updateSession” when the authenticated session performs a step-up, but I must investigate this more in detail.
Configuration is simple, make sure your Python application is running and go to the reverse proxy of choice, configure it to support DSC:
Save and deploy the changes and restart the reverse proxy.
You may want to set “load-balance” to “yes” or use another load balancer to the shims, there’s no more master concept so all hosts can process the request accordingly (this should increase performance as well).
When you have “debug_mode” enabled in the project, you’ll notice the first set of log messages coming in, most importantly the WebSEAL will join the replica-set, I have persisted this in Redis as well, although it doesn’t seem necessary, you can verify it in Redis using:
And to get each instance separately:
Where “default” would be the name of your replica set that has been defined in the Reverse Proxy.
While active, the WebSEAL will send pings. I just reply to these statically because I don’t really see a way to correlate the WebSEAL (except for Client IP) -> ideally, there’d be a cleanup action after a WebSEAL has not sent a ping for a certain amount of time, but that’s for another time.
Next up, point your browser the reverse proxy which has been configured to make use of the shim. If you’ve previously done this, you’ll notice the webserver registering an additional request, this is the “getSession” function. Because the session probably doesn’t exist in Redis, you’re prompted for login. If something were to be wrong with the response from the shim, WebSEAL would show “Service Unavailable” message.
When you perform a login, the shim will consume the “Session ID” and each credential attribute, e.g.:
And will store it separately in Redis using the following concatenation:
The value which is stored is simply the XML data of the attribute.
Beside adding all the credential attributes in Redis, it will also update the “Replica Set” sessions with the new Session ID.
In order to speed up troubleshooting, I have set my “inactive-timeout” to “10”, which means that after 10 seconds of inactivity, WebSEAL will notify the DSC that a session has become inactive. This message is intercepted by the shim as well, and will do one specific action:
Presumably, this credential attribute is then used in the “Get Session” operation to verify whether the session has been marked as idle.
At some point, you’ll re-request the page and WebSEAL may request the information from the DSC again. In my case, the session has been idle for more then 10 seconds, and I see a “getSession” request coming along.
In my implementation, I first verify whether the session is found in the replica set. If the Session ID is found in the replica set, I verify the credential attribute “com.tivoli.am.eb.is-inactive” (which was set in the “idle session” operation) and will act accordingly:
In case the retrieved session was marked as inactive, I clean up all credential attributes, and will also remove the Session ID from the replica set.
I’ve also seen WebSEAL send a “terminateSession” request to the endpoint, which essentially does the same, but I couldn’t determine when WebSEAL triggers this action.
When the WebSEAL is stopped, it sends a “replicaShutdown” and I remove it from the replica set in Redis.
When externalizing the DSC service, it’s important to configure some form of authentication, WebSEAL supports either Basic Authentication or Mutual TLS Authentication. For my proof of concept, I haven’t implemented either.
There’s no tamper protection either when stored in Redis, as such, anyone with access to the Redis can essentially modify the data to spoof any user in the security domain. A “simple” solution could be that the ISAM Reverse Proxies modify the of the session from XML elements to a JWT.
In conclusion, I’ve had a lot of fun reverse engineering the protocol and implementing it in Python to interface the WebSEAL with a Redis. This deployment strategy makes IBM Security Access Manager Docker/OpenShift deployments more sensible in the fact that they can now scale accordingly, whereas otherwise you’d be limited to only 1 active DSC server.
In retrospect, it would’ve been nice if the protocol would in fact be described, I’ve made some gross assumptions on some points and I’m not entirely sure whether these are correct, e.g.: does “idleTimeout” also modify the credential attribute “com.tivoli.am.eb.is-inactive” in vanilla DSC, are the values in “currentKey” and “oldKey” always “iuhew9873hkediuyer987”, … ?
I haven’t touched the Redis Cluster (or multiple Redis servers) either, this may be interesting as well, I assume that key routing based on the Session ID contains enough entropy for distribution. It would also be interesting to see a performance comparison between the two and verify where a Redis becomes more efficient than the vanilla DSC.
If you’re interested in more IBM Security Access Manager on Docker / OpenShift, I suggest catching up on Jon Harry’s work: https://github.com/jonpharry/isamdocker. It’d be interesting to see this implementation paired with an OpenShift deployment.
If you like the stuff I did there, consider liking the article and following us for future content. If you have any questions be sure to let me know as well!
A security enthusiast dedicated to access management. Loves hacking around in products and developing new ideas.