In un precedente articolo ho mostrato come configurare ActiveMQ in alta affidabilità in modalità Master/Slave. Vediamo ora come rendere sicure le connessioni verso il broker sfruttando il protocollo SSL. ActiveMQ usa JSSE (Java Secure Socket Extension) per il supporto a connessioni sicure tramite certificato SSL. In questo articolo faccio un esempio di connessione SSL “trusted” sia lato client sia lato server. Tale connessione può essere instaurata sia dichiarando le proprietà Java SSL relative al certificato direttamente in un profilo Maven, sia utilizzando con Spring la connection factory SSL di ActiveMQ.
ROADMAP
1. Creazione certificato SSL self-signed (keystore e truststore)
2. Abilitazione connessioni SSL su ActiveMQ
3. Setup client SSL con JSSE su profilo Maven
4. Setup client SSL con Spring
1. Creazione certificato SSL self-signed (keystore e truststore)
Per creare il proprio certificato SSL self-signed viene utilizzato “keytool“.
## Create a keystore for the broker SERVER $ keytool -genkey -alias amq-server -keyalg RSA -keysize 2048 -validity 90 -keystore amq-server.ks ## Export the broker SERVER certificate from the keystore $ keytool -export -alias amq-server -keystore amq-server.ks -file amq-server_cert ## Create the CLIENT keystore $ keytool -genkey -alias amq-client -keyalg RSA -keysize 2048 -validity 90 -keystore amq-client.ks ## Import the previous exported broker's certificate into a CLIENT truststore $ keytool -import -alias amq-server -keystore amq-client.ts -file amq-server_cert ## If you want to make trusted also the client, you must export the client's certificate from the keystore $ keytool -export -alias amq-client -keystore amq-client.ks -file amq-client_cert ## Import the client's exported certificate into a broker SERVER truststore $ keytool -import -alias amq-client -keystore amq-server.ts -file amq-client_cert
## Other useful commands $ openssl s_client -connect activemq-host:61617 $ keytool -list -keystore amq-server.ks $ keytool -printcert -v -file amq-server_cert $ keytool -storepasswd -keystore amq-server.ks
2. Abilitazione connessioni SSL su ActiveMQ
Per configurare una connessione SSL su ActiveMQ è necessario definire la posizione dove risiedono i file keystore e truststore sul server. Copiamo i file creati al passo precedente nella directory conf del broker.
$ cp amq-server.ks ${ACTIVEMQ_HOME}/conf/ $ cp amq-server.ts ${ACTIVEMQ_HOME}/conf/
Ora definiamo l’elemento <transportConnector> nel file ${ACTIVEMQ_HOME}/conf/activemq.xml file e specifichiamo il parametro “needClientAuth=true” per obbligare il broker ad accettare connessioni trusted SSL autenticate attraverso il file truststore con password.
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="fnmamq01" dataDirectory="/mnt/amq_data/amq-5.9"> <!-- blablablabla --> <transportConnectors> <transportConnector name="ssl" uri="ssl://0.0.0.0:61617?trace=true&needClientAuth=true" /> </transportConnectors> <!-- SSL Configuration Context --> <sslContext> <sslContext keyStore="file:${activemq.base}/conf/amq-server.ks" keyStorePassword="123456" trustStore="file:${activemq.base}/conf/amq-server.ts" trustStorePassword="123456" /> </sslContext> </broker>
Se non ci sono errori all’avvio il broker dovrebbe visualizzare il seguente messaggio in console.
3. Setup client SSL con JSSE su profilo Maven
Per ActiveMQ, la stringa di connessione SSL e del tipo ssl://hostname:port?key=value. In una configurazione di ActiveMQ in modalità Master/Slave è necessario definire una stringa del tipo failover:(ssl://amq01:61617,ssl://amq02:61617)?randomize=false. In questo esempio viene utilizzato l’oggetto ResouceBundle per esternalizzare alcune proprietà come appunto la stringa di connessione al broker. Il file da modificare è activemq-samples/ssl-transport/src/main/resources/configuration.properties
### TCP over SSL Access to a Master/Slave ActiveMQ istance amq.url=failover:(ssl://amq01:61617,ssl://amq02:61617)?randomize=false
La classe che definisce il Producer è activemq-samples/ssl-transport/src/main/java/activemq/samples/ssltransport/Producer.java
public class Producer { ResourceBundle settings = ResourceBundle.getBundle("configuration"); private String brokerURL = settings.getString("amq.url"); private static transient ConnectionFactory factory; private transient Connection connection; private transient Session session; private transient MessageProducer producer; private static int count = 10; private static int total; private static int id = 1000000; private String jobs[] = new String[]{"coda1", "coda2"}; public Producer() throws JMSException { factory = new ActiveMQConnectionFactory(brokerURL); connection = factory.createConnection(); connection.start(); session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); producer = session.createProducer(null); } // Something other here ... }
Quando viene utilizzato JSSE è necessario definire le proprietà di sistema con le informazioni legate al keystore e al truststore. E’ possibile impostare la variabile JAVA_OPTS con le seguenti proprietà.
JAVA_OPTS="-Djavax.net.ssl.keyStore=${ACTIVEMQ_HOME}/conf/amq-client.ks -Djavax.net.ssl.keyStorePassword=123456 -Djavax.net.ssl.trustStore=${ACTIVEMQ_HOME}/conf/amq-client.ts -Djavax.net.ssl.trustStorePassword=123456"
Tali proprietà possono essere dichiarate anche con Maven utilizzando il plugin “exec-maven-plugin“. In questo esempio ho creato un profilo Maven per impostare a runtime le proprietà SSL legate al certificato. In questo modo posso lanciare più comodamente diversi client, ovvero Producer e Consumer, ognuno con il proprio certificato SSL. Ecco un esempio per il Producer nel file activemq-samples/ssl-transport/pom.xml
<properties> <sslsample.keystore>${basedir}/src/main/resources/amq-client.ks</sslsample.keystore> <sslsample.keypasswd>123456</sslsample.keypasswd> <sslsample.truststore>${basedir}/src/main/resources/amq-client.ts</sslsample.truststore> <sslsample.truststorepasswd>123456</sslsample.truststorepasswd> </properties> <profiles> <profile> <id>producer</id> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.2.1</version> <executions> <execution> <goals> <goal>java</goal> </goals> </execution> </executions> <configuration> <mainClass>activemq.samples.ssltransport.Producer</mainClass> <systemProperties> <systemProperty> <key>javax.net.ssl.keyStore</key> <value>${sslsample.keystore}</value> </systemProperty> <systemProperty> <key>javax.net.ssl.keyStorePassword</key> <value>${sslsample.keypasswd}</value> </systemProperty> <systemProperty> <key>javax.net.ssl.trustStore</key> <value>${sslsample.truststore}</value> </systemProperty> <systemProperty> <key>javax.net.ssl.trustStorePassword</key> <value>${sslsample.truststorepasswd}</value> </systemProperty> </systemProperties> </configuration> </plugin> </plugins> </build> </profile> </profiles>
Compilare e lanciare maven su “exec:java”
$ cd activemq-samples/ssl-transport $ mvn clean install $ mvn exec:java -Pproducer
Se non ci sono errori, dovrebbe comparire il seguente messaggio in console.
4. Setup client SSL con Spring
Se il client è stato implementato utilizzando Spring, è possibile definire una propria Connection Factory JMS SSL usando l’oggetto org.apache.activemq.ActiveMQSslConnectionFactory. Il file in cui viene definito il bean è activemq-samples/ssl-transport/src/main/resources/SpringBeans-Producer.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="propertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:configuration.properties</value> </list> </property> </bean> <!-- SSL Connection Factory --> <bean id="jmsSslConnectionFactory" class="org.apache.activemq.ActiveMQSslConnectionFactory"> <property name="brokerURL" value="${amq.url}" /> <property name="keyStore" value="amq-client.ks" /> <property name="keyStorePassword" value="123456" /> <property name="trustStore" value="amq-client.ts" /> <property name="trustStorePassword" value="123456" /> </bean> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <property name="connectionFactory" ref="jmsSslConnectionFactory" /> </bean> <bean id="producer" class="activemq.samples.ssltransport.Producer" scope="prototype"> <property name="template" ref="jmsTemplate" /> </bean> </beans>