redhat Posts

Intercepting and Read JMS Messages on Red Hat AMQ

Red Hat AMQ, or its OpenSource version which is ActiveMQ, is a JMS 1.1-compliant messaging system. It consists of a broker and client-side libraries that enable remote communication among distributed client applications. Red Hat AMQ provides numerous connectivity options and can communicate with a wide variety of non-JMS clients through its support of the OpenWire and STOMP wire protocols.

So basically it’s a very reliable messaging system, but there are times where we need to capture whatever messages that comes thru AMQ for some debugging purpose. And I can see that AMQ have an interceptor feature that we can leverage to capturing JMS messages that goes thru AMQ.

https://access.redhat.com/documentation/en-us/red_hat_amq/7.2/html/using_amq_broker/interceptors

Creating an interceptor class is actually quite straight forward, lets start with a simple POM file

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.edw</groupId>
    <artifactId>AmqInterceptor</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>artemis-jms-client-all</artifactId>
            <version>2.17.0</version>
        </dependency>

    </dependencies>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

</project>

And a simple java file,

package com.edw;

import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.api.core.ActiveMQException;
import org.apache.activemq.artemis.api.core.Interceptor;
import org.apache.activemq.artemis.api.core.Message;
import org.apache.activemq.artemis.core.protocol.core.Packet;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionReceiveMessage;
import org.apache.activemq.artemis.core.protocol.core.impl.wireformat.SessionSendMessage;
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;

import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.List;

public class SimpleInterceptor implements Interceptor {
    public boolean intercept(final Packet packet, final RemotingConnection connection) throws ActiveMQException {

        try {
            if (packet instanceof SessionSendMessage) {
                SessionSendMessage realPacket = (SessionSendMessage) packet;
                Message msg = realPacket.getMessage();

                if((msg.getTimestamp()>0) && msg.getUserID()!=null) {
                    ActiveMQBuffer activeMQBuffer = realPacket.getMessage().getBodyBuffer();

		    // it will write the log to a file. Dont use this, use a real logging mechanism such as slf4j
                    List<String> lines = Arrays.asList( "***** msg in *****", msg.toString(), activeMQBuffer.readNullableSimpleString().toString(), "*****");
                    Files.write(Paths.get("amq.out"), lines, 
						StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
                }
            }
            else if (packet instanceof SessionReceiveMessage) {
                SessionReceiveMessage realPacket = (SessionReceiveMessage) packet;
                Message msg = realPacket.getMessage();

                if((msg.getTimestamp()>0) && msg.getUserID()!=null) {

		    // it will write the log to a file. Dont use this, use a real logging mechanism such as slf4j
                    List<String> lines = Arrays.asList( "***** msg out *****", msg.toString(), "*****");
                    Files.write(Paths.get("amq.out"), lines, 
						StandardCharsets.UTF_8, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return true;
    }
}

Build by using below command,

mvn clean package

And put the created jar file into amq_broker_home/lib folder. Restart AMQ and we can see that messages that goes in and out of AMQ is now can be logged.

Code for this can be found here,

https://github.com/edwin/amq-message-interceptor

Fixing “SSL routines:tls_process_ske_dhe:dh key too small” on Containerized RHEL8

I have a very unique error today, so basically my RHEL 8 (Red Hat Enterpise Linux) cannot connect to another system due to SSL issue. The exception is quite clear, and can be seen below.

error:141A318A:SSL routines:tls_process_ske_dhe:dh key too small

It is quite easy to do it in a standalone infrastructure, but this problem happen on a containerized application which make it much more complicated.

After searching for a solution, i come up with this Dockerfile

FROM registry.redhat.io/application/application-rhel8:7.8.0

user root
RUN update-crypto-policies --set LEGACY 

user 185

Build it,

docker build -f Dockerfile -t application-rhel8-modified:7.8.0 .

Deploy it, and i can see that the previous error is no longer exist.

Fixing Error “null username” when Integrating RedHat Single Sign On to Active Directory

Previously never had any issue when integrating RedHat SSO (Keycloak) to LDAP, but now got a very weird issue because now im trying to connecting RHSSO to Microsoft Active Directory instead of standard LDAP.

One biggest difference is that ActiveDirectory is using “sAMAccountName” field for user primarykey mapping, and somehow RHSSO is always get null value when trying to synchronize with existing user. Below is the complete stacktrace.

11:59:45,031 ERROR [org.keycloak.storage.ldap.LDAPStorageProviderFactory] (default task-122) Failed during import user from LDAP: 
org.keycloak.models.ModelException: User returned from LDAP has null username! 
Check configuration of your LDAP mappings. Mapped username LDAP attribute: sAMAccountName, 
user DN: CN=XXX,OU=User,OU=HO,DC=llll,DC=co,DC=id, attributes from LDAP: 
{whenChanged=[20191016020643.0Z], whenCreated=[20170105023800.0Z], mail=[xxx@lll.co.id], givenName=[cccc], sn=[dddd], cn=[ccccc dddd], userAccountControl=[512], pwdLastSet=[132156652033202194]}
	at org.keycloak.storage.ldap.LDAPUtils.getUsername(LDAPUtils.java:113)
	at org.keycloak.storage.ldap.LDAPStorageProviderFactory$3.run(LDAPStorageProviderFactory.java:542)
	at org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction(KeycloakModelUtils.java:227)
	at org.keycloak.storage.ldap.LDAPStorageProviderFactory.importLdapUsers(LDAPStorageProviderFactory.java:535)
	at org.keycloak.storage.ldap.LDAPStorageProviderFactory.syncImpl(LDAPStorageProviderFactory.java:490)
	at org.keycloak.storage.ldap.LDAPStorageProviderFactory.sync(LDAPStorageProviderFactory.java:428)
	at org.keycloak.services.managers.UserStorageSyncManager$2$1.call(UserStorageSyncManager.java:107)
	at org.keycloak.services.managers.UserStorageSyncManager$2$1.call(UserStorageSyncManager.java:102)
	at org.keycloak.cluster.infinispan.InfinispanClusterProvider.executeIfNotExecuted(InfinispanClusterProvider.java:78)
	at org.keycloak.services.managers.UserStorageSyncManager$2.run(UserStorageSyncManager.java:102)
	at org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction(KeycloakModelUtils.java:227)
	at org.keycloak.services.managers.UserStorageSyncManager.syncAllUsers(UserStorageSyncManager.java:92)
	at org.keycloak.services.resources.admin.UserStorageProviderResource.syncUsers(UserStorageProviderResource.java:142)
	at sun.reflect.GeneratedMethodAccessor891.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)

It turns out that i have to mapping “sAMAccountName” field to username. Can find the complete screenshot below,

Cheers (^)