Представим весьма типичную ситуацию. Есть приложение, написанное на Java (например, с использованием Spring framework), которое в рамках своей функциональности осуществляет запросы к какому-либо стороннему сервису по HTTP. Для лучшей переносимости и иозляции приложения мы решили использовать Docker: выбрали подходящий базовый образ с нужным дистрибутивом OpenJDK (например eclipse-temurin:17), добавили необходимые команды в Dockerfile, собрали свой образ с приложением и создали на его основе контейнер. Со временем же тот самый сторонний сервис, к которому обращалось приложение, был обновлен, а взаимодействие с ним стало возможным исключительно по протоколу HTTPS. В связи с этим потребовались правки в нашей конфигурации, чтобы избежать бесчисленных SslHandshakeException'ов, которые неизбежно возникли при отсутствии дополнительных действий по обеспечению безопасного соединения.
Какие же изменения нужно предпринять, чтобы все работало? В самом общем случае, не учитывая специфику конкретной системы, Dockerfile для сборки образа должен выглядеть примерно так, как представлено в коде ниже. Для простоты условимся, что .jar архив для запуска приложения и SSL-сертификат, который нам нужно использовать, находятся в той же директории, что и сам Dockerfile:
FROM eclipse-temurin:17
RUN apt update && apt install -y ca-certificates-java
WORKDIR /app
COPY our-app-1.0-RELEASE.jar /app
COPY required-ca.crt /usr/local/share/ca-certificates/required-ca.crt
RUN update-ca-certificates
ENV OPTS=""
ENV USE_SYSTEM_CA_CERTS=true
CMD java $OPTS -jar our-app-1.0-RELEASE.jar
На что здесь стоит обратить внимание?
Во-первых, установка пакета ca-certificates-java, который предоставляет общие сертификаты центров сертификации (CA) для сред выполнения Java. То есть именно благодаря этому пакету происходит автоматическая синхронизация общесистемного хранилища сертификатов с доверенными сертификатами среды исполнения Java. Так мы можем гарантировать, что сертификаты в операционной системе и те, которые используются Java-приложением одинаковы и управление ими происходит однообразно.
Во-вторых, это копирование дополнительного специфичного для нашей системы сертификата required-ca.crt в общесистемное хранилище, после чего вызывается команда update-ca-certificates. Эта команда нужна, чтобы запустить механизм обновления того списка сертификатов, которые используются уже джавой. Этот инструмент мы используем для того, чтобы в приложении использовася актуальный набор доверенных сертификатов, синхронизированный с операционной системой.
В-третьих, это переменная окружения USE_SYSTEM_CA_CERTS=true. В целом при сборке образа она также показывает, что небходимо использовать системное хранилище доверенных сертификатов вместо собственного встроенного, что также является дополнительной гарантией, что сертификаты в системе и сертификаты, используемые Java, одинаковы и единообразны. Явное выставление значения этой переменной в true является скорее опциональной мерой и возможно при условии выполнения указанных выше команд мерой несколько избыточной, тем не менее для гарантии результата рекомендуем этим не пренебрегать.
В конечном итоге после выполнения указанных действий (с поправками на специфику конкретного приложения) проблема решилась и возможность обмена данными со сторонним сервисом по HTTPS появилась