Ext service integration support for Netconf SSH client 99/108499/10
authorRuslan Kashapov <ruslan.kashapov@pantheon.tech>
Wed, 18 Oct 2023 14:21:51 +0000 (17:21 +0300)
committerRobert Varga <nite@hq.sk>
Mon, 13 Nov 2023 15:30:01 +0000 (15:30 +0000)
Netconf topology and call-home components are using
own services for authorization, configuration only
approach for ssh client is not sufficient.

JIRA: NETCONF-1108
Change-Id: Iac63445067b235437ebf6d2c06df7f9f22acbf89
Signed-off-by: Ruslan Kashapov <ruslan.kashapov@pantheon.tech>
protocol/netconf-client/src/main/java/org/opendaylight/netconf/client/NetconfClientFactoryImpl.java
protocol/netconf-client/src/main/java/org/opendaylight/netconf/client/conf/NetconfClientConfiguration.java
protocol/netconf-client/src/main/java/org/opendaylight/netconf/client/conf/NetconfClientConfigurationBuilder.java
protocol/netconf-client/src/test/java/org/opendaylight/netconf/client/NetconfClientFactoryImplTest.java

index 36ddec034509f1a00de995beb56847d9383fb7fc..6c9424db2d3dc61eae0c07a07e20b91ccae4408e 100644 (file)
@@ -77,7 +77,7 @@ public class NetconfClientFactoryImpl implements NetconfClientFactory {
         } else if (SSH.equals(protocol)) {
             factory.connectClient(TransportConstants.SSH_SUBSYSTEM,
                 new ClientTransportChannelListener(future, channelInitializer), configuration.getTcpParameters(),
-                configuration.getSshParameters());
+                configuration.getSshParameters(), configuration.getSshConfigurator());
         }
         return future;
     }
index 9653a6b9765aa43e0e802533df1447455fff563d..24b7a75cfde21ea206909e76dad1965c08100336 100644 (file)
@@ -21,6 +21,7 @@ import org.opendaylight.netconf.client.NetconfClientSessionListener;
 import org.opendaylight.netconf.client.SslHandlerFactory;
 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
 import org.opendaylight.netconf.nettyutil.handler.ssh.client.NetconfSshClient;
+import org.opendaylight.netconf.transport.ssh.ClientFactoryManagerConfigurator;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev230417.SshClientGrouping;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tcp.client.rev230417.TcpClientGrouping;
@@ -50,6 +51,7 @@ public class NetconfClientConfiguration {
     private final TlsClientGrouping tlsParameters;
     private final org.opendaylight.netconf.transport.tls.SslHandlerFactory transportSslHandlerFactory;
     private final SshClientGrouping sshParameters;
+    private final ClientFactoryManagerConfigurator sshConfigurator;
 
     NetconfClientConfiguration(final NetconfClientProtocol protocol, final InetSocketAddress address,
             final Long connectionTimeoutMillis,
@@ -74,6 +76,7 @@ public class NetconfClientConfiguration {
         this.tlsParameters = null;
         this.transportSslHandlerFactory = null;
         this.sshParameters = null;
+        this.sshConfigurator = null;
         validateConfiguration();
     }
 
@@ -82,6 +85,7 @@ public class NetconfClientConfiguration {
             final TlsClientGrouping tlsParameters,
             final org.opendaylight.netconf.transport.tls.SslHandlerFactory transportSslHandlerFactory,
             final SshClientGrouping sshParameters,
+            final ClientFactoryManagerConfigurator sshConfigurator,
             final NetconfClientSessionListener sessionListener,
             final List<Uri> odlHelloCapabilities,
             final Long connectionTimeoutMillis,
@@ -93,6 +97,7 @@ public class NetconfClientConfiguration {
         this.tlsParameters = tlsParameters;
         this.transportSslHandlerFactory = transportSslHandlerFactory;
         this.sshParameters = sshParameters;
+        this.sshConfigurator = sshConfigurator;
         this.sessionListener = requireNonNull(sessionListener);
         this.odlHelloCapabilities = odlHelloCapabilities;
         this.connectionTimeoutMillis = connectionTimeoutMillis;
@@ -172,6 +177,10 @@ public class NetconfClientConfiguration {
         return sshParameters;
     }
 
+    public ClientFactoryManagerConfigurator getSshConfigurator() {
+        return sshConfigurator;
+    }
+
     private void validateConfiguration() {
         switch (requireNonNull(clientProtocol)) {
             case TLS:
index ce368e7cdf33ab3d42b479190e26a6526e40168d..01fb02c1da1a7c38986b4f9d887434e20b4779a5 100644 (file)
@@ -18,6 +18,7 @@ import org.opendaylight.netconf.client.SslHandlerFactory;
 import org.opendaylight.netconf.nettyutil.NetconfSessionNegotiator;
 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
 import org.opendaylight.netconf.nettyutil.handler.ssh.client.NetconfSshClient;
+import org.opendaylight.netconf.transport.ssh.ClientFactoryManagerConfigurator;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev230417.SshClientGrouping;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tcp.client.rev230417.TcpClientGrouping;
@@ -48,6 +49,7 @@ public class NetconfClientConfigurationBuilder {
     private TlsClientGrouping tlsParameters;
     private org.opendaylight.netconf.transport.tls.SslHandlerFactory transportSslHandlerFactory;
     private SshClientGrouping sshParameters;
+    private ClientFactoryManagerConfigurator sshConfigurator;
 
     protected NetconfClientConfigurationBuilder() {
     }
@@ -258,6 +260,19 @@ public class NetconfClientConfigurationBuilder {
         return this;
     }
 
+    /**
+     * Set SSH Client Factory Manager configurator.
+     *
+     * @param sshConfigurator configurator
+     * @return current builder instance
+     */
+    @SuppressWarnings("checkstyle:hiddenField")
+    public NetconfClientConfigurationBuilder withSshConfigurator(
+            final ClientFactoryManagerConfigurator sshConfigurator) {
+        this.sshConfigurator = sshConfigurator;
+        return this;
+    }
+
     final InetSocketAddress getAddress() {
         return address;
     }
@@ -315,7 +330,7 @@ public class NetconfClientConfigurationBuilder {
                 maximumIncomingChunkSize, name)
             // new configuration
             : new NetconfClientConfiguration(clientProtocol, tcpParameters, tlsParameters, transportSslHandlerFactory,
-                sshParameters, sessionListener, odlHelloCapabilities, connectionTimeoutMillis,
+                sshParameters, sshConfigurator, sessionListener, odlHelloCapabilities, connectionTimeoutMillis,
                 maximumIncomingChunkSize, additionalHeader, name);
     }
 }
index ea8436b68d931f526b3f896b4022c837e42a1b24..f0497504864091d77679e7a023945fc95989189f 100644 (file)
@@ -46,9 +46,14 @@ import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
 import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
+import org.opendaylight.netconf.shaded.sshd.client.auth.password.PasswordIdentityProvider;
+import org.opendaylight.netconf.shaded.sshd.server.auth.password.UserAuthPasswordFactory;
+import org.opendaylight.netconf.shaded.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
 import org.opendaylight.netconf.transport.api.TransportChannel;
 import org.opendaylight.netconf.transport.api.TransportChannelListener;
+import org.opendaylight.netconf.transport.ssh.ClientFactoryManagerConfigurator;
 import org.opendaylight.netconf.transport.ssh.SSHTransportStackFactory;
+import org.opendaylight.netconf.transport.ssh.ServerFactoryManagerConfigurator;
 import org.opendaylight.netconf.transport.tcp.TCPServer;
 import org.opendaylight.netconf.transport.tls.TLSServer;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana.crypt.hash.rev140806.CryptHash;
@@ -258,4 +263,38 @@ class NetconfClientFactoryImplTest {
             new UsersBuilder().setUser(Map.of(user.key(), user)).build()
         ).build();
     }
+
+    @Test
+    void sshClientWithConfigurator() throws Exception {
+        final ServerFactoryManagerConfigurator serverConfigurator = factoryManager -> {
+            factoryManager.setUserAuthFactories(List.of(new UserAuthPasswordFactory()));
+            factoryManager.setPasswordAuthenticator(
+                (usr, psw, session) -> USERNAME.equals(usr) && PASSWORD.equals(psw));
+            factoryManager.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
+        };
+        final ClientFactoryManagerConfigurator clientConfigurator = factoryManager -> {
+            factoryManager.setPasswordIdentityProvider(PasswordIdentityProvider.wrapPasswords(PASSWORD));
+            factoryManager.setUserAuthFactories(List.of(
+                new org.opendaylight.netconf.shaded.sshd.client.auth.password.UserAuthPasswordFactory()));
+        };
+
+        final var server = serverTransportFactory.listenServer("netconf", serverTransportListener, tcpServerParams,
+            null, serverConfigurator).get(10, TimeUnit.SECONDS);
+        try {
+            final var clientConfig = NetconfClientConfigurationBuilder.create()
+                .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH)
+                .withTcpParameters(tcpClientParams)
+                .withSshParameters(new SshClientParametersBuilder()
+                    .setClientIdentity(new ClientIdentityBuilder().setUsername(USERNAME).build()).build())
+                .withSshConfigurator(clientConfigurator)
+                .withSessionListener(sessionListener)
+                .withConnectionTimeoutMillis(10_000)
+                .build();
+            assertNotNull(factory.createClient(clientConfig));
+            verify(serverTransportListener, timeout(10_000L))
+                .onTransportChannelEstablished(any(TransportChannel.class));
+        } finally {
+            server.shutdown().get(1, TimeUnit.SECONDS);
+        }
+    }
 }