For this demo we are using CamelK to produce/consume and bridge messages between AMQ6 and AMQ7.
- Logged on Openshift with oc command
- Cluster admin access on Openshift
- Locally Installed Camel K Client
Create a namespace for the project.
oc new-project camelk-ssl
As the Cluster admin install the Red Hat Integration - Camel K
Operator on OCP4 using the Operator Hub.
As the Cluster admin install the Red Hat Intergration - AMQ Broker
on OCP4 using the Operator Hub
Create certificates to enable ssl to be secure. You need to use Java8 to ensure compability with AMQ6 and AMQ7. Ensure you change the common name CN=
on the broker to your server domain name.
createcerts.sh
creates the client/broker keystores/truststores. Edit the script to include your common name.
example:
export CLIENT_KEYSTORE_PASSWORD=password
export CLIENT_TRUSTSTORE_PASSWORD=password
export BROKER_KEYSTORE_PASSWORD=password
export BROKER_TRUSTSTORE_PASSWORD=password
#Client Keystore
keytool -genkey -alias client -keyalg RSA -keystore client.ks -storepass $CLIENT_KEYSTORE_PASSWORD -keypass $CLIENT_KEYSTORE_PASSWORD -dname "CN=camelssl-example, O=RedHat, C=UK"
#Broker Keystore
keytool -genkey -alias broker -keyalg RSA -keystore broker.ks -storepass $BROKER_KEYSTORE_PASSWORD -keypass $BROKER_KEYSTORE_PASSWORD -dname "CN=*.apps.cluster-hhl6r.hhl6r.sandbox55.opentlc.com, O=RedHat, C=UK"
#Export Client PublicKey
keytool -export -alias client -keystore client.ks -storepass $CLIENT_KEYSTORE_PASSWORD -file client.cert
#Export Server PublicKey
keytool -export -alias broker -keystore broker.ks -storepass $BROKER_KEYSTORE_PASSWORD -file broker.cert
#Import Server PublicKey into Client Truststore
keytool -import -alias broker -keystore client.ts -file broker.cert -storepass $CLIENT_TRUSTSTORE_PASSWORD -trustcacerts -noprompt
#Import Client PublicKey into Server Truststore
keytool -import -alias client -keystore broker.ts -file client.cert -storepass $BROKER_TRUSTSTORE_PASSWORD -trustcacerts -noprompt
#Import Server PublicKey into Server Truststore (i.e.: trusts its self)
keytool -import -alias broker -keystore broker.ts -file broker.cert -storepass $BROKER_TRUSTSTORE_PASSWORD -trustcacerts -noprompt
oc create secret generic example-amq-secret \
--from-file=broker.ks \
--from-literal=keyStorePassword=password \
--from-file=client.ts=broker.ts \
--from-literal=trustStorePassword=password
Using the AMQ Broker Operator create a new AMQ Broker
.
apiVersion: broker.amq.io/v2alpha4
kind: ActiveMQArtemis
metadata:
name: example-amq
application: example-amq
namespace: camelk-ssl
spec:
deploymentPlan:
size: 1
image: registry.redhat.io/amq7/amq-broker:7.6
requireLogin: false
adminUser: admin
adminPassword: admin
console:
expose: true
acceptors:
- name: amqp
protocols: amqp
port: 5672
sslEnabled: true
sslSecret: example-amq-secret
verifyHost: false
expose: true
Using the AMQ Broker Operator create a new AMQ Broker Address
.
apiVersion: broker.amq.io/v2alpha2
kind: ActiveMQArtemisAddress
metadata:
name: example-testaddress
spec:
addressName: test
queueName: test
routingType: anycast
Using Artemis produce messages on your local machine. Ensure you change the URL and Truststore Location. https://activemq.apache.org/components/artemis/download/
example:
./artemis producer --url 'amqps://example-amq7-amqp-0-svc-rte-camelk-ssl.apps.cluster-hhl6r.hhl6r.sandbox55.opentlc.com:443?jms.username=admin&jms.password=admin&transport.trustStoreLocation=/home/marslan/work/camelkssl/client.ts&transport.trustStorePassword=password&transport.verifyHost=false' --threads 1 --protocol amqp --message-count 10 --destination 'queue://test'
example:
./artemis consumer --url 'amqps://example-amq7-amqp-0-svc-rte-camelk-ssl.apps.cluster-hhl6r.hhl6r.sandbox55.opentlc.com:443?jms.username=admin&jms.password=admin&transport.trustStoreLocation=/home/marslan/work/camelkssl/client.ts&transport.trustStorePassword=password&transport.verifyHost=false' --threads 1 --protocol amqp --message-count 10 --destination 'queue://test'
Creating A AMQ6 broker with support for SSL
oc create secret generic example-amq6-secret \
--from-file=broker.ks \
--from-literal=keyStorePassword=password \
--from-file=broker.ts \
--from-literal=trustStorePassword=password
oc new-app amq63-ssl -p APPLICATION_NAME=amq6-broker -p MQ_QUEUES=test -p MQ_TOPICS=test -p MQ_USERNAME=admin \
-p MQ_PASSWORD=admin -p AMQ_SECRET=example-amq6-secret -p AMQ_TRUSTSTORE=broker.ts -p AMQ_TRUSTSTORE_PASSWORD=password \
-p AMQ_KEYSTORE=broker.ks -p AMQ_KEYSTORE_PASSWORD=password -n camelk-ssl
You need to create a expose a route for AMQ6 using the amq6-broker-tcp-ssl
service.
you have to enable TLS Passthrough, this is done by clicking secure route
and selecting Passthough
on the TLS Termination
option. Select None
for Insecure Traffic
.
FYI: socket closed warnings after the command is normal Change Broker URL and Truststore/Keystore Location
https://activemq.apache.org/components/classic/download/
example:
./activemq producer \
-Djavax.net.ssl.trustStore=/home/marslan/work/camelkssl/client.ts \
-Djavax.net.ssl.trustStorePassword=password \
-Djavax.net.ssl.keyStore=/home/marslan/work/camelkssl/client.ks \
-Djavax.net.ssl.keyStorePassword=password \
--brokerUrl ssl://test-camelk-ssl.apps.cluster-hhl6r.hhl6r.sandbox55.opentlc.com:443 \
--user admin \
--password admin \
--destination queue://test \
--messageCount 1000 \
--message HelloWorld
example:
./activemq consumer \
-Djavax.net.ssl.trustStore=/home/marslan/work/camelkssl/client.ts \
-Djavax.net.ssl.trustStorePassword=password \
-Djavax.net.ssl.keyStore=/home/marslan/work/camelkssl/client.ks \
-Djavax.net.ssl.keyStorePassword=password \
-Djavax.net.debug=ssl \
--brokerUrl ssl://test-camelk-ssl.apps.cluster-hhl6r.hhl6r.sandbox55.opentlc.com:443 \
--user admin \
--password admin \
--destination queue://test
-Djavax.net.debug=ssl
To ensure CamelK is working correctly, you can test it using HelloCamelK.java
. Feel free to delete the Integration afterwards.
kamel run amq7/HelloCamelK.java
Create a amq7
folder to seperate AMQ6 and AMQ7.
Create application properties so CamelK and the Integration knows where to look. Specifing URL, username, password, destinationType and destinationName.
Tthe URL requires you to add the keyStoreLocation and trustStoreLocation location within the URL. The keystore and truststore location is where we import the secrets we created earlier. See Run the Consumer using CamelK
section for explantion.
Create the application.properties in the amq7/configs/
folder.
example:
quarkus.qpid-jms.url=amqps://example-amq7-amqp-0-svc-rte-camelk-ssl.apps.cluster-hhl6r.hhl6r.sandbox55.opentlc.com:443?transport.keyStoreLocation=/etc/ssl/example-amq-secret/broker.ks&transport.keyStorePassword=password&transport.trustStoreLocation=/etc/ssl/example-amq-secret/client.ts&transport.trustStorePassword=password&transport.verifyHost=false
quarkus.qpid-jms.username=admin
quarkus.qpid-jms.password=admin
jms.destinationType=queue
jms.destinationName=test
within the amq7/
folder create amq7consumer.java
example:
// camel-k: language=java
// camel-k: dependency=mvn:org.amqphub.quarkus:quarkus-qpid-jms
import org.apache.camel.builder.RouteBuilder;
public class amq7consumer extends RouteBuilder {
@Override
public void configure() throws Exception {
from("jms:{{jms.destinationType}}:{{jms.destinationName}}").to("log:info");
}
}
camel-k: language=java
and camel-k: dependency=mvn:org.amqphub.quarkus:quarkus-qpid-jms
provide infomation for camelk to run the java file, it enables camelk to sort out the dependecies.
the file consumes from jms.url
, jms.destinationType
and jms.destinationName
specifed in the application.properties
and outputs it to the log.
This is imported when using the kamel run
command, which is the next step.
To run the amq7consumer.java
kamel run -n camelk-ssl \
--property file:./amq7/configs/application.properties \
--resource secret:example-amq-secret@/etc/ssl/example-amq-secret \
amq7/amq7consumer.java
--property file:
Allows us to import properties to Camel.
--resource
allows us to add resources to the Pod, we are adding example-amq-secret
to the pod and placing it to /etc/ssl/example-amq-secret
folder within the pod.
To debug Camek add this to the command:
--trait jvm.options=-Djavax.net.debug=all
Create a amq6
folder to seperate AMQ6 and AMQ7.
Create application properties so CamelK and the Integration knows where to look. Specifing URL, username, password, destinationType and destinationName.
Tthe URL requires you to add the keyStoreLocation and trustStoreLocation location within the URL. The keystore and truststore location is where we import the secrets we created earlier. See Run the Producer using CamelK
section for explantion.
Create the application.properties in the amq6/configs/
folder.
example:
activemq.destination.brokerURL=ssl://test-camelk-ssl.apps.cluster-hhl6r.hhl6r.sandbox55.opentlc.com:443
activemq.destination.username=admin
activemq.destination.password=admin
activemq.destination.ssl.keyStorePassword=password
activemq.destination.ssl.keyStoreLocation=/etc/ssl/example-amq6-secret/broker.ks
activemq.destination.ssl.trustStorePassword=password
activemq.destination.ssl.trustStoreLocation=/etc/ssl/example-amq6-secret/broker.ts
activemq.destination.type=queue
activemq.destination.name=test
within the amq6/
folder create amq6SSLProcducer.java
example:
// camel-k: language=java
// camel-k: dependency=camel:camel-quarkus-activemq
// camel-k: dependency=camel:camel-quarkus-timer
// camel-k: property=period=5000
// camel-k: dependency=mvn:com.github.javafaker:javafaker:1.0.2
// camel-k: dependency=mvn:org.slf4j:slf4j-simple:1.7.32
import org.apache.camel.builder.RouteBuilder;
import com.github.javafaker.Faker;
import org.apache.activemq.ActiveMQSslConnectionFactory;
import org.apache.camel.component.activemq.ActiveMQComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.activemq.pool.PooledConnectionFactory;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import org.apache.camel.BindToRegistry;
import org.apache.camel.PropertyInject;
public class amq6SSLProducer extends RouteBuilder {
@Inject
PooledConnectionFactory pooledConnectionFactory;
@PropertyInject("activemq.destination.brokerURL")
String destinationBrokerURL;
@PropertyInject("activemq.destination.username")
String destinationUserName;
@PropertyInject("activemq.destination.password")
String destinationPassword;
@PropertyInject("activemq.destination.ssl.keyStorePassword")
String destinationKeystorePassword;
@PropertyInject("activemq.destination.ssl.trustStorePassword")
String destinationTruststorePassword;
@PropertyInject("activemq.destination.ssl.trustStoreLocation")
String destinationTrustStoreLocation;
@PropertyInject("activemq.destination.ssl.keyStoreLocation")
String destinationKeyStoreLocation;
Logger logger = LoggerFactory.getLogger(amq6SSLProducer.class.getName());
@Override
public void configure() throws Exception {
from("timer:foo?fixedRate=true&period={{period}}").bean(this, "generateFakePerson()").to("log:info")
.to("activemq:{{activemq.destination.type}}:{{activemq.destination.name}}?connectionFactory=#pooledConnectionFactory");
}
public String generateFakePerson() {
Faker faker = new Faker();
return faker.name().fullName() + " lives on " + faker.address().streetAddress();
}
@ApplicationScoped
public ActiveMQComponent activeMq(PooledConnectionFactory pooledConnectionFactory) {
ActiveMQComponent activeMqComponent = new ActiveMQComponent();
activeMqComponent.setConnectionFactory(pooledConnectionFactory);
activeMqComponent.setCacheLevelName("CACHE_CONSUMER");
return activeMqComponent;
}
@BindToRegistry
public PooledConnectionFactory pooledConnectionFactory() throws Exception {
return new PooledConnectionFactory(sslConnectionFactory());
}
private ActiveMQSslConnectionFactory sslConnectionFactory() throws Exception {
ActiveMQSslConnectionFactory connectionFactory = new ActiveMQSslConnectionFactory();
logger.info("BrokerURL: " + destinationBrokerURL);
connectionFactory.setBrokerURL(destinationBrokerURL);
connectionFactory.setUserName(destinationUserName);
connectionFactory.setPassword(destinationPassword);
connectionFactory.setTrustStore(destinationTrustStoreLocation);
connectionFactory.setTrustStorePassword(destinationTruststorePassword);
connectionFactory.setKeyStore(destinationKeyStoreLocation);
connectionFactory.setKeyStorePassword(destinationKeystorePassword);
return connectionFactory;
}
}
camel-k:
provide infomation for camelk to run the java file, it also enables camelk to sort out the dependecies.
This File uses a custom connectionFactory to enable the use of SSL to send Produced messages. The messages produced are of a Fake Person using Javafaker. The timer allows a messages to be send every 5000ms.
The application.properties
are injected to amq6SSLProducer.java
when we import the property file using the kamel run
command, which is the next step.
To run the amqssl.java
kamel run -n camelk-ssl \
--property file:./amq6/configs/application.properties \
--resource secret:example-amq6-secret@/etc/ssl/example-amq6-secret
amq6/amqssl.java
--property file:
Allows us to import properties to Camel and to inject properties to the java file.
--resource
allows us to add resources to the Pod, we are adding example-amq6-secret
to the pod and placing it to /etc/ssl/example-amq6-secret
folder within the pod.
To debug Camek add this to the command:
--trait jvm.options=-Djavax.net.debug=all
Create a message-bridge
folder.
Create the application.properties in the message-bridge/configs/
folder.
using amq6/configs/application.properties
and amq7/configs/application.properties
to create your message-bridge application.properties. But changing from destination
to source
.
Example:
#amq6
activemq.source.brokerURL=ssl://test-camelk-ssl.apps.cluster-hhl6r.hhl6r.sandbox55.opentlc.com:443
activemq.source.username=admin
activemq.source.password=admin
activemq.source.ssl.keyStorePassword=password
activemq.source.ssl.keyStoreLocation=/etc/ssl/example-amq6-secret/broker.ks
activemq.source.ssl.trustStorePassword=password
activemq.source.ssl.trustStoreLocation=/etc/ssl/example-amq6-secret/broker.ts
activemq.source.type=queue
activemq.source.name=test
#amq7
quarkus.qpid-jms.url=amqps://example-amq7-amqp-0-svc-rte-camelk-ssl.apps.cluster-hhl6r.hhl6r.sandbox55.opentlc.com:443?transport.keyStoreLocation=/etc/ssl/example-amq-secret/broker.ks&transport.keyStorePassword=password&transport.trustStoreLocation=/etc/ssl/example-amq-secret/client.ts&transport.trustStorePassword=password&transport.verifyHost=false
quarkus.qpid-jms.username=admin
quarkus.qpid-jms.password=admin
jms.destinationType=queue
jms.destinationName=test
within the message-bridge/
folder create sixToSevenBridge.java
Using a custom SSL connectionFactory to read from the AMQ6 Broker and sending it to our AMQ7 broker.
Example:
// camel-k: language=java
// camel-k: dependency=camel:camel-quarkus-activemq
// camel-k: dependency=mvn:org.amqphub.quarkus:quarkus-qpid-jms
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.activemq.ActiveMQSslConnectionFactory;
import org.apache.camel.component.activemq.ActiveMQComponent;
import org.apache.activemq.pool.PooledConnectionFactory;
import org.apache.qpid.jms.JmsConnectionFactory;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import org.apache.camel.BeanInject;
import org.apache.camel.BindToRegistry;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.apache.camel.PropertyInject;
public class sixToSevenBridge extends RouteBuilder {
@Inject
PooledConnectionFactory pooledConnectionFactory;
@PropertyInject("activemq.source.brokerURL")
String sourceBrokerURL;
@PropertyInject("activemq.source.username")
String sourceUserName;
@PropertyInject("activemq.source.password")
String sourcePassword;
@PropertyInject("activemq.source.ssl.keyStorePassword")
String sourceKeystorePassword;
@PropertyInject("activemq.source.ssl.trustStorePassword")
String sourceTruststorePassword;
@PropertyInject("activemq.source.ssl.trustStoreLocation")
String sourceTrustStoreLocation;
@PropertyInject("activemq.source.ssl.keyStoreLocation")
String sourceKeyStoreLocation;
@PropertyInject("quarkus.qpid-jms.url")
String destinationBrokerURL;
@Override
public void configure() throws Exception {
from("activeMQSource:{{activemq.source.type}}:{{activemq.source.name}}").to("log:info")
.to("jms:{{jms.destinationType}}:{{jms.destinationName}}?connectionFactory=artemisConnectionFactory");
}
@BindToRegistry("artemisConnectionFactory")
public JmsConnectionFactory connectionFactory() throws Exception {
return new JmsConnectionFactory(destinationBrokerURL);
}
@BindToRegistry("activeMQSource")
public ActiveMQComponent activeMQSource() throws Exception{
ActiveMQComponent activeMqComponent = new ActiveMQComponent();
activeMqComponent.setConnectionFactory(pooledConnectionFactorySource());
activeMqComponent.setCacheLevelName("CACHE_CONSUMER");
return activeMqComponent;
}
@BindToRegistry("pooledConnectionFactorySource")
public PooledConnectionFactory pooledConnectionFactorySource() throws Exception {
return new PooledConnectionFactory(sslConnectionFactorySource());
}
private ActiveMQSslConnectionFactory sslConnectionFactorySource() throws Exception {
ActiveMQSslConnectionFactory connectionFactory = new ActiveMQSslConnectionFactory();
System.out.println("BrokerURL: " + sourceBrokerURL);
connectionFactory.setBrokerURL(sourceBrokerURL);
connectionFactory.setUserName(sourceUserName);
connectionFactory.setPassword(sourcePassword);
connectionFactory.setTrustStore(sourceTrustStoreLocation);
connectionFactory.setTrustStorePassword(sourceTruststorePassword);
connectionFactory.setKeyStore(sourceKeyStoreLocation);
connectionFactory.setKeyStorePassword(sourceKeystorePassword);
return connectionFactory;
}
}
kamel run -n camelk-ssl \
--property file:./message-bridge/configs/application.properties message-bridge/sixToSevenBridge.java \
--resource secret:example-amq6-secret@/etc/ssl/example-amq6-secret \
--resource secret:example-amq-secret@/etc/ssl/example-amq-secret
--property file:
Allows us to import properties to Camel and to inject properties to the java file.
--resource
allows us to add resources to the Pod, we are adding both example-amq6-secret
and example-amq-secret
to the pod and placing it to /etc/ssl/
folder within the pod.
https://gist.github.com/welshstew/45366bbd230c24e3c289038c9eb2665d