This is an automated email from the ASF dual-hosted git repository. pzampino pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/knox.git
The following commit(s) were added to refs/heads/master by this push: new 8bb386ff4 KNOX-3172: New intercepting socket and outputstream to handle FIPS bouncycastle exception, set max connections for PoolingHttpClientConnectionManager (#1065) 8bb386ff4 is described below commit 8bb386ff400ed2d065ee2a3856c5a2654ca5a408 Author: hanicz <han...@users.noreply.github.com> AuthorDate: Wed Jul 30 21:14:36 2025 +0200 KNOX-3172: New intercepting socket and outputstream to handle FIPS bouncycastle exception, set max connections for PoolingHttpClientConnectionManager (#1065) --- .../apache/knox/gateway/SpiGatewayMessages.java | 6 + .../gateway/dispatch/DefaultHttpClientFactory.java | 25 +- .../BCInterceptingConnectionSocketFactory.java | 47 ++++ .../gateway/fips/BCInterceptingOutputStream.java | 100 ++++++++ .../knox/gateway/fips/BCInterceptingSocket.java | 275 +++++++++++++++++++++ .../gateway/fips/FipsConnectionManagerFactory.java | 51 ++++ .../org/apache/knox/gateway/fips/FipsUtils.java | 27 ++ .../fips/BCInterceptingOutputStreamTest.java | 180 ++++++++++++++ .../fips/FipsConnectionManagerFactoryTest.java | 33 +++ .../apache/knox/gateway/fips/FipsUtilsTest.java | 44 ++++ 10 files changed, 780 insertions(+), 8 deletions(-) diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/SpiGatewayMessages.java b/gateway-spi/src/main/java/org/apache/knox/gateway/SpiGatewayMessages.java index 4440069d8..1037bffc4 100644 --- a/gateway-spi/src/main/java/org/apache/knox/gateway/SpiGatewayMessages.java +++ b/gateway-spi/src/main/java/org/apache/knox/gateway/SpiGatewayMessages.java @@ -138,4 +138,10 @@ public interface SpiGatewayMessages { @Message( level = MessageLevel.ERROR, text = "Unable to close SSE producer" ) void sseProducerCloseError(@StackTrace(level=MessageLevel.ERROR) Exception e); + + @Message( level = MessageLevel.INFO, text = "BouncyCastle handleClose error, ignoring exception" ) + void ignoreHandleCloseError(); + + @Message(level = MessageLevel.INFO, text = "FIPS environment, configuring intercepting socket") + void configureInterceptingSocket(); } diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultHttpClientFactory.java b/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultHttpClientFactory.java index cb658c8fe..206a349e1 100644 --- a/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultHttpClientFactory.java +++ b/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultHttpClientFactory.java @@ -30,6 +30,8 @@ import javax.servlet.FilterConfig; import org.apache.commons.lang3.StringUtils; import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; import org.apache.http.ssl.SSLContextBuilder; +import org.apache.knox.gateway.fips.FipsConnectionManagerFactory; +import org.apache.knox.gateway.fips.FipsUtils; import org.apache.knox.gateway.services.ServiceType; import org.apache.knox.gateway.services.security.AliasService; import org.apache.knox.gateway.services.security.KeystoreService; @@ -91,11 +93,8 @@ public class DefaultHttpClientFactory implements HttpClientFactory { builder = HttpClients.custom(); } - // Conditionally set a custom SSLContext SSLContext sslContext = createSSLContext(services, filterConfig, serviceRole); - if(sslContext != null) { - builder.setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext)); - } + setSSLSocketFactory(sslContext, filterConfig, builder); if (Boolean.parseBoolean(System.getProperty(GatewayConfig.HADOOP_KERBEROS_SECURED))) { CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); @@ -117,10 +116,6 @@ public class DefaultHttpClientFactory implements HttpClientFactory { builder.setRedirectStrategy( new NeverRedirectStrategy() ); builder.setRetryHandler( new NeverRetryHandler() ); - int maxConnections = getMaxConnections( filterConfig ); - builder.setMaxConnTotal( maxConnections ); - builder.setMaxConnPerRoute( maxConnections ); - builder.setDefaultRequestConfig(getRequestConfig(filterConfig, serviceRole)); // See KNOX-1530 for details @@ -143,6 +138,20 @@ public class DefaultHttpClientFactory implements HttpClientFactory { return builder.build(); } + private void setSSLSocketFactory(SSLContext sslContext, FilterConfig filterConfig, HttpClientBuilder builder) { + int maxConnections = getMaxConnections(filterConfig); + if (FipsUtils.isFipsEnabledWithBCProvider()) { + builder.setConnectionManager(FipsConnectionManagerFactory.createConnectionManager(sslContext, maxConnections)); + } else { + builder.setMaxConnTotal(maxConnections); + builder.setMaxConnPerRoute(maxConnections); + // Conditionally set a custom SSLContext + if (sslContext != null) { + builder.setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext)); + } + } + } + private boolean doesRetryParamExist(final FilterConfig filterConfig) { return filterConfig.getInitParameter(PARAMETER_RETRY_COUNT) != null && StringUtils diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/fips/BCInterceptingConnectionSocketFactory.java b/gateway-spi/src/main/java/org/apache/knox/gateway/fips/BCInterceptingConnectionSocketFactory.java new file mode 100644 index 000000000..e7875aef4 --- /dev/null +++ b/gateway-spi/src/main/java/org/apache/knox/gateway/fips/BCInterceptingConnectionSocketFactory.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.knox.gateway.fips; + +import org.apache.http.HttpHost; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.protocol.HttpContext; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; + +public class BCInterceptingConnectionSocketFactory implements ConnectionSocketFactory { + private final ConnectionSocketFactory delegate; + + public BCInterceptingConnectionSocketFactory(ConnectionSocketFactory delegate) { + this.delegate = delegate; + } + + @Override + public Socket createSocket(HttpContext context) throws IOException { + return new BCInterceptingSocket(delegate.createSocket(context)); + } + + @Override + public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, + InetSocketAddress remoteAddress, InetSocketAddress localAddress, + HttpContext context) throws IOException { + Socket connected = delegate.connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, context); + return connected instanceof BCInterceptingSocket ? connected : new BCInterceptingSocket(connected); + } +} diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/fips/BCInterceptingOutputStream.java b/gateway-spi/src/main/java/org/apache/knox/gateway/fips/BCInterceptingOutputStream.java new file mode 100644 index 000000000..e6be1435e --- /dev/null +++ b/gateway-spi/src/main/java/org/apache/knox/gateway/fips/BCInterceptingOutputStream.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.knox.gateway.fips; + +import org.apache.commons.lang3.StringUtils; +import org.apache.knox.gateway.SpiGatewayMessages; +import org.apache.knox.gateway.i18n.messages.MessagesFactory; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.SocketException; + +public class BCInterceptingOutputStream extends OutputStream { + + private static final SpiGatewayMessages LOG = MessagesFactory.get(SpiGatewayMessages.class); + private final OutputStream delegate; + private static final String BOUNCYCASTLE_TLS_PROTOCOL_NAME = "org.bouncycastle.tls.TlsProtocol"; + private static final String BOUNCYCASTLE_HANDLE_CLOSE_METHOD = "handleClose"; + private static final String BROKEN_PIPE_MESSAGE = "Broken pipe"; + + public BCInterceptingOutputStream(OutputStream delegate) { + this.delegate = delegate; + } + + @Override + public void write(int b) throws IOException { + try { + delegate.write(b); + } catch (SocketException e) { + handleSocketException(e); + } + } + + @Override + public void write(byte[] b) throws IOException { + try { + delegate.write(b); + } catch (SocketException e) { + handleSocketException(e); + } + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + try { + delegate.write(b, off, len); + } catch (SocketException e) { + handleSocketException(e); + } + } + + @Override + public void flush() throws IOException { + delegate.flush(); + } + + @Override + public void close() throws IOException { + delegate.close(); + } + + /** + * This method swallows the socketException if org.bouncycastle.tls.TlsProtocol.handleClose method is in the stacktrace. + * This exception is thrown with 'Broken Pipe' message on FIPS environments where the BouncyCastle provider is used. + * @param socketException The exception thrown + * @throws SocketException The exception is thrown again if it's not the BouncyCastle one. + */ + private void handleSocketException(SocketException socketException) throws SocketException { + boolean exceptionIgnored = false; + if (socketException.getMessage() != null && StringUtils.containsIgnoreCase(socketException.getMessage(), BROKEN_PIPE_MESSAGE)) { + StackTraceElement[] stackTrace = socketException.getStackTrace(); + for (StackTraceElement ste : stackTrace) { + if (BOUNCYCASTLE_TLS_PROTOCOL_NAME.equals(ste.getClassName())) { + if (BOUNCYCASTLE_HANDLE_CLOSE_METHOD.equals(ste.getMethodName())) { + LOG.ignoreHandleCloseError(); + exceptionIgnored = true; + } + } + } + } + if (!exceptionIgnored) { + throw socketException; + } + } +} diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/fips/BCInterceptingSocket.java b/gateway-spi/src/main/java/org/apache/knox/gateway/fips/BCInterceptingSocket.java new file mode 100644 index 000000000..636607565 --- /dev/null +++ b/gateway-spi/src/main/java/org/apache/knox/gateway/fips/BCInterceptingSocket.java @@ -0,0 +1,275 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.knox.gateway.fips; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.net.SocketOption; +import java.nio.channels.SocketChannel; +import java.util.Set; + +public class BCInterceptingSocket extends Socket { + private final Socket delegate; + + public BCInterceptingSocket(Socket delegate) throws IOException { + this.delegate = delegate; + } + + @Override + public OutputStream getOutputStream() throws IOException { + OutputStream originalOutputStream = delegate.getOutputStream(); + return new BCInterceptingOutputStream(originalOutputStream); + } + + @Override + public InputStream getInputStream() throws IOException { + return delegate.getInputStream(); + } + + @Override + public void connect(SocketAddress endpoint) throws IOException { + delegate.connect(endpoint); + } + + @Override + public void connect(SocketAddress endpoint, int timeout) throws IOException { + delegate.connect(endpoint, timeout); + } + + @Override + public void bind(SocketAddress bindpoint) throws IOException { + delegate.bind(bindpoint); + } + + @Override + public void close() throws IOException { + delegate.close(); + } + + @Override + public boolean isConnected() { + return delegate.isConnected(); + } + + @Override + public boolean isClosed() { + return delegate.isClosed(); + } + + @Override + public boolean isInputShutdown() { + return delegate.isInputShutdown(); + } + + @Override + public boolean isOutputShutdown() { + return delegate.isOutputShutdown(); + } + + @Override + public void shutdownInput() throws IOException { + delegate.shutdownInput(); + } + + @Override + public void shutdownOutput() throws IOException { + delegate.shutdownOutput(); + } + + @Override + public InetAddress getInetAddress() { + return delegate.getInetAddress(); + } + + @Override + public InetAddress getLocalAddress() { + return delegate.getLocalAddress(); + } + + @Override + public int getPort() { + return delegate.getPort(); + } + + @Override + public int getLocalPort() { + return delegate.getLocalPort(); + } + + @Override + public SocketAddress getRemoteSocketAddress() { + return delegate.getRemoteSocketAddress(); + } + + @Override + public boolean isBound() { + return delegate.isBound(); + } + + @Override + public boolean getReuseAddress() throws SocketException { + return delegate.getReuseAddress(); + } + + @Override + public void setReuseAddress(boolean on) throws SocketException { + delegate.setReuseAddress(on); + } + + @Override + public int getTrafficClass() throws SocketException { + return delegate.getTrafficClass(); + } + + @Override + public void setTrafficClass(int tc) throws SocketException { + delegate.setTrafficClass(tc); + } + + @Override + public boolean getKeepAlive() throws SocketException { + return delegate.getKeepAlive(); + } + + @Override + public void setKeepAlive(boolean on) throws SocketException { + delegate.setKeepAlive(on); + } + + @Override + public SocketAddress getLocalSocketAddress() { + return delegate.getLocalSocketAddress(); + } + + @Override + public SocketChannel getChannel() { + return delegate.getChannel(); + } + + @Override + public void setTcpNoDelay(boolean on) throws SocketException { + delegate.setTcpNoDelay(on); + } + + @Override + public boolean getTcpNoDelay() throws SocketException { + return delegate.getTcpNoDelay(); + } + + @Override + public void setSoLinger(boolean on, int linger) throws SocketException { + delegate.setSoLinger(on, linger); + } + + @Override + public int getSoLinger() throws SocketException { + return delegate.getSoLinger(); + } + + @Override + public void sendUrgentData(int data) throws IOException { + delegate.sendUrgentData(data); + } + + @Override + public void setOOBInline(boolean on) throws SocketException { + delegate.setOOBInline(on); + } + + @Override + public boolean getOOBInline() throws SocketException { + return delegate.getOOBInline(); + } + + @Override + public void setSoTimeout(int timeout) throws SocketException { + delegate.setSoTimeout(timeout); + } + + @Override + public int getSoTimeout() throws SocketException { + return delegate.getSoTimeout(); + } + + @Override + public void setSendBufferSize(int size) throws SocketException { + delegate.setSendBufferSize(size); + } + + @Override + public int getSendBufferSize() throws SocketException { + return delegate.getSendBufferSize(); + } + + @Override + public void setReceiveBufferSize(int size) throws SocketException { + delegate.setReceiveBufferSize(size); + } + + @Override + public int getReceiveBufferSize() throws SocketException { + return delegate.getReceiveBufferSize(); + } + + @Override + public void setPerformancePreferences(int connectionTime, + int latency, + int bandwidth) { + delegate.setPerformancePreferences(connectionTime, latency, bandwidth); + } + + /** + * This method is only available in JDK9+ therefor reflection is used to call it. + */ + @SuppressWarnings({"PMD.MissingOverride", "unchecked"}) + public Set<SocketOption<?>> supportedOptions() { + return invokeDelegateMethod("supportedOptions", new Class<?>[]{}); + } + + /** + * This method is only available in JDK9+ therefor reflection is used to call it. + */ + @SuppressWarnings({"PMD.MissingOverride", "unchecked"}) + public <T> T getOption(SocketOption<T> name) throws IOException { + return invokeDelegateMethod("getOption", new Class<?>[]{SocketOption.class}, name); + } + + + /** + * This method is only available in JDK9+ therefor reflection is used to call it. + */ + @SuppressWarnings("PMD.MissingOverride") + public <T> Socket setOption(SocketOption<T> name, T value) throws IOException { + return invokeDelegateMethod("setOption", new Class<?>[]{SocketOption.class, Object.class}, name, value); + } + + @SuppressWarnings("unchecked") + private <T> T invokeDelegateMethod(String methodName, Class<?>[] parameterTypes, Object... args) { + try { + return (T) delegate.getClass().getMethod(methodName, parameterTypes).invoke(delegate, args); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new UnsupportedOperationException("Socket option not supported", e); + } + } +} diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/fips/FipsConnectionManagerFactory.java b/gateway-spi/src/main/java/org/apache/knox/gateway/fips/FipsConnectionManagerFactory.java new file mode 100644 index 000000000..d201a8fc2 --- /dev/null +++ b/gateway-spi/src/main/java/org/apache/knox/gateway/fips/FipsConnectionManagerFactory.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.knox.gateway.fips; + +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.knox.gateway.SpiGatewayMessages; +import org.apache.knox.gateway.i18n.messages.MessagesFactory; + +import javax.net.ssl.SSLContext; + +public class FipsConnectionManagerFactory { + + private static final SpiGatewayMessages LOG = MessagesFactory.get(SpiGatewayMessages.class); + + public static PoolingHttpClientConnectionManager createConnectionManager(SSLContext sslContext, int maxConnections) { + LOG.configureInterceptingSocket(); + BCInterceptingConnectionSocketFactory BCInterceptingConnectionSocketFactory = sslContext != null ? + new BCInterceptingConnectionSocketFactory(new SSLConnectionSocketFactory(sslContext)) + : new BCInterceptingConnectionSocketFactory(SSLConnectionSocketFactory.getSocketFactory()); + + Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create() + .register("http", new BCInterceptingConnectionSocketFactory(PlainConnectionSocketFactory.getSocketFactory())) + .register("https", BCInterceptingConnectionSocketFactory) + .build(); + + PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); + connManager.setMaxTotal(maxConnections); + connManager.setDefaultMaxPerRoute(maxConnections); + return connManager; + } +} diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/fips/FipsUtils.java b/gateway-spi/src/main/java/org/apache/knox/gateway/fips/FipsUtils.java new file mode 100644 index 000000000..e586e971e --- /dev/null +++ b/gateway-spi/src/main/java/org/apache/knox/gateway/fips/FipsUtils.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.knox.gateway.fips; + +public class FipsUtils { + + private static final String FIPS_SYSTEM_PROPERTY = "com.safelogic.cryptocomply.fips.approved_only"; + + public static boolean isFipsEnabledWithBCProvider() { + return Boolean.parseBoolean(System.getProperty(FIPS_SYSTEM_PROPERTY)); + } +} diff --git a/gateway-spi/src/test/java/org/apache/knox/gateway/fips/BCInterceptingOutputStreamTest.java b/gateway-spi/src/test/java/org/apache/knox/gateway/fips/BCInterceptingOutputStreamTest.java new file mode 100644 index 000000000..50e5ecb81 --- /dev/null +++ b/gateway-spi/src/test/java/org/apache/knox/gateway/fips/BCInterceptingOutputStreamTest.java @@ -0,0 +1,180 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.knox.gateway.fips; + +import org.easymock.EasyMock; +import org.junit.Test; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.SocketException; + +public class BCInterceptingOutputStreamTest { + + @Test + public void writeExceptionIgnoredTest() throws IOException { + OutputStream outputStream = EasyMock.createNiceMock(OutputStream.class); + BCInterceptingOutputStream BCInterceptingOutputStream = + new BCInterceptingOutputStream(outputStream); + SocketException socketException = EasyMock.createNiceMock(SocketException.class); + StackTraceElement ste = new StackTraceElement("org.bouncycastle.tls.TlsProtocol", "handleClose", null, 1); + StackTraceElement[] steArray = new StackTraceElement[1]; + steArray[0] = ste; + + outputStream.write(10); + EasyMock.expectLastCall().andThrow(socketException); + EasyMock.expect(socketException.getMessage()).andReturn("Broken pipe (Write failed)").times(2); + EasyMock.expect(socketException.getStackTrace()).andReturn(steArray); + EasyMock.replay(outputStream, socketException); + + BCInterceptingOutputStream.write(10); + } + + @Test + public void writeByteArrayExceptionIgnoredTest() throws IOException { + OutputStream outputStream = EasyMock.createNiceMock(OutputStream.class); + BCInterceptingOutputStream BCInterceptingOutputStream = + new BCInterceptingOutputStream(outputStream); + SocketException socketException = EasyMock.createNiceMock(SocketException.class); + StackTraceElement ste = new StackTraceElement("org.bouncycastle.tls.TlsProtocol", "handleClose", null, 1); + StackTraceElement[] steArray = new StackTraceElement[1]; + steArray[0] = ste; + + outputStream.write(new byte[]{10}); + EasyMock.expectLastCall().andThrow(socketException); + EasyMock.expect(socketException.getMessage()).andReturn("Broken pipe (Write failed)").times(2); + EasyMock.expect(socketException.getStackTrace()).andReturn(steArray); + EasyMock.replay(outputStream, socketException); + + BCInterceptingOutputStream.write(new byte[]{10}); + } + + @Test + public void writeByteArrayOffsetExceptionIgnoredTest() throws IOException { + OutputStream outputStream = EasyMock.createNiceMock(OutputStream.class); + BCInterceptingOutputStream BCInterceptingOutputStream = + new BCInterceptingOutputStream(outputStream); + SocketException socketException = EasyMock.createNiceMock(SocketException.class); + StackTraceElement ste = new StackTraceElement("org.bouncycastle.tls.TlsProtocol", "handleClose", null, 1); + StackTraceElement[] steArray = new StackTraceElement[1]; + steArray[0] = ste; + + outputStream.write(new byte[]{10}, 10, 10); + EasyMock.expectLastCall().andThrow(socketException); + EasyMock.expect(socketException.getMessage()).andReturn("Broken pipe (Write failed)").times(2); + EasyMock.expect(socketException.getStackTrace()).andReturn(steArray); + EasyMock.replay(outputStream, socketException); + + BCInterceptingOutputStream.write(new byte[]{10}, 10, 10); + } + + @Test + public void writeExceptionIgnoredJDK17Test() throws IOException { + OutputStream outputStream = EasyMock.createNiceMock(OutputStream.class); + BCInterceptingOutputStream BCInterceptingOutputStream = + new BCInterceptingOutputStream(outputStream); + SocketException socketException = EasyMock.createNiceMock(SocketException.class); + StackTraceElement ste = new StackTraceElement("org.bouncycastle.tls.TlsProtocol", "handleClose", null, 1); + StackTraceElement[] steArray = new StackTraceElement[1]; + steArray[0] = ste; + + outputStream.write(10); + EasyMock.expectLastCall().andThrow(socketException); + EasyMock.expect(socketException.getMessage()).andReturn("Broken pipe").times(2); + EasyMock.expect(socketException.getStackTrace()).andReturn(steArray); + EasyMock.replay(outputStream, socketException); + + BCInterceptingOutputStream.write(10); + } + + @Test(expected = SocketException.class) + public void writeDifferentMessageTest() throws IOException { + OutputStream outputStream = EasyMock.createNiceMock(OutputStream.class); + BCInterceptingOutputStream BCInterceptingOutputStream = + new BCInterceptingOutputStream(outputStream); + SocketException socketException = EasyMock.createNiceMock(SocketException.class); + StackTraceElement ste = new StackTraceElement("org.bouncycastle.tls.TlsProtocol", "handleClose", null, 1); + StackTraceElement[] steArray = new StackTraceElement[1]; + steArray[0] = ste; + + outputStream.write(10); + EasyMock.expectLastCall().andThrow(socketException); + EasyMock.expect(socketException.getMessage()).andReturn("Non Broken message (Write failed)").times(2); + EasyMock.expect(socketException.getStackTrace()).andReturn(steArray); + EasyMock.replay(outputStream, socketException); + + BCInterceptingOutputStream.write(10); + } + + @Test(expected = SocketException.class) + public void writeDifferentClassNameTest() throws IOException { + OutputStream outputStream = EasyMock.createNiceMock(OutputStream.class); + BCInterceptingOutputStream BCInterceptingOutputStream = + new BCInterceptingOutputStream(outputStream); + SocketException socketException = EasyMock.createNiceMock(SocketException.class); + StackTraceElement ste = new StackTraceElement("org.bouncycastle.tls.NonTlsProtocol", "handleClose", null, 1); + StackTraceElement[] steArray = new StackTraceElement[1]; + steArray[0] = ste; + + outputStream.write(10); + EasyMock.expectLastCall().andThrow(socketException); + EasyMock.expect(socketException.getMessage()).andReturn("Broken pipe (Write failed)").times(2); + EasyMock.expect(socketException.getStackTrace()).andReturn(steArray); + EasyMock.replay(outputStream, socketException); + + BCInterceptingOutputStream.write(10); + } + + @Test(expected = SocketException.class) + public void writeDifferentMethodNameTest() throws IOException { + OutputStream outputStream = EasyMock.createNiceMock(OutputStream.class); + BCInterceptingOutputStream BCInterceptingOutputStream = + new BCInterceptingOutputStream(outputStream); + SocketException socketException = EasyMock.createNiceMock(SocketException.class); + StackTraceElement ste = new StackTraceElement("org.bouncycastle.tls.TlsProtocol", "nonHandleClose", null, 1); + StackTraceElement[] steArray = new StackTraceElement[1]; + steArray[0] = ste; + + outputStream.write(10); + EasyMock.expectLastCall().andThrow(socketException); + EasyMock.expect(socketException.getMessage()).andReturn("Broken pipe (Write failed)").times(2); + EasyMock.expect(socketException.getStackTrace()).andReturn(steArray); + EasyMock.replay(outputStream, socketException); + + BCInterceptingOutputStream.write(10); + } + + @Test(expected = IOException.class) + public void writeNonSocketExceptionTest() throws IOException { + OutputStream outputStream = EasyMock.createNiceMock(OutputStream.class); + BCInterceptingOutputStream BCInterceptingOutputStream = + new BCInterceptingOutputStream(outputStream); + IOException ioException = EasyMock.createNiceMock(IOException.class); + StackTraceElement ste = new StackTraceElement("org.bouncycastle.tls.TlsProtocol", "handleClose", null, 1); + StackTraceElement[] steArray = new StackTraceElement[1]; + steArray[0] = ste; + + outputStream.write(10); + EasyMock.expectLastCall().andThrow(ioException); + EasyMock.expect(ioException.getMessage()).andReturn("Broken pipe (Write failed)").times(2); + EasyMock.expect(ioException.getStackTrace()).andReturn(steArray); + EasyMock.replay(outputStream, ioException); + + BCInterceptingOutputStream.write(10); + } +} diff --git a/gateway-spi/src/test/java/org/apache/knox/gateway/fips/FipsConnectionManagerFactoryTest.java b/gateway-spi/src/test/java/org/apache/knox/gateway/fips/FipsConnectionManagerFactoryTest.java new file mode 100644 index 000000000..83b1047b2 --- /dev/null +++ b/gateway-spi/src/test/java/org/apache/knox/gateway/fips/FipsConnectionManagerFactoryTest.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.knox.gateway.fips; + +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class FipsConnectionManagerFactoryTest { + + @Test + public void testCreateConnectionManager() { + PoolingHttpClientConnectionManager connectionManager = FipsConnectionManagerFactory.createConnectionManager(null, 10); + assertEquals(10, connectionManager.getMaxTotal()); + assertEquals(10, connectionManager.getDefaultMaxPerRoute()); + } +} diff --git a/gateway-spi/src/test/java/org/apache/knox/gateway/fips/FipsUtilsTest.java b/gateway-spi/src/test/java/org/apache/knox/gateway/fips/FipsUtilsTest.java new file mode 100644 index 000000000..df2364b82 --- /dev/null +++ b/gateway-spi/src/test/java/org/apache/knox/gateway/fips/FipsUtilsTest.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.knox.gateway.fips; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class FipsUtilsTest { + + @Test + public void testIsFipsEnabledWithBCProviderEmpty() { + System.clearProperty("com.safelogic.cryptocomply.fips.approved_only"); + assertFalse(FipsUtils.isFipsEnabledWithBCProvider()); + } + + @Test + public void testIsFipsEnabledWithBCProviderSetToTrue() { + System.setProperty("com.safelogic.cryptocomply.fips.approved_only", "true"); + assertTrue(FipsUtils.isFipsEnabledWithBCProvider()); + } + + @Test + public void testIsFipsEnabledWithBCProviderSetToFalse() { + System.setProperty("com.safelogic.cryptocomply.fips.approved_only", "false"); + assertFalse(FipsUtils.isFipsEnabledWithBCProvider()); + } +}