Защищаем JMS соединения с помощью SSL
Допустим, есть у нас задача раскинуть выполнение некой произвольной очереди заданий на несколько компьютеров. Основные условия этой задачи примерно такие:
- Менеджер заданий и сам исполнитель (воркер) должны быть упакованы в одно приложение. Это дает возможность не напрягаться с установкой каких-то сложных систем. Запустили приложение — оно что-то вычисляет само для себя. Хотим ускорить процесс — запускаем на соседнем компьютере его-же в качестве клиента к первому, и все делается в два раза быстрее.
- Задачи раздаются через JMS.
- К JMS брокеру нельзя присоединиться кому попало. Т.е. нужна аутентификация.
- Не должно быть возможности прослушать трафик между менеджером и воркером.
Саму задачу я решал с помощью ActiveMQ и spring-jms. В классической схеме, мы должны создать сертификат для брокера. Создать сертификат для каждого клиента. Настроить SslContext брокера для использования в качестве KeyStore своего сертификата и в качестве TrustStore сертификатов клиентов. В окружениях клиентов прописать использование правильных сертификатов. В нашем случае есть несколько оговорок или упрощений:
- Брокер должен требовать от клиента сертификат для идентификации.
- Мы не должны менять глобальные настройки SSL клиента, как это рекомендуется в классической схеме. Его основной KeyStore может использоваться другими частями приложения. И KeyStore, используемый для соединения с брокером не должен конфликтовать с основным.
- Нам хватит одного сертификата для обоих сторон, т.к. и клиент и сервер — части одного и того-же приложения.
Первым делом генерируем новый KeyStore, который будет упакован в jar приложения.
keytool -genkey -alias broker -keyalg RSA -keystore broker.ks
Далее в конфигурации Spring контейнера настраиваем запуск брокера с обязательной аутентификацией клиента по сертификату
<amq:broker brokerName="ActiveMQBroker" useJmx="false"
persistent="false" >
<amq:sslContext>
<amq:sslContext keyStore="classpath:broker.ks"
keyStorePassword="123456"
trustStore="classpath:broker.ks"
trustStorePassword="123456" />
</amq:sslContext>
<amq:transportConnectors>
<amq:transportConnector uri="ssl://localhost:61616?needClientAuth=true"></amq:transportConnector>
</amq:transportConnectors>
</amq:broker>
И с этим же сертификатом настраиваем соединения к этому брокеру.
<bean id="jmsFactory"
class="org.apache.activemq.pool.PooledConnectionFactory"
destroy-method="stop">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQSslConnectionFactory">
<property name="brokerURL">
<value>ssl://localhost:61616</value>
</property>
<property name="keyStore" value="broker.ks" />
<property name="keyStorePassword" value="123456" />
<property name="trustStore" value="broker.ks" />
<property name="trustStorePassword" value="123456" />
</bean>
</property>
</bean>
В обоих случаях blocker.ks лежит прямо в jar файле, а пути к нему прописываются немного по разному. В зависимости от того, как каждый класс осуществляет доступ к ресурсам.
Пример использования этих кусков конфигурации можно посмотреть в демонстрационном приложении Sample Spring-Jms.