Spring: TLS/SSL подключение к RabbitMQ

Возникла потребность подключаться из 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.

Ссылки по теме (глубже в теорию):

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *