From aeada5b1a6917202532c07e744b8c4612e6dda9e Mon Sep 17 00:00:00 2001 From: exceptionfactory Date: Sat, 18 Jan 2025 11:18:52 -0600 Subject: [PATCH] NIFI-14068 Fixed Domain Name Handling for SFTP Proxy Access - Added Standard extension of SSHClient with Socket Address creation based on proxy configuration --- .../standard/ssh/StandardSSHClient.java | 56 ++++++++++++++++ .../ssh/StandardSSHClientProvider.java | 2 +- .../standard/ssh/StandardSSHClientTest.java | 64 +++++++++++++++++++ 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ssh/StandardSSHClient.java create mode 100644 nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/ssh/StandardSSHClientTest.java diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ssh/StandardSSHClient.java b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ssh/StandardSSHClient.java new file mode 100644 index 000000000000..0076663a8799 --- /dev/null +++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ssh/StandardSSHClient.java @@ -0,0 +1,56 @@ +/* + * 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.ssh; + +import com.exceptionfactory.socketbroker.BrokeredSocketFactory; +import net.schmizz.sshj.Config; +import net.schmizz.sshj.SSHClient; +import org.apache.nifi.processors.standard.socket.ProxySocketFactory; + +import javax.net.SocketFactory; +import java.net.InetSocketAddress; + +/** + * Standard extension of SSHJ SSHClient supporting unresolved Socket Addresses for control over proxy behavior + */ +public class StandardSSHClient extends SSHClient { + + public StandardSSHClient(final Config config) { + super(config); + } + + /** + * Create InetSocketAddress to based on proxy configuration + * + * @param hostname Hostname or Internet Protocol address + * @param port TCP port number + * @return Socket Address resolved or unresolved based on proxy configuration + */ + @Override + protected InetSocketAddress makeInetSocketAddress(final String hostname, final int port) { + final SocketFactory socketFactory = getSocketFactory(); + final boolean proxyConfigured = socketFactory instanceof ProxySocketFactory || socketFactory instanceof BrokeredSocketFactory; + + final InetSocketAddress socketAddress; + if (proxyConfigured) { + socketAddress = InetSocketAddress.createUnresolved(hostname, port); + } else { + socketAddress = new InetSocketAddress(hostname, port); + } + return socketAddress; + } +} diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ssh/StandardSSHClientProvider.java b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ssh/StandardSSHClientProvider.java index fae64eace378..b8261b019d6e 100644 --- a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ssh/StandardSSHClientProvider.java +++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/ssh/StandardSSHClientProvider.java @@ -97,7 +97,7 @@ public SSHClient getClient(final PropertyContext context, final Map authMethods = getPasswordAuthMethods(context, attributes); final Config config = SSH_CONFIG_PROVIDER.getConfig(address, context); - final SSHClient client = new SSHClient(config); + final SSHClient client = new StandardSSHClient(config); try { setClientProperties(client, context); diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/ssh/StandardSSHClientTest.java b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/ssh/StandardSSHClientTest.java new file mode 100644 index 000000000000..d8766c13ffbe --- /dev/null +++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/ssh/StandardSSHClientTest.java @@ -0,0 +1,64 @@ +/* + * 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.ssh; + +import net.schmizz.sshj.DefaultConfig; +import org.apache.nifi.processors.standard.socket.ProxySocketFactory; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Proxy; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class StandardSSHClientTest { + private static final String HOSTNAME = "localhost"; + + private static final int PORT = 22; + + private static final int PROXY_PORT = 8080; + + @Test + void testMakeInetSocketAddressResolved() throws IOException { + try (StandardSSHClient client = new StandardSSHClient(new DefaultConfig())) { + final InetSocketAddress address = client.makeInetSocketAddress(HOSTNAME, PORT); + + assertFalse(address.isUnresolved()); + assertEquals(PORT, address.getPort()); + assertEquals(HOSTNAME, address.getHostString()); + } + } + + @Test + void testMakeInetSocketAddressUnresolved() throws IOException { + final InetSocketAddress proxyAddress = InetSocketAddress.createUnresolved(HOSTNAME, PROXY_PORT); + final Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyAddress); + final ProxySocketFactory proxySocketFactory = new ProxySocketFactory(proxy); + + try (StandardSSHClient client = new StandardSSHClient(new DefaultConfig())) { + client.setSocketFactory(proxySocketFactory); + final InetSocketAddress address = client.makeInetSocketAddress(HOSTNAME, PORT); + + assertTrue(address.isUnresolved()); + assertEquals(PORT, address.getPort()); + assertEquals(HOSTNAME, address.getHostString()); + } + } +}