Implementing a Custom Preprocessor in HiveMQ Enterprise Security Extension (ESE)
This article demonstrates how to implement a custom preprocessor using the HiveMQ Enterprise Security Extension (ESE) Customization SDK. The example shows how to examine the Common Name (CN) in a client certificate and override the authorization role key with a default value if the CN exceeds a certain length.
Prerequisites
HiveMQ Enterprise Edition with the Enterprise Security Extension enabled.
Ready Keystore and trustore certificates. (You can also follow our documentation to create self-signed certificates if not ready already)
Java development environment (JDK 21+).
Access to the HiveMQ ESE Customization SDK and valid Security extension license
Familiarity with MQTT, TLS, and X.509 certificates.
Instructions
Skip this step if you already configured HiveMQ config.xml for mTLS:
Configure
tls-tcp-listener
listener for enabling mtls connection;<tls-tcp-listener> <port>8883</port> <name>tls-cert-required-listener</name> <bind-address>0.0.0.0</bind-address> <tls> <keystore> <path>conf/hivemq.jks</path> <password>changeme</password> <private-key-password>changeme</private-key-password> </keystore> <client-authentication-mode>REQUIRED</client-authentication-mode> <truststore> <path>conf/hivemq-trust-store.jks</path> <password>changeme</password> </truststore> </tls> </tls-tcp-listener>
Create the Custom Preprocessor:
Clone the reference customization SDK project:
HiveMQ ESE Hello World Customization (GitHub)Create the
EseVariableCommonPreprocessor
class with the following logic:package com.hivemq.extensions.enterprise.security.customizations.helloworld; import com.hivemq.extensions.enterprise.security.api.preprocessor.*; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collections; public class EseVariableCommonPreprocessor implements CommonPreprocessor { private static final Logger log = LoggerFactory.getLogger(EseVariableCommonPreprocessor.class); @Override public void process( final @NotNull CommonPreprocessorProcessInput input, final @NotNull CommonPreprocessorProcessOutput output) { input.getEseVariablesInput().getAuthorizationRoleKey().ifPresent(authorizationRoleKey -> { log.info("EseVariableCommonPreprocessor Process started, authorizationRoleKey ::: " + authorizationRoleKey); if (authorizationRoleKey.getFirst().length() > 8) { log.info("CN length > 8, setting role key to 'generic-device'"); output.getEseVariablesOutput().setAuthorizationRoleKey(Collections.singletonList("generic-device")); } }); } }
⚠️ Note: This is a simplified example for demonstration and not production-ready.
Run the code and build the Jar file, then copy the resulting JAR into:
/opt/hivemq/extensions/hivemq-enterprise-security-extension/customizations/
Update HiveMQ Enterprise Security Extension
config.xml
. Modify your ESE configuration to include the new preprocessor in the listener pipeline:<listener-pipeline name="tls-pipeline" listener="tls-cert-required-listener"> <allow-all-authentication-manager name="allowAllAuthn"/> <authorization-preprocessors> <x509-preprocessor> <x509-extractions> <x509-extraction> <x509-field>subject-common-name</x509-field> <ese-variable>authorization-role-key</ese-variable> </x509-extraction> </x509-extractions> </x509-preprocessor> <logging-preprocessor> <name>log-cert-pre</name> <level>info</level> <message>Using CN as Role Key='${authorization-role-key}'</message> </logging-preprocessor> <custom-preprocessor> <implementation>com.hivemq.extensions.enterprise.security.customizations.helloworld.EseVariableCommonPreprocessor</implementation> </custom-preprocessor> <logging-preprocessor> <name>log-cert-post</name> <level>info</level> <message>After custom preprocessor: Role Key='${authorization-role-key}'</message> </logging-preprocessor> </authorization-preprocessors> <file-authorization-manager name="fileAuthz"> <realm>rolesRealm</realm> <use-authorization-key>false</use-authorization-key> <use-authorization-role-key>true</use-authorization-role-key> </file-authorization-manager> </listener-pipeline>
Restart the Extension and Test
Restart the HiveMQ ESE extension (or the HiveMQ broker) to apply changes.
Use a MQTT CLI with a certificate to test: Enter the private key password when prompted.
mqtt sub -h localhost -p 8883 -t "test/one-/#" \ --cafile hivemq-server-cert.pem \ --key mqtt-client-key-3.pem \ --cert mqtt-client-cert-3.pem -d
Expected Output in Logs
If configured correctly, you will see logs like:
INFO - HiveMQ Enterprise Security Extension: Started HiveMQ Enterprise Security Extension successfully in 134ms. INFO - Extension "HiveMQ Enterprise Security Extension" version 4.39.0 started successfully. INFO - CERT Pipeline (8883): Using CN='largercnname' as Role Key='largercnname' INFO - EseVariableCommonPreprocessor Process started authorizationRoleKey ::: [largercnname] INFO - CN length is > 8 hence reset authorizationRoleKey=generic-device INFO - CERT Pipeline (8883): Using CN='{{generic-device}}' as Role Key='{{generic-device}}'
This confirms that the custom logic was applied and the role key was overridden based on CN length.