From 64495e99e97e9f7279ed8b5317b070fd64944fbf Mon Sep 17 00:00:00 2001 From: exceptionfactory Date: Tue, 16 Nov 2021 15:22:09 -0600 Subject: [PATCH] NIFI-7835 Added authenticated SOCKS proxy support for SFTP NIFI-7749 Added authenticated HTTP proxy support for SFTP - Added StandardSocketFactoryProvider to return SocketFactory based on credentials Signed-off-by: Joe Gresock This closes #5624. --- .../nifi-standard-processors/pom.xml | 5 ++ .../{ssh => socket}/ProxySocketFactory.java | 2 +- .../socket/SocketFactoryProvider.java | 34 ++++++++ .../socket/StandardSocketFactoryProvider.java | 65 +++++++++++++++ .../ssh/StandardSSHClientProvider.java | 7 +- .../ProxySocketFactoryTest.java | 2 +- .../StandardSocketFactoryProviderTest.java | 80 +++++++++++++++++++ 7 files changed, 191 insertions(+), 4 deletions(-) rename nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/{ssh => socket}/ProxySocketFactory.java (98%) create mode 100644 nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/socket/SocketFactoryProvider.java create mode 100644 nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/socket/StandardSocketFactoryProvider.java rename nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/{ssh => socket}/ProxySocketFactoryTest.java (96%) create mode 100644 nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/socket/StandardSocketFactoryProviderTest.java diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml index aca1bd08b1..72d760d7cb 100644 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml @@ -141,6 +141,11 @@ com.hierynomus sshj + + com.exceptionfactory.socketbroker + socketbroker + 1.0.2 + org.eclipse.jetty jetty-server diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ssh/ProxySocketFactory.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/socket/ProxySocketFactory.java similarity index 98% rename from nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ssh/ProxySocketFactory.java rename to nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/socket/ProxySocketFactory.java index 4414880d82..d5125b0407 100644 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ssh/ProxySocketFactory.java +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/socket/ProxySocketFactory.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.processors.standard.ssh; +package org.apache.nifi.processors.standard.socket; import javax.net.SocketFactory; import java.io.IOException; diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/socket/SocketFactoryProvider.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/socket/SocketFactoryProvider.java new file mode 100644 index 0000000000..357eb47e53 --- /dev/null +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/socket/SocketFactoryProvider.java @@ -0,0 +1,34 @@ +/* + * 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.nifi.processors.standard.socket; + +import org.apache.nifi.proxy.ProxyConfiguration; + +import javax.net.SocketFactory; + +/** + * Socket Factory Provider abstracts implementation selection based on Proxy Configuration + */ +public interface SocketFactoryProvider { + /** + * Get Socket Factory based on provided Proxy Configuration + * + * @param proxyConfiguration Proxy Configuration required + * @return Socket Factory + */ + SocketFactory getSocketFactory(ProxyConfiguration proxyConfiguration); +} diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/socket/StandardSocketFactoryProvider.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/socket/StandardSocketFactoryProvider.java new file mode 100644 index 0000000000..967943d891 --- /dev/null +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/socket/StandardSocketFactoryProvider.java @@ -0,0 +1,65 @@ +/* + * 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.nifi.processors.standard.socket; + +import com.exceptionfactory.socketbroker.BrokeredSocketFactory; +import com.exceptionfactory.socketbroker.configuration.AuthenticationCredentials; +import com.exceptionfactory.socketbroker.configuration.BrokerConfiguration; +import com.exceptionfactory.socketbroker.configuration.ProxyType; +import com.exceptionfactory.socketbroker.configuration.StandardBrokerConfiguration; +import com.exceptionfactory.socketbroker.configuration.StandardUsernamePasswordAuthenticationCredentials; +import org.apache.nifi.proxy.ProxyConfiguration; + +import javax.net.SocketFactory; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.util.Objects; + +/** + * Standard implementation of Socket Factory Provider support authenticated or unauthenticated SOCKS or HTTP proxies + */ +public class StandardSocketFactoryProvider implements SocketFactoryProvider { + /** + * Get Socket Factory returns ProxySocketFactory without credentials and BrokeredSocketFactory with credentials + * + * @param proxyConfiguration Proxy Configuration required + * @return Socket Factory + */ + @Override + public SocketFactory getSocketFactory(final ProxyConfiguration proxyConfiguration) { + Objects.requireNonNull(proxyConfiguration, "Proxy Configuration required"); + + final String userName = proxyConfiguration.getProxyUserName(); + final SocketFactory socketFactory; + if (userName == null) { + final Proxy proxy = proxyConfiguration.createProxy(); + socketFactory = new ProxySocketFactory(proxy); + } else { + final Proxy.Type proxyType = proxyConfiguration.getProxyType(); + final ProxyType brokerProxyType = Proxy.Type.SOCKS == proxyType ? ProxyType.SOCKS5 : ProxyType.HTTP_CONNECT; + final InetSocketAddress proxySocketAddress = new InetSocketAddress(proxyConfiguration.getProxyServerHost(), proxyConfiguration.getProxyServerPort()); + + final String proxyPassword = proxyConfiguration.getProxyUserPassword(); + final char[] brokerProxyPassword = proxyPassword == null ? new char[]{} : proxyPassword.toCharArray(); + final AuthenticationCredentials credentials = new StandardUsernamePasswordAuthenticationCredentials(userName, brokerProxyPassword); + + final BrokerConfiguration brokerConfiguration = new StandardBrokerConfiguration(brokerProxyType, proxySocketAddress, credentials); + socketFactory = new BrokeredSocketFactory(brokerConfiguration, SocketFactory.getDefault()); + } + return socketFactory; + } +} diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ssh/StandardSSHClientProvider.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ssh/StandardSSHClientProvider.java index 758ba06b46..f16da8db46 100644 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ssh/StandardSSHClientProvider.java +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ssh/StandardSSHClientProvider.java @@ -34,6 +34,8 @@ import net.schmizz.sshj.userauth.password.PasswordFinder; import net.schmizz.sshj.userauth.password.PasswordUtils; import org.apache.nifi.context.PropertyContext; +import org.apache.nifi.processors.standard.socket.SocketFactoryProvider; +import org.apache.nifi.processors.standard.socket.StandardSocketFactoryProvider; import org.apache.nifi.proxy.ProxyConfiguration; import org.apache.nifi.util.StringUtils; @@ -68,6 +70,8 @@ import static org.apache.nifi.processors.standard.util.SFTPTransfer.USE_COMPRESS public class StandardSSHClientProvider implements SSHClientProvider { private static final SSHConfigProvider SSH_CONFIG_PROVIDER = new StandardSSHConfigProvider(); + private static final SocketFactoryProvider SOCKET_FACTORY_PROVIDER = new StandardSocketFactoryProvider(); + private static final List SUPPORTED_PROXY_TYPES = Arrays.asList(Proxy.Type.HTTP, Proxy.Type.SOCKS); private static final String ADDRESS_FORMAT = "%s:%d"; @@ -170,8 +174,7 @@ public class StandardSSHClientProvider implements SSHClientProvider { final ProxyConfiguration proxyConfiguration = ProxyConfiguration.getConfiguration(context, createComponentProxyConfigSupplier(context)); final Proxy.Type proxyType = proxyConfiguration.getProxyType(); if (SUPPORTED_PROXY_TYPES.contains(proxyType)) { - final Proxy proxy = proxyConfiguration.createProxy(); - final SocketFactory socketFactory = new ProxySocketFactory(proxy); + final SocketFactory socketFactory = SOCKET_FACTORY_PROVIDER.getSocketFactory(proxyConfiguration); client.setSocketFactory(socketFactory); } } diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/ssh/ProxySocketFactoryTest.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/socket/ProxySocketFactoryTest.java similarity index 96% rename from nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/ssh/ProxySocketFactoryTest.java rename to nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/socket/ProxySocketFactoryTest.java index d599e1883e..90a5e2780f 100644 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/ssh/ProxySocketFactoryTest.java +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/socket/ProxySocketFactoryTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.nifi.processors.standard.ssh; +package org.apache.nifi.processors.standard.socket; import org.junit.jupiter.api.Test; diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/socket/StandardSocketFactoryProviderTest.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/socket/StandardSocketFactoryProviderTest.java new file mode 100644 index 0000000000..7680f13193 --- /dev/null +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/socket/StandardSocketFactoryProviderTest.java @@ -0,0 +1,80 @@ +/* + * 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.nifi.processors.standard.socket; + +import com.exceptionfactory.socketbroker.BrokeredSocketFactory; +import org.apache.nifi.proxy.ProxyConfiguration; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import javax.net.SocketFactory; +import java.net.Proxy; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class StandardSocketFactoryProviderTest { + private static final String HOST = "localhost"; + + private static final int PORT = 1080; + + private static final String USERNAME = "user"; + + private static final String PASSWORD = "password"; + + private StandardSocketFactoryProvider provider; + + @BeforeEach + public void setProvider() { + provider = new StandardSocketFactoryProvider(); + } + + @Test + public void testGetSocketFactoryWithoutCredentials() { + final ProxyConfiguration proxyConfiguration = new ProxyConfiguration(); + proxyConfiguration.setProxyType(Proxy.Type.SOCKS); + proxyConfiguration.setProxyServerHost(HOST); + proxyConfiguration.setProxyServerPort(PORT); + + final SocketFactory socketFactory = provider.getSocketFactory(proxyConfiguration); + assertEquals(ProxySocketFactory.class, socketFactory.getClass()); + } + + @Test + public void testGetSocketFactoryWithUsername() { + final ProxyConfiguration proxyConfiguration = new ProxyConfiguration(); + proxyConfiguration.setProxyType(Proxy.Type.SOCKS); + proxyConfiguration.setProxyServerHost(HOST); + proxyConfiguration.setProxyServerPort(PORT); + proxyConfiguration.setProxyUserName(USERNAME); + + final SocketFactory socketFactory = provider.getSocketFactory(proxyConfiguration); + assertEquals(BrokeredSocketFactory.class, socketFactory.getClass()); + } + + @Test + public void testGetSocketFactoryWithUsernamePassword() { + final ProxyConfiguration proxyConfiguration = new ProxyConfiguration(); + proxyConfiguration.setProxyType(Proxy.Type.SOCKS); + proxyConfiguration.setProxyServerHost(HOST); + proxyConfiguration.setProxyServerPort(PORT); + proxyConfiguration.setProxyUserName(USERNAME); + proxyConfiguration.setProxyUserPassword(PASSWORD); + + final SocketFactory socketFactory = provider.getSocketFactory(proxyConfiguration); + assertEquals(BrokeredSocketFactory.class, socketFactory.getClass()); + } +}