Skip to content

Commit ee40cf0

Browse files
committed
fix: allowing for the usage of authenticated http proxies for https
closes: fabric8io#6350 Signed-off-by: Steve Hawkins <[email protected]>
1 parent 8d8cd46 commit ee40cf0

File tree

23 files changed

+370
-74
lines changed

23 files changed

+370
-74
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
* Fix #6247: Support for proxy authentication from proxy URL user info
1313
* Fix #6281: use GitHub binary repo for Kube API Tests
1414
* Fix #6282: Allow annotated types with Pattern, Min, and Max with Lists and Maps and CRD generation
15+
* Fix #6350: Allowing authenticated http proxy usage with Jetty, vertx, and JDK for https endpoints
1516
* Fix #5480: Move `io.fabric8:zjsonpatch` to KubernetesClient project
1617

1718
#### Dependency Upgrade

httpclient-jetty/src/main/java/io/fabric8/kubernetes/client/jetty/JettyHttpClientBuilder.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,20 @@
2424
import org.eclipse.jetty.client.HttpProxy;
2525
import org.eclipse.jetty.client.Origin;
2626
import org.eclipse.jetty.client.Socks4Proxy;
27+
import org.eclipse.jetty.client.Socks5Proxy;
28+
import org.eclipse.jetty.client.api.Authentication;
2729
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
2830
import org.eclipse.jetty.client.http.HttpClientConnectionFactory;
2931
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
32+
import org.eclipse.jetty.client.util.BasicAuthentication;
3033
import org.eclipse.jetty.http2.client.HTTP2Client;
3134
import org.eclipse.jetty.http2.client.http.ClientConnectionFactoryOverHTTP2;
3235
import org.eclipse.jetty.io.ClientConnector;
3336
import org.eclipse.jetty.util.ssl.SslContextFactory;
3437
import org.eclipse.jetty.websocket.client.WebSocketClient;
3538

39+
import java.net.URI;
40+
import java.net.URISyntaxException;
3641
import java.time.Duration;
3742
import java.util.Optional;
3843
import java.util.stream.Stream;
@@ -91,11 +96,20 @@ public JettyHttpClient build() {
9196
case SOCKS4:
9297
sharedHttpClient.getProxyConfiguration().addProxy(new Socks4Proxy(address, false));
9398
break;
99+
case SOCKS5:
100+
sharedHttpClient.getProxyConfiguration().addProxy(new Socks5Proxy(address, false));
101+
break;
94102
default:
95103
throw new KubernetesClientException("Unsupported proxy type");
96104
}
97-
sharedHttpClient.getProxyConfiguration().addProxy(new HttpProxy(address, false));
98-
addProxyAuthInterceptor();
105+
URI proxyUri;
106+
try {
107+
proxyUri = new URI("http://" + proxyAddress.getHostString() + ":" + proxyAddress.getPort());
108+
} catch (URISyntaxException e) {
109+
throw KubernetesClientException.launderThrowable(e);
110+
}
111+
sharedHttpClient.getAuthenticationStore()
112+
.addAuthentication(new BasicAuthentication(proxyUri, Authentication.ANY_REALM, proxyUsername, proxyPassword));
99113
}
100114
clientFactory.additionalConfig(sharedHttpClient, sharedWebSocketClient);
101115
return new JettyHttpClient(this, sharedHttpClient, sharedWebSocketClient);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (C) 2015 Red Hat, 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+
* http://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+
package io.fabric8.kubernetes.client.jetty;
17+
18+
import io.fabric8.kubernetes.client.http.AbstractHttpClientProxyHttpsTest;
19+
import io.fabric8.kubernetes.client.http.HttpClient;
20+
import okhttp3.mockwebserver.SocketPolicy;
21+
22+
@SuppressWarnings("java:S2187")
23+
public class JettyHttpClientProxyHttpsTest extends AbstractHttpClientProxyHttpsTest {
24+
@Override
25+
protected HttpClient.Factory getHttpClientFactory() {
26+
server.setDefaultSocketPolicy(SocketPolicy.KEEP_OPEN); // we need a challenge before switching to ssl
27+
return new JettyHttpClientFactory();
28+
}
29+
}

httpclient-okhttp/src/main/java/io/fabric8/kubernetes/client/okhttp/OkHttpClientBuilderImpl.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import io.fabric8.kubernetes.client.http.HttpClient.ProxyType;
2020
import io.fabric8.kubernetes.client.http.StandardHttpClientBuilder;
2121
import io.fabric8.kubernetes.client.http.StandardHttpHeaders;
22+
import io.fabric8.kubernetes.client.utils.HttpClientUtils;
2223
import okhttp3.Authenticator;
2324
import okhttp3.ConnectionSpec;
2425
import okhttp3.OkHttpClient;
@@ -74,10 +75,11 @@ public OkHttpClientImpl initialBuild(okhttp3.OkHttpClient.Builder builder) {
7475
builder.proxy(Proxy.NO_PROXY);
7576
} else if (proxyAddress != null) {
7677
builder.proxy(new Proxy(convertProxyType(), proxyAddress));
77-
if (proxyAuthorization != null) {
78+
if (proxyUsername != null && proxyPassword != null) {
79+
String auth = HttpClientUtils.basicCredentials(proxyUsername, proxyPassword);
7880
builder.proxyAuthenticator(
7981
(route, response) -> response.request().newBuilder()
80-
.header(StandardHttpHeaders.PROXY_AUTHORIZATION, proxyAuthorization).build());
82+
.header(StandardHttpHeaders.PROXY_AUTHORIZATION, auth).build());
8183
}
8284
}
8385
if (tlsVersions != null) {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (C) 2015 Red Hat, 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+
* http://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+
package io.fabric8.kubernetes.client.okhttp;
17+
18+
import io.fabric8.kubernetes.client.http.AbstractHttpClientProxyHttpsTest;
19+
import io.fabric8.kubernetes.client.http.HttpClient;
20+
import okhttp3.OkHttpClient.Builder;
21+
22+
import javax.net.ssl.HostnameVerifier;
23+
import javax.net.ssl.SSLSession;
24+
25+
@SuppressWarnings("java:S2187")
26+
public class OkHttpClientProxyHttpsTest extends AbstractHttpClientProxyHttpsTest {
27+
@Override
28+
protected HttpClient.Factory getHttpClientFactory() {
29+
return new OkHttpClientFactory() {
30+
@Override
31+
protected Builder newOkHttpClientBuilder() {
32+
Builder builder = super.newOkHttpClientBuilder();
33+
builder.hostnameVerifier(new HostnameVerifier() {
34+
@Override
35+
public boolean verify(String hostname, SSLSession session) {
36+
return true;
37+
}
38+
});
39+
return builder;
40+
}
41+
};
42+
}
43+
}

httpclient-okhttp/src/test/java/io/fabric8/kubernetes/client/okhttp/OkHttpClientProxyTest.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,4 @@ protected HttpClient.Factory getHttpClientFactory() {
2525
return new OkHttpClientFactory();
2626
}
2727

28-
@Override
29-
protected void proxyConfigurationAddsRequiredHeaders() {
30-
// NO-OP
31-
// OkHttp uses a response intercept to add the auth proxy headers in case the original response failed
32-
}
3328
}

httpclient-vertx/src/main/java/io/fabric8/kubernetes/client/vertx/VertxHttpClientBuilder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,10 @@ public VertxHttpClient<F> build() {
7777
ProxyOptions proxyOptions = new ProxyOptions()
7878
.setHost(this.proxyAddress.getHostName())
7979
.setPort(this.proxyAddress.getPort())
80+
.setUsername(this.proxyUsername)
81+
.setPassword(this.proxyPassword)
8082
.setType(convertProxyType());
8183
options.setProxyOptions(proxyOptions);
82-
addProxyAuthInterceptor();
8384
}
8485

8586
final String[] protocols;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright (C) 2015 Red Hat, 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+
* http://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+
package io.fabric8.kubernetes.client.vertx;
17+
18+
import io.fabric8.kubernetes.client.http.AbstractHttpClientProxyHttpsTest;
19+
import io.fabric8.kubernetes.client.http.HttpClient;
20+
21+
@SuppressWarnings("java:S2187")
22+
public class VertxHttpClientProxyHttpsTest extends AbstractHttpClientProxyHttpsTest {
23+
@Override
24+
protected HttpClient.Factory getHttpClientFactory() {
25+
return new VertxHttpClientFactory();
26+
}
27+
}

junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/DefaultMockServer.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import okhttp3.mockwebserver.Dispatcher;
2323
import okhttp3.mockwebserver.MockWebServer;
2424
import okhttp3.mockwebserver.RecordedRequest;
25+
import okhttp3.mockwebserver.SocketPolicy;
2526

2627
import java.io.IOException;
2728
import java.net.InetAddress;
@@ -38,13 +39,15 @@ public class DefaultMockServer implements MockServer {
3839

3940
private final Context context;
4041
private final boolean useHttps;
42+
private boolean tunnelProxy;
4143
private final MockWebServer server;
4244
private final Map<ServerRequest, Queue<ServerResponse>> responses;
4345
private final AtomicInteger lastRequestCount;
4446
private final AtomicReference<RecordedRequest> lastRequest;
4547

4648
private final AtomicBoolean initialized = new AtomicBoolean();
4749
private final AtomicBoolean shutdown = new AtomicBoolean();
50+
private Dispatcher dispatcher;
4851

4952
public DefaultMockServer() {
5053
this(new Context(), new MockWebServer(), new HashMap<>(), false);
@@ -72,17 +75,34 @@ public DefaultMockServer(Context context, MockWebServer server, Map<ServerReques
7275
this.lastRequest = new AtomicReference<>();
7376
this.lastRequestCount = new AtomicInteger(0);
7477
this.server.setDispatcher(dispatcher);
78+
this.dispatcher = dispatcher;
79+
}
80+
81+
public void setHttpsTunnelProxy(boolean tunnelProxy) {
82+
this.tunnelProxy = tunnelProxy;
7583
}
7684

7785
private void startInternal() {
7886
if (initialized.compareAndSet(false, true)) {
7987
if (useHttps) {
80-
server.useHttps(MockSSLContextFactory.create().getSocketFactory(), false);
88+
server.useHttps(MockSSLContextFactory.create().getSocketFactory(), tunnelProxy);
89+
if (tunnelProxy) {
90+
setDefaultSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END);
91+
}
8192
}
8293
onStart();
8394
}
8495
}
8596

97+
/**
98+
* Only works when using the default {@link MockDispatcher} type
99+
*
100+
* @param socketPolicy to be returned when peeking at the dispatcher
101+
*/
102+
public void setDefaultSocketPolicy(SocketPolicy socketPolicy) {
103+
((MockDispatcher) dispatcher).setDefaultSocketPolicy(socketPolicy);
104+
}
105+
86106
private void shutdownInternal() {
87107
if (shutdown.compareAndSet(false, true)) {
88108
onShutdown();

junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/HttpMethodable.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,5 @@ public interface HttpMethodable<T> {
2929

3030
T patch();
3131

32+
T connect();
3233
}

junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/dsl/Replyable.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@
2222

2323
public interface Replyable<T> {
2424

25-
T andReply(int statusCode, BodyProvider<Object> contentSupplier);
25+
T andReply(int statusCode, BodyProvider<?> contentSupplier);
2626

27-
T andReply(ResponseProvider<Object> contentSupplier);
27+
T andReply(ResponseProvider<?> contentSupplier);
2828

29-
T andReplyChunked(int statusCode, BodyProvider<List<Object>> content);
29+
T andReplyChunked(int statusCode, BodyProvider<List<?>> content);
3030

31-
T andReplyChunked(ResponseProvider<List<Object>> content);
31+
T andReplyChunked(ResponseProvider<List<?>> content);
3232

3333
}

junit/mockwebserver/src/main/java/io/fabric8/mockwebserver/internal/MockDispatcher.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,19 @@ public class MockDispatcher extends Dispatcher {
3232

3333
private final Map<ServerRequest, Queue<ServerResponse>> responses;
3434
private final Collection<WebSocketSession> webSocketSessions = new ConcurrentLinkedQueue<>();
35+
private SocketPolicy defaultSocketPolicy = SocketPolicy.EXPECT_CONTINUE;
3536

3637
public MockDispatcher(Map<ServerRequest, Queue<ServerResponse>> responses) {
3738
this.responses = responses;
3839
}
3940

41+
public void setDefaultSocketPolicy(SocketPolicy defaultSocketPolicy) {
42+
this.defaultSocketPolicy = defaultSocketPolicy;
43+
}
44+
4045
@Override
4146
public MockResponse peek() {
42-
return new MockResponse().setSocketPolicy(SocketPolicy.EXPECT_CONTINUE);
47+
return new MockResponse().setSocketPolicy(defaultSocketPolicy);
4348
}
4449

4550
@Override

0 commit comments

Comments
 (0)