Вопрос или проблема
Я вызываю другой API, используя WebClient. У меня есть сертификат, который используется API с хост-именем X. Однако, если я хочу делать https-запросы к API, используя другой URL, отличный от сертификата, я правильно получаю ошибку.
Проблема в том, что я буду использовать сети Docker и сервисное обнаружение Docker для вызова API. Это означает, что я знаю, что сертификат действителен, я знаю, что имена будут не совпадать (URL Docker и URL сертификата), и клиент должен это принять.
Я пробовал следующее, но это не сработало
.secure { sslSpec: SslProvider.SslContextSpec ->
sslSpec
.sslContext(SslContextBuilder.forClient()
.build())
.serverNames(SNIHostName("docker-network-url"))
.build()
}
Я знаю, что отключение проверки имени хоста также является вариантом, но это в целом менее безопасно и плохая практика.
Есть ли способ, чтобы я мог проверить имя сертификата и принять его, даже если URL отличается?
Да, вы можете настроить свой клиент так, чтобы он принимал сертификат, даже если имя хоста URL отличается от имени хоста сертификата, указав ожидаемое имя хоста во время SSL рукопожатия. В вашем случае, поскольку вы используете Reactor Netty с WebClient, вы можете установить handshakeHostname в вашей SSL конфигурации. Это говорит слою SSL использовать указанное имя хоста как для SNI (Server Name Indication), так и для проверки имени хоста, даже если вы подключаетесь к другому имени хоста в вашем URL.
Вот как вы можете изменить свой код:
HttpClient httpClient = HttpClient.create()
.secure(sslSpec -> sslSpec
.sslContext(SslContextBuilder.forClient().build())
.handshakeHostname("X") // Замените "X" на имя хоста в вашем сертификате
);
// Используйте этот httpClient с вашим WebClient
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
В этом коде:
- .handshakeHostname(“X”): Устанавливает имя хоста, которое будет использоваться во время SSL рукопожатия. Это имя хоста используется как для SNI, так и для проверки имени хоста. Замените “X” на фактическое имя хоста, присутствующее в сертификате вашего сервера.
Установив handshakeHostname, слой SSL будет:
- Использовать “X” как имя хоста SNI, подавая серверу сигнал представить правильный сертификат.
- Использовать “X” для проверки имени хоста, обеспечивая соответствие сертификата этому имени хоста.
Этот подход позволяет вам поддерживать безопасную проверку имени хоста, не отключая ее полностью и не принимая все сертификаты без различий. Это эффективно говорит слою SSL: “Я подключаюсь к хосту ‘docker-network-url’, но для целей SSL рассматривайте это как ‘X’.”
Примечание: Убедитесь, что вы заменили “X” на фактическое имя хоста, указанное в SSL сертификате вашего сервера.
Ответ или решение
Spring Boot Reactive WebClient: Как добавить пользовательскую проверку SSL на основе имени хоста
Вопрос, с которым вы столкнулись, связан с использованием WebClient в Spring Boot для выполнения HTTPS-запросов к API, где сертификат SSL не соответствует имени хоста, используемому в Docker-окружении. Это распространенная проблема в сценариях, когда ваш клиент обращается к сервисам через сервисную маршрутизацию или сетевую обнаружимость контейнеров, таких как Docker.
В данном случае, необходимо настроить клиент так, чтобы он принимал сертификат, даже если имя хоста, к которому выполняется запрос, отличается от имени, указанного в сертификате. Ваша цель — сохранить безопасность, избегая общей разблокировки проверки сертификатов.
Чтобы решить эту задачу, вы можете установить handshakeHostname в вашей конфигурации SSL, что позволит клиенту использовать ожидаемое имя хоста в процессе SSL-рукопожатия. Таким образом, сертификат будет корректно проверен, даже если URL-адрес отличен от указанного в сертификате.
Пример конфигурации
Вы можете использовать следующий код для настройки вашего HTTP-клиента с кастомной проверкой SSL:
HttpClient httpClient = HttpClient.create()
.secure(sslSpec -> sslSpec
.sslContext(SslContextBuilder.forClient().build())
.handshakeHostname("X") // Замените "X" на имя хоста, указанное в вашем сертификате
);
// Используйте этот httpClient с вашим WebClient
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
Объяснение кода
.handshakeHostname("X")
: Данный метод устанавливает имя хоста, которое будет использоваться в процессе SSL-рукопожатия. Это имя хоста будет использоваться как для SNI (Server Name Indication), так и для проверки имени хоста. Замените "X" на фактическое имя хоста, указанное в сертификате вашего сервера.
При использовании handshakeHostname
, слой SSL будет работать следующим образом:
- Использование "X" как имени хоста SNI: Это заставляет сервер предоставить правильный сертификат во время SSL-рукопожатия.
- Проверка имени хоста: Это гарантирует, что сертификат будет проверен по имени "X", даже если вы подключаетесь к другому URL.
Преимущества данного подхода
Такой подход позволяет поддерживать безопасность проверки имен хостов без необходимости отключать ее или принимать все сертификаты без разбора. Это оптимальное решение для случаев, когда SSL-сертификаты применяются через механизмы виртуализации/docker’а.
Заключение
Данный метод предоставит вам возможность надежно управлять подключениями к API в вашем Docker-контейнере, обеспечивая правильную проверку сертификата. Убедитесь, что замена "X" на фактическое имя хоста, указанное в SSL-сертификате, корректна, чтобы избежать проблем с подключением в дальнейшем.
Следуя описанным рекомендациям, вы сможете эффективно настраивать WebClient в вашем приложении на Spring Boot с учетом особенностей работы в Docker-среде и требования к безопасности.