Как вы упомянули, есть несколько способов сделать это, и в зависимости от ваших конкретных потребностей, какой из них будет работать лучше всего. Я постараюсь обрисовать как можно больше вариантов и плюсов / минусов каждого.
Если все, что вам нужно, - это распространить доверенные сертификаты, то добавление доверенных сертификатов Bosh и разрешение Bosh распространять их на все виртуальные машины и контейнеры в CF - хороший способ. В Ops Manager под плиткой Bosh перейдите в Безопасность, и вы можете добавить любое количество доверенных сертификатов.
Это хороший подход, когда нескольким приложениям необходимо доверять сертификатам, например, в корпоративном / частном ЦС. Он также хорошо работает с разными средами / основаниями, потому что у вас есть другой Ops Manager, который может поддерживать другой список доверенных сертификатов. Это может быть сложно управлять, если у вас есть много сертификатов, которым необходимо доверять, и / или если сертификаты используются только одним или небольшим количеством приложений. Это также требует административных привилегий для CF и может занять некоторое время, чтобы развернуть обновления на всей основе.
Это вариант, о котором вы упомянули в своем вопросе. Импортируйте сертификат в файл хранилища доверенных сертификатов Java, упакуйте файл в приложение Java и укажите путь к нему с помощью переменной среды JAVA_OPTS
. Файл склада доверенных сертификатов можно поместить в каталог resource
.
Ex: cf set-env <app> JAVA_OPTS '-Djavax.net.ssl.trustStore=/home/vcap/app/BOOT-INF/classes/jssecacerts'
Эта опция полезна для сценариев, не охваченных опцией № 1 выше, например, для одиночных или небольших приложений, где есть сертификаты, уникальные для этого приложения. Например, у вас есть сервер базы данных, которому вы должны доверять, и только одно приложение использует этот сервер базы данных.
У него есть недостаток, о котором вы упомянули, хранилище доверенных сертификатов должно быть встроено в приложение, поэтому для обработки нескольких сред вам нужно будет сделать что-то вроде упаковки всех сертификатов среды в одно хранилище доверенных сертификатов и упаковать их в один JAR / WAR для всех среды или создайте несколько файлов JAR / WAR, по одному для каждой среды.
У него также есть недостаток, заключающийся в том, что вы полностью переопределяете хранилище доверенных сертификатов по умолчанию. Хранилище доверенных сертификатов по умолчанию содержит множество хорошо известных сертификатов ЦС, поэтому вы должны начать с копии хранилища доверенных сертификатов по умолчанию и добавить сертификаты, которые вы хотите добавить. В противном случае вы потеряете список доверенных центров сертификации по умолчанию, и проверка будет нарушена для таких сайтов, как Google или Yahoo. Сертификат CA по умолчанию поставляется с каждой JRE, но может варьироваться от версии к версии, поэтому вы должны поддерживать его в актуальном состоянии.
Это небольшая вариация №2. Создайте файл сценария в корневом каталоге приложения с именем .profile
. В нем запустите keytool
и импортируйте все сертификаты, которые упакованы с вашим приложением.
Ex:
#!/bin/bash
$HOME/.java-buildpack/open_jdk_jre/bin/keytool \
-keystore $HOME/.java-buildpack/open_jdk_jre/lib/security/cacerts \
-storepass changeit \
-importcert \
-noprompt \
-alias MyCert \
-file $HOME/BOOT-INF/classes/ssl/MyCert.crt
# TODO: repeat this command for all the certs you need to import
Затем запустите jar uf target/your-app.jar .profile
, чтобы добавить сценарий в корень JAR / WAR вашего приложения. Сценарий должен существовать в корне вашего файла JAR / WAR, чтобы Cloud Foundry мог его подобрать и запустить. Вы можете запустить jar tf target/your-app.jar | grep profile
, чтобы подтвердить это. В выводе должно быть указано .profile
без ведущего пути. Если есть начальный путь, значит файл добавлен не в то место.
Преимущество этого подхода в том, что вам не нужно предоставлять хранилище полного доверия. Это большой недостаток №2, и в этом случае вы будете аккуратно использовать хранилище доверенных сертификатов по умолчанию, упакованное с JRE, предоставляемым пакетом сборки Java. Скрипт добавит дополнительные сертификаты перед запуском вашего приложения.
Обратной стороной этого подхода является то, что добавление сертификата беспорядочно, и его можно легко пропустить / забыть, что приведет к поломке вашего приложения. Он также должен находиться в очень конкретном месте, чтобы Cloud Foundry его обнаружила и выполнила. У этой опции также присутствует обратная сторона необходимости упаковывать ваши сертификаты в JAR / WAR.
Следующий вариант - это итерация №3. В этом случае вы включаете сценарий .profile
, но не упаковываете сертификаты в свой JAR / WAR. Вместо этого вы предоставляете сертификаты через переменную среды или связанную службу (это прекрасно работает с брокером служб CredHub).
Чтобы это сработало, вам необходимо настроить сценарий таким образом, чтобы он извлекал сертификат из переменной среды, создавал временный файл и импортировал его в хранилище доверенных сертификатов по умолчанию с помощью keytool
. В следующем примере показано извлечение его из переменной среды, но вы можете использовать jq
и извлечь его из VCAP_SERVICES
для связанной службы.
Ex:
#!/bin/bash
$HOME/.java-buildpack/open_jdk_jre/bin/keytool \
-keystore $HOME/.java-buildpack/open_jdk_jre/lib/security/cacerts \
-storepass changeit \
-importcert \
-noprompt \
-alias MyCert \
-file <(echo $CERT_1)
# TODO: repeat this command for all the certs you need to import
Преимущества этого подхода такие же, как и у # 3, за исключением того, что вам больше не нужно связывать сертификаты с вашим приложением. Их можно кормить извне. Обратной стороной является тот же сценарий .profile
, с которым сложно работать, и о нем можно забыть.
Вариант №2 - установить системные свойства в вашем коде. Вам просто нужно, чтобы это произошло на раннем этапе запуска приложения, прежде чем JVM инициализирует TLS. Если это произойдет позже, ничего не произойдет и ваши сертификаты не будут найдены.
В этой статье объясняется, где можно разместить код инициализации. в приложении Spring Boot. По сути, вы просто используете это для вызова System.setProperty("javax.net.ssl.trustStore", "path/to/custom/truststore")
, а если вы измените пароль, System.setProperty("javax.net.ssl.trustStorePassword", "newpassword")
.
Я не фанат этого подхода, потому что все может неожиданно сломаться, если TLS инициализируется до запуска вашего кода. IMHO, это сложно контролировать, особенно в Spring Boot со всеми его автоматическими конфигурациями (не то чтобы это плохо, просто вы должны быть очень осторожны с порядком). Я считаю, что это проще сделать в скрипте профиля, который гарантированно запускается до запуска вашего приложения.
Другой вариант - настроить ваш WebClient вручную и вообще не зависеть от хранилища доверенных сертификатов по умолчанию. Вы можете создать свой собственный магазин доверия, что даст вам максимальную гибкость.
Ex:
@Bean
public WebClient createWebClient() throws SSLException {
SslContext sslContext = SslContextBuilder
.forClient()
.trustManager( /* TODO */ )
.build();
ClientHttpConnector httpConnector = HttpClient.create().secure(t -> t.sslContext(sslContext) )
return WebClient.builder().clientConnector(httpConnector).build();
}
В приведенном выше коде есть маркер TODO, куда вам нужно добавить своего доверительного менеджера. В этом сообщении SO есть несколько хороших мыслей о том, как это сделать, но в целом то, что вы создаете и откуда загружаете сертификаты, довольно гибкий.
Результатом этого метода является то, что он полностью настраивается. Вы можете получить свои сертификаты откуда угодно, из переменных среды, VCAP_SERVICES
или даже из Spring Cloud Config. Они просто понадобятся вам при инициализации WebClient
. Обратная сторона, вероятно, очевидна, нужно писать больше кода.
Вероятно, есть и другие варианты, но, надеюсь, это заставит вас задуматься о том, что возможно. Приведенная выше информация также только касается доверенных магазинов. Если вам нужно предоставить хранилище ключей с закрытыми ключами, некоторые из вышеперечисленных параметров будут работать с небольшими отличиями.
Вам также нужно быть более осторожным с ключами, потому что это очень конфиденциальная информация. ИМХО, стоило бы дополнительных усилий, чтобы убедиться, что они не хранятся в JAR / WAR и хранятся в безопасности, как в CredHub или Spring Cloud Config Server.