Skip to content

Commit e2371c9

Browse files
pablocarlePablo Carle
andauthored
fix: WebSocket (#4519)
Signed-off-by: Pablo Carle <pablo.carle@broadcom.com> Co-authored-by: Pablo Carle <pablo.carle@broadcom.com>
1 parent 516c1f8 commit e2371c9

File tree

25 files changed

+1265
-61
lines changed

25 files changed

+1265
-61
lines changed

apiml/src/test/resources/application.yml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ logging:
22
level:
33
ROOT: DEBUG
44
org.springframework: DEBUG
5+
org.springframework.boot.autoconfigure.web: DEBUG
6+
org.springframework.cloud.gateway: DEBUG
7+
org.springframework.http.server.reactive: DEBUG
8+
org.springframework.web.reactive: DEBUG
59
org.zowe.apiml: DEBUG
6-
org.springframework.cloud.gateway: TRACE
7-
org.springframework.http.server.reactive: TRACE
8-
org.springframework.web.reactive: TRACE
9-
org.springframework.boot.autoconfigure.web: TRACE
10-
reactor.netty: TRACE
11-
redisratelimiter: TRACE
10+
redisratelimiter: DEBUG
1211

1312
eureka:
1413
dashboard:
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* This program and the accompanying materials are made available under the terms of the
3+
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
4+
* https://www.eclipse.org/legal/epl-v20.html
5+
*
6+
* SPDX-License-Identifier: EPL-2.0
7+
*
8+
* Copyright Contributors to the Zowe Project.
9+
*/
10+
11+
package org.zowe.apiml.security;
12+
13+
import lombok.AccessLevel;
14+
import lombok.NoArgsConstructor;
15+
16+
import javax.net.ssl.KeyManager;
17+
import javax.net.ssl.SSLContext;
18+
import javax.net.ssl.TrustManager;
19+
import javax.net.ssl.X509TrustManager;
20+
21+
import java.security.KeyManagementException;
22+
import java.security.SecureRandom;
23+
import java.util.ArrayList;
24+
import java.util.Collection;
25+
26+
/**
27+
* Builder meant to be used only for non-strict configuration
28+
*/
29+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
30+
public class HostnameIgnoringSSLContextBuilder extends org.apache.hc.core5.ssl.SSLContextBuilder {
31+
32+
@Override
33+
protected void initSSLContext(SSLContext sslContext, Collection<KeyManager> keyManagers,
34+
Collection<TrustManager> trustManagers, SecureRandom secureRandom) throws KeyManagementException {
35+
36+
Collection<TrustManager> laxTrustManager = new ArrayList<>();
37+
if (trustManagers != null) {
38+
trustManagers.forEach(tm -> {
39+
if (tm instanceof X509TrustManager x509tm) {
40+
laxTrustManager.add(new HostnameIgnoringTrustManager(x509tm));
41+
} else {
42+
laxTrustManager.add(tm);
43+
}
44+
});
45+
}
46+
47+
super.initSSLContext(sslContext, keyManagers, laxTrustManager, secureRandom);
48+
}
49+
50+
public static HostnameIgnoringSSLContextBuilder create() {
51+
return new HostnameIgnoringSSLContextBuilder();
52+
}
53+
54+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* This program and the accompanying materials are made available under the terms of the
3+
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
4+
* https://www.eclipse.org/legal/epl-v20.html
5+
*
6+
* SPDX-License-Identifier: EPL-2.0
7+
*
8+
* Copyright Contributors to the Zowe Project.
9+
*/
10+
11+
package org.zowe.apiml.security;
12+
13+
import lombok.AccessLevel;
14+
import lombok.RequiredArgsConstructor;
15+
import lombok.experimental.Delegate;
16+
17+
import javax.net.ssl.SSLEngine;
18+
import javax.net.ssl.X509ExtendedTrustManager;
19+
import javax.net.ssl.X509TrustManager;
20+
21+
import java.net.Socket;
22+
import java.security.cert.CertificateException;
23+
import java.security.cert.X509Certificate;
24+
25+
/**
26+
* To use for WebSocket scenarios that require non-strict hostname validation
27+
*/
28+
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
29+
public class HostnameIgnoringTrustManager extends X509ExtendedTrustManager {
30+
31+
@Delegate
32+
private final X509TrustManager delegate;
33+
34+
@Override
35+
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket)
36+
throws CertificateException {
37+
delegate.checkClientTrusted(chain, authType);
38+
}
39+
40+
@Override
41+
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket)
42+
throws CertificateException {
43+
delegate.checkServerTrusted(chain, authType);
44+
}
45+
46+
@Override
47+
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
48+
throws CertificateException {
49+
delegate.checkClientTrusted(chain, authType);
50+
}
51+
52+
@Override
53+
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine)
54+
throws CertificateException {
55+
delegate.checkServerTrusted(chain, authType);
56+
}
57+
58+
}

common-service-core/src/main/java/org/zowe/apiml/security/HttpsFactory.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,15 @@
3333

3434
import javax.net.ssl.HostnameVerifier;
3535
import javax.net.ssl.SSLContext;
36+
3637
import java.io.File;
3738
import java.io.IOException;
3839
import java.net.URL;
39-
import java.security.*;
40+
import java.security.KeyManagementException;
41+
import java.security.KeyStore;
42+
import java.security.KeyStoreException;
43+
import java.security.NoSuchAlgorithmException;
44+
import java.security.UnrecoverableKeyException;
4045
import java.security.cert.CertificateException;
4146

4247

@@ -190,8 +195,11 @@ private synchronized SSLContext createSecureSslContext() {
190195
log.debug("Protocol: {}", config.getProtocol());
191196
SSLContextBuilder sslContextBuilder = SSLContexts.custom();
192197
try {
193-
loadTrustMaterial(sslContextBuilder);
198+
if (config.isNonStrictVerifySslCertificatesOfServices()) {
199+
sslContextBuilder = HostnameIgnoringSSLContextBuilder.create();
200+
}
194201
loadKeyMaterial(sslContextBuilder);
202+
loadTrustMaterial(sslContextBuilder);
195203
this.secureSslContext = sslContextBuilder.build();
196204
validateSslConfig();
197205
return secureSslContext;
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* This program and the accompanying materials are made available under the terms of the
3+
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
4+
* https://www.eclipse.org/legal/epl-v20.html
5+
*
6+
* SPDX-License-Identifier: EPL-2.0
7+
*
8+
* Copyright Contributors to the Zowe Project.
9+
*/
10+
11+
package org.zowe.apiml.security;
12+
13+
import org.junit.jupiter.api.BeforeEach;
14+
import org.junit.jupiter.api.Test;
15+
import org.junit.jupiter.api.extension.ExtendWith;
16+
import org.mockito.junit.jupiter.MockitoExtension;
17+
18+
import javax.net.ssl.KeyManager;
19+
import javax.net.ssl.SSLContext;
20+
import javax.net.ssl.TrustManager;
21+
import javax.net.ssl.X509TrustManager;
22+
23+
import java.security.KeyManagementException;
24+
import java.security.SecureRandom;
25+
import java.util.ArrayList;
26+
import java.util.Collection;
27+
28+
import static org.junit.jupiter.api.Assertions.assertEquals;
29+
import static org.junit.jupiter.api.Assertions.assertNotNull;
30+
import static org.junit.jupiter.api.Assertions.assertSame;
31+
import static org.junit.jupiter.api.Assertions.assertTrue;
32+
import static org.mockito.ArgumentMatchers.any;
33+
import static org.mockito.ArgumentMatchers.argThat;
34+
import static org.mockito.ArgumentMatchers.eq;
35+
import static org.mockito.Mockito.doNothing;
36+
import static org.mockito.Mockito.mock;
37+
38+
@ExtendWith(MockitoExtension.class)
39+
class HostnameIgnoringSSLContextBuilderTest {
40+
41+
private HostnameIgnoringSSLContextBuilder builder;
42+
43+
@BeforeEach
44+
void setUp() {
45+
this.builder = HostnameIgnoringSSLContextBuilder.create();
46+
}
47+
48+
@Test
49+
void testInitSSLContext_withX509Trust() throws KeyManagementException {
50+
var sslContext = mock(SSLContext.class);
51+
var secureRandom = mock(SecureRandom.class);
52+
Collection<KeyManager> km = new ArrayList<>();
53+
Collection<TrustManager> tm = new ArrayList<>();
54+
55+
var x509tm = mock(X509TrustManager.class);
56+
km.add(mock(KeyManager.class));
57+
tm.add(x509tm);
58+
59+
doNothing().when(sslContext).init((KeyManager[]) eq(km.toArray()), argThat(t -> {
60+
TrustManager[] trustManagers = t;
61+
assertNotNull(trustManagers);
62+
assertEquals(1, trustManagers.length);
63+
assertTrue(trustManagers[0] instanceof HostnameIgnoringTrustManager);
64+
return true;
65+
}), any());
66+
67+
builder.initSSLContext(sslContext, km, tm, secureRandom);
68+
}
69+
70+
@Test
71+
void testInitSSLContext_withoutX509Trust() throws KeyManagementException {
72+
var sslContext = mock(SSLContext.class);
73+
var secureRandom = mock(SecureRandom.class);
74+
Collection<KeyManager> km = new ArrayList<>();
75+
Collection<TrustManager> tm = new ArrayList<>();
76+
77+
var nonX509tm = mock(TrustManager.class);
78+
km.add(mock(KeyManager.class));
79+
tm.add(nonX509tm);
80+
81+
doNothing().when(sslContext).init((KeyManager[]) eq(km.toArray()), argThat(t -> {
82+
TrustManager[] trustManagers = t;
83+
assertNotNull(trustManagers);
84+
assertEquals(1, trustManagers.length);
85+
assertSame(nonX509tm, trustManagers[0]);
86+
return true;
87+
}), any());
88+
89+
builder.initSSLContext(sslContext, km, tm, secureRandom);
90+
}
91+
92+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* This program and the accompanying materials are made available under the terms of the
3+
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
4+
* https://www.eclipse.org/legal/epl-v20.html
5+
*
6+
* SPDX-License-Identifier: EPL-2.0
7+
*
8+
* Copyright Contributors to the Zowe Project.
9+
*/
10+
11+
package org.zowe.apiml.security;
12+
13+
import org.junit.jupiter.api.BeforeEach;
14+
import org.junit.jupiter.api.Test;
15+
import org.junit.jupiter.api.extension.ExtendWith;
16+
import org.mockito.Mock;
17+
import org.mockito.junit.jupiter.MockitoExtension;
18+
19+
import javax.net.ssl.SSLEngine;
20+
import javax.net.ssl.X509TrustManager;
21+
22+
import java.net.Socket;
23+
import java.security.cert.CertificateException;
24+
import java.security.cert.X509Certificate;
25+
26+
import static org.mockito.Mockito.doNothing;
27+
import static org.mockito.Mockito.mock;
28+
import static org.mockito.Mockito.times;
29+
import static org.mockito.Mockito.verify;
30+
import static org.mockito.Mockito.when;
31+
32+
@ExtendWith(MockitoExtension.class)
33+
class HostnameIgnoringTrustManagerTest {
34+
35+
private HostnameIgnoringTrustManager trustManager;
36+
37+
@Mock
38+
private X509TrustManager delegate;
39+
40+
@BeforeEach
41+
void setUp() {
42+
this.trustManager = new HostnameIgnoringTrustManager(delegate);
43+
}
44+
45+
@Test
46+
void testCheckClientTrusted() throws CertificateException {
47+
X509Certificate[] certs = new X509Certificate[]{};
48+
var authType = "test";
49+
Socket s = mock(Socket.class);
50+
51+
doNothing().when(delegate).checkClientTrusted(certs, authType);
52+
53+
trustManager.checkClientTrusted(certs, authType, s);
54+
55+
verify(delegate, times(1)).checkClientTrusted(certs, authType);
56+
}
57+
58+
@Test
59+
void testCheckClientTrusted2() throws CertificateException {
60+
X509Certificate[] certs = new X509Certificate[]{};
61+
var authType = "test";
62+
SSLEngine e = mock(SSLEngine.class);
63+
64+
doNothing().when(delegate).checkClientTrusted(certs, authType);
65+
66+
trustManager.checkClientTrusted(certs, authType, e);
67+
68+
verify(delegate, times(1)).checkClientTrusted(certs, authType);
69+
}
70+
71+
@Test
72+
void testCheckServerTrusted() throws CertificateException {
73+
X509Certificate[] certs = new X509Certificate[]{};
74+
var authType = "test";
75+
Socket s = mock(Socket.class);
76+
77+
doNothing().when(delegate).checkServerTrusted(certs, authType);
78+
79+
trustManager.checkServerTrusted(certs, authType, s);
80+
81+
verify(delegate, times(1)).checkServerTrusted(certs, authType);
82+
}
83+
84+
@Test
85+
void testCheckServerTrusted2() throws CertificateException {
86+
X509Certificate[] certs = new X509Certificate[]{};
87+
var authType = "test";
88+
SSLEngine e = mock(SSLEngine.class);
89+
90+
doNothing().when(delegate).checkServerTrusted(certs, authType);
91+
92+
trustManager.checkServerTrusted(certs, authType, e);
93+
94+
verify(delegate, times(1)).checkServerTrusted(certs, authType);
95+
}
96+
97+
@Test
98+
void testGetAcceptedIssuers() {
99+
when(delegate.getAcceptedIssuers()).thenReturn(null);
100+
trustManager.getAcceptedIssuers();
101+
verify(delegate, times(1)).getAcceptedIssuers();
102+
}
103+
}

discoverable-client/src/main/java/org/zowe/apiml/client/ws/WebSocketServerHandler.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
1717

1818
public class WebSocketServerHandler extends AbstractWebSocketHandler {
19+
1920
@Override
2021
public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage)
2122
throws Exception {
@@ -25,4 +26,5 @@ public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?>
2526
webSocketSession.close();
2627
}
2728
}
29+
2830
}

discoverable-client/src/test/java/org/zowe/apiml/client/ws/WebSocketServerHandlerTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,5 @@ void handleByeMessage() throws Exception {
4949
assertEquals("BYE", messageCaptor.getValue().getPayload().toString());
5050
verify(session, times(1)).close();
5151
}
52+
5253
}

0 commit comments

Comments
 (0)