Skip to content

Commit a325c0b

Browse files
authored
Merge pull request #691 from ahazeltonNF/FixSSLZitiConnectionsWithHttpClient5
#690 Fix ssl ziti connections with httpclient5
2 parents 3eca227 + 554a99b commit a325c0b

File tree

2 files changed

+103
-5
lines changed

2 files changed

+103
-5
lines changed

ziti-springboot-client/src/main/java/org/openziti/springboot/client/web/config/ZitiHttpClientConfiguration.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
3737
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
3838
import org.apache.hc.client5.http.io.HttpClientConnectionOperator;
39-
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
4039
import org.apache.hc.client5.http.ssl.TlsSocketStrategy;
4140
import org.apache.hc.client5.http.ssl.TrustAllStrategy;
4241
import org.apache.hc.core5.http.HeaderElement;
@@ -61,21 +60,20 @@
6160
import org.springframework.http.client.ClientHttpRequestFactory;
6261
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
6362
import org.springframework.web.client.RestTemplate;
64-
import jakarta.annotation.PreDestroy;
6563
import lombok.extern.slf4j.Slf4j;
6664

6765
@Slf4j
6866
@Configuration
6967
public class ZitiHttpClientConfiguration {
7068

7169
// The default timeout when requesting a connection from the connection manager.
72-
private static final long DEFAULT_CONNECTION_REQUEST_TIMEOUT = 30 * 1000;
70+
private static final long DEFAULT_CONNECTION_REQUEST_TIMEOUT = 30 * 1000L;
7371

7472
// The default time to wait for data, the maximum time between two consecutive packets of data.
7573
private static final int DEFAULT_RESPONSE_TIMEOUT = 60 * 1000;
7674

7775
// The default time to keep a connection alive.
78-
private static final long DEFAULT_KEEP_ALIVE_TIME_MILLIS = 20 * 1000;
76+
private static final long DEFAULT_KEEP_ALIVE_TIME_MILLIS = 20 * 1000L;
7977

8078
@ConditionalOnProperty(value = "spring.ziti.client.rest-template.enabled", havingValue = "true", matchIfMissing = true)
8179
@Bean
@@ -114,7 +112,7 @@ public ZitiContext context(@Value("${spring.ziti.client.identity.file:}") Resour
114112
@ConditionalOnProperty(value = "spring.ziti.client.tls-socket-strategy.enabled", havingValue = "true", matchIfMissing = true)
115113
@Bean("zitiTlsSocketStrategy")
116114
public TlsSocketStrategy zitiTlsSocketStrategy() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
117-
return new DefaultClientTlsStrategy(new SSLContextBuilder().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build());
115+
return new ZitiTlsSocketStrategy(new SSLContextBuilder().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build());
118116
}
119117

120118
@ConditionalOnProperty(value = "spring.ziti.client.connection-manager.enabled", havingValue = "true", matchIfMissing = true)
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright (c) 2018-2025 NetFoundry Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.openziti.springboot.client.web.config;
18+
19+
import java.io.IOException;
20+
import java.net.Socket;
21+
import javax.net.ssl.HostnameVerifier;
22+
import javax.net.ssl.SSLContext;
23+
import javax.net.ssl.SSLParameters;
24+
import javax.net.ssl.SSLSocket;
25+
import javax.net.ssl.SSLSocketFactory;
26+
import org.apache.hc.client5.http.config.TlsConfig;
27+
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
28+
import org.apache.hc.client5.http.ssl.HostnameVerificationPolicy;
29+
import org.apache.hc.core5.http.URIScheme;
30+
import org.apache.hc.core5.http.protocol.HttpContext;
31+
import org.apache.hc.core5.http.ssl.TLS;
32+
import org.apache.hc.core5.http.ssl.TlsCiphers;
33+
import org.apache.hc.core5.io.Closer;
34+
import org.apache.hc.core5.util.Timeout;
35+
import org.openziti.Ziti;
36+
import lombok.extern.slf4j.Slf4j;
37+
38+
@Slf4j
39+
public class ZitiTlsSocketStrategy extends DefaultClientTlsStrategy {
40+
private final SSLSocketFactory zitiSslSocketFactory;
41+
private final HostnameVerificationPolicy hostnameVerificationPolicy;
42+
43+
public ZitiTlsSocketStrategy(final SSLContext sslContext,
44+
final HostnameVerificationPolicy hostnameVerificationPolicy,
45+
final HostnameVerifier hostnameVerifier) {
46+
super(sslContext, hostnameVerificationPolicy, hostnameVerifier);
47+
this.zitiSslSocketFactory = Ziti.getSSLSocketFactory(sslContext);
48+
this.hostnameVerificationPolicy = hostnameVerificationPolicy;
49+
}
50+
51+
public ZitiTlsSocketStrategy(final SSLContext sslContext) {
52+
super(sslContext);
53+
this.zitiSslSocketFactory = Ziti.getSSLSocketFactory(sslContext);
54+
this.hostnameVerificationPolicy = HostnameVerificationPolicy.BOTH;
55+
}
56+
57+
@Override
58+
public SSLSocket upgrade(Socket socket, String target, int port, Object attachment, HttpContext context) throws IOException {
59+
final SSLSocket upgradedSocket = (SSLSocket) zitiSslSocketFactory.createSocket(socket, target, port, true);
60+
try {
61+
executeHandshake(upgradedSocket, target, attachment);
62+
return upgradedSocket;
63+
} catch (IOException | RuntimeException ex) {
64+
Closer.closeQuietly(upgradedSocket);
65+
throw ex;
66+
}
67+
}
68+
69+
// This is based on the private executeHandshake method in AbstractClientTlsStrategy
70+
private void executeHandshake(
71+
final SSLSocket upgradedSocket,
72+
final String target,
73+
final Object attachment) throws IOException {
74+
final TlsConfig tlsConfig = attachment instanceof TlsConfig tlsAttachment ? tlsAttachment : TlsConfig.DEFAULT;
75+
76+
final SSLParameters sslParameters = upgradedSocket.getSSLParameters();
77+
sslParameters.setProtocols((TLS.excludeWeak(upgradedSocket.getEnabledProtocols())));
78+
sslParameters.setCipherSuites(TlsCiphers.excludeWeak(upgradedSocket.getEnabledCipherSuites()));
79+
if (hostnameVerificationPolicy == HostnameVerificationPolicy.BUILTIN || hostnameVerificationPolicy == HostnameVerificationPolicy.BOTH) {
80+
sslParameters.setEndpointIdentificationAlgorithm(URIScheme.HTTPS.id);
81+
}
82+
upgradedSocket.setSSLParameters(sslParameters);
83+
84+
final Timeout handshakeTimeout = tlsConfig.getHandshakeTimeout();
85+
if (handshakeTimeout != null) {
86+
upgradedSocket.setSoTimeout(handshakeTimeout.toMillisecondsIntBound());
87+
}
88+
89+
initializeSocket(upgradedSocket);
90+
91+
if (log.isDebugEnabled()) {
92+
log.debug("Enabled protocols: {}", (Object) upgradedSocket.getEnabledProtocols());
93+
log.debug("Enabled cipher suites: {}", (Object) upgradedSocket.getEnabledCipherSuites());
94+
log.debug("Starting handshake ({})", handshakeTimeout);
95+
}
96+
upgradedSocket.startHandshake();
97+
verifySession(target, upgradedSocket.getSession());
98+
}
99+
100+
}

0 commit comments

Comments
 (0)