Возникла потребность подключаться из Java приложения к брокеру RabbitMQ с использованием защищенного соединения. Приложение на Spring Boot, живёт в контейнере Docker и должно слушать два узла Rabbit параллельно.
- На входе: сертификат клиентского узла — контейнер client-cert.pfx, корневой сертификат для него root-cert.crt
- Открытый серверный сертификат шлюза Rabbit: выкачивается автоматически (можно подложить вручную)
- Используемая версия java поддерживает форматы хранилищ и JKS, и PKCS12
- Требуется: создать и положить сертификаты в Key Store (сертификаты приложения «клиентские») и Trust Store (доверенные «серверные» сертификаты). В чём разница.
Команды для создания KeyStore и TrustStore
Настройки вынесены в переменные в начале списка команд, можно заполнить значения и скопировать всё в консоль целиком.
export CLIENT_APP_CRT_NAME=client-cert;
# Пароль, заданный при выпуске клиентского сертификата
# Он же используется в кач-ве пароля для хранилищ
export CRT_STORAGES_PASSWORD='changeit';
export RABBIT_GATEWAY_CRT_NAME=rabbit-gw-dev;
export RABBIT_GATEWAY_URL=rabbit-gw-dev.url;
export RABBIT_GATEWAY_PORT=1234;
export ROOT_CRT_NAME=root-cert;
# Извлечь ключ pem и сертификат crt из pfx
openssl pkcs12 -in "${CLIENT_APP_CRT_NAME}".pfx -passin pass:"${CRT_STORAGES_PASSWORD}" -nocerts -nodes -out "${CLIENT_APP_CRT_NAME}".pem;
openssl pkcs12 -in "${CLIENT_APP_CRT_NAME}".pfx -passin pass:"${CRT_STORAGES_PASSWORD}" -clcerts -nokeys -out "${CLIENT_APP_CRT_NAME}".crt;
# Key Store (сертификат приложения)
openssl pkcs12 -export -in "${CLIENT_APP_CRT_NAME}".crt -inkey "${CLIENT_APP_CRT_NAME}".pem \
-out "${RABBIT_GATEWAY_CRT_NAME}"_key_store.p12 -name "${RABBIT_GATEWAY_CRT_NAME}" -passout pass:"${CRT_STORAGES_PASSWORD}" \
-CAfile "${ROOT_CRT_NAME}".crt -caname root;
# Trust Store (сертификат шлюза)
keytool -import -alias "${RABBIT_GATEWAY_URL}"_client -file "${CLIENT_APP_CRT_NAME}".crt -keystore "${RABBIT_GATEWAY_CRT_NAME}"_trust_store.jks \
-noprompt -deststorepass "${CRT_STORAGES_PASSWORD}";
openssl s_client -showcerts -connect "${RABBIT_GATEWAY_URL}":"${RABBIT_GATEWAY_PORT}" -prexit 2>/dev/null | sed -n -e '/BEGIN\ CERTIFICATE/,/END\ CERTIFICATE/ p' \
| tee "${RABBIT_GATEWAY_URL}".cert \
&& keytool -import -alias "${RABBIT_GATEWAY_URL}"_server -file "${RABBIT_GATEWAY_URL}".cert -keystore "${RABBIT_GATEWAY_CRT_NAME}"_trust_store.jks \
-noprompt -deststorepass "${CRT_STORAGES_PASSWORD}";
unset CLIENT_APP_CRT_NAME;
unset CRT_STORAGES_PASSWORD;
unset RABBIT_GATEWAY_CRT_NAME;
unset RABBIT_GATEWAY_URL;
unset RABBIT_GATEWAY_PORT;
unset ROOT_CRT_NAME;
Это создаст хранилища с сертификатами KeyStore и TrustStore: rabbit-gw-dev_key_store.p12
и rabbit-gw-dev_trust_store.jks
Копирование хранилищ в образ Docker
Хранилища лежат в репозитории, в каталоге keystore
, и копируются в контейнер — также в каталог keystore
, расположенный в папке приложения.
ADD app-repository/keystore/rabbit-gw-dev_key_store.p12 /app-in-container/keystore/rabbit-gw-dev_key_store.p12
ADD app-repository/keystore/rabbit-gw-dev_trust_store.jks /app-in-container/keystore/rabbit-gw-dev_trust_store.jks
ENV SSL_KEYSTORE=/app-in-container/keystore/
Через переменную SSL_KEYSTORE
можно сослаться на каталог с хранилищами ключей, расположенный в контейнере, из настроек приложения.
Включение TLS для RabbitMQ в Spring Boot
Выдержка из application.yml с настройками, отвечающими за TLS/SSL:
spring:
rabbitmq:
# <-- host, port, username, password
ssl:
enabled: true
keyStore: file:${SSL_KEYSTORE}${RABBIT_SSL_KEY_STORE}
key-store-password: ${RABBIT_SSL_KEY_STORE_PASSWORD}
trust-store: file:${SSL_KEYSTORE}${RABBIT_SSL_TRUST_STORE}
trust-store-password: ${RABBIT_SSL_TRUST_STORE_PASSWORD}
multirabbitmq:
enabled: true
connections:
reserve-gateway:
# <-- host, port, username, password
ssl:
enabled: ${spring.rabbitmq.ssl.enabled}
keyStore: ${spring.rabbitmq.ssl.keyStore}
key-store-password: ${spring.rabbitmq.ssl.key-store-password}
trust-store: ${spring.rabbitmq.ssl.trust-store}
trust-store-password: ${spring.rabbitmq.ssl.trust-store-password}
Дополнительно здесь используется Spring MultiRabbit Library для одновременного подключения к нескольким узлам RabbitMQ. Чтобы вручную СontainerFactory
/RabbitTemplate
для слушателей не создавать/настраивать.
Ранее писал про обертку для Testcontainers, в которой тоже используется эта библиотека.
Дополнительно — в документации RabbitMQ описано TLS подключение «голого» java client из кода, без использования Spring.
Итог и дополнительные ссылки
В целом — всё оказалось несложно (когда один раз проделаешь этот путь). Основная «проблема» — правильно создать KeyStore и TrustStore с сертификатами. Ну и базово понять разницу межу типами хранилищ KeyStore / TrustStore.
Ссылки по теме (глубже в теорию):