From: Tomas Cere Date: Fri, 27 Oct 2017 14:43:25 +0000 (+0000) Subject: Merge "Remove the default loopback mount from the old connector feature" X-Git-Tag: release/oxygen~43 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=4653c163772a24ebefdcb21a08315f539678ad4e;hp=0a8023b85121107d494caaa7b86d4df71dfafb6a;p=netconf.git Merge "Remove the default loopback mount from the old connector feature" --- diff --git a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeMountSessionContext.java b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeMountSessionContext.java index 539f6ab315..9c0de4d55c 100644 --- a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeMountSessionContext.java +++ b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeMountSessionContext.java @@ -21,7 +21,7 @@ import org.opendaylight.netconf.client.NetconfClientSessionListener; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordDeprecatedBuilder; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder; @@ -67,7 +67,7 @@ class CallHomeMountSessionContext { node.setHost(new Host(key.getIpAddress())); node.setPort(key.getPort()); node.setTcpOnly(Boolean.FALSE); - node.setCredentials(new LoginPasswordBuilder().setUsername("ommited").setPassword("ommited").build()); + node.setCredentials(new LoginPasswordDeprecatedBuilder().setUsername("ommited").setPassword("ommited").build()); node.setSchemaless(Boolean.FALSE); return node.build(); } diff --git a/netconf/netconf-client/src/test/java/org/opendaylight/netconf/client/TestingNetconfClient.java b/netconf/netconf-client/src/test/java/org/opendaylight/netconf/client/TestingNetconfClient.java index 10f7ae893d..7f25c25031 100644 --- a/netconf/netconf-client/src/test/java/org/opendaylight/netconf/client/TestingNetconfClient.java +++ b/netconf/netconf-client/src/test/java/org/opendaylight/netconf/client/TestingNetconfClient.java @@ -30,7 +30,7 @@ import org.opendaylight.netconf.client.conf.NetconfClientConfiguration; import org.opendaylight.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol; import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder; import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; -import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword; +import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler; import org.opendaylight.protocol.framework.NeverReconnectStrategy; @@ -108,7 +108,7 @@ public class TestingNetconfClient implements Closeable { NioEventLoopGroup nettyGroup = new NioEventLoopGroup(); NetconfClientDispatcherImpl netconfClientDispatcher = new NetconfClientDispatcherImpl(nettyGroup, nettyGroup, hashedWheelTimer); - LoginPassword authHandler = new LoginPassword("admin", "admin"); + LoginPasswordHandler authHandler = new LoginPasswordHandler("admin", "admin"); TestingNetconfClient client = new TestingNetconfClient("client", netconfClientDispatcher, getClientConfig("127.0.0.1", 1830, true, Optional.of(authHandler))); System.console().writer().println(client.getCapabilities()); diff --git a/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfConnectDeviceCommand.java b/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfConnectDeviceCommand.java index 6ecaec63b2..6cffd1b164 100644 --- a/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfConnectDeviceCommand.java +++ b/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfConnectDeviceCommand.java @@ -20,7 +20,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types. import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordDeprecatedBuilder; @Command(name = "netconf:connect-device", scope = "netconf", description = "Connect to a netconf device.") public class NetconfConnectDeviceCommand extends AbstractAction { @@ -95,7 +95,8 @@ public class NetconfConnectDeviceCommand extends AbstractAction { final boolean isTcpOnly = connectionType.equals("true"); final boolean isSchemaless = schemaless.equals("true"); - final Credentials credentials = new LoginPasswordBuilder().setPassword(password).setUsername(username).build(); + final Credentials credentials = + new LoginPasswordDeprecatedBuilder().setPassword(password).setUsername(username).build(); final NetconfNode netconfNode = new NetconfNodeBuilder() .setHost(new Host(new IpAddress(new Ipv4Address(deviceIp)))) diff --git a/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/impl/NetconfCommandsImpl.java b/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/impl/NetconfCommandsImpl.java index 9a9fa6e1ee..be851a1b23 100644 --- a/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/impl/NetconfCommandsImpl.java +++ b/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/impl/NetconfCommandsImpl.java @@ -37,7 +37,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev15 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordDeprecatedBuilder; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; @@ -223,7 +223,7 @@ public class NetconfCommandsImpl implements NetconfCommands { ? updated.get(NetconfConsoleConstants.PASSWORD) : password; final Credentials credentials = - new LoginPasswordBuilder().setPassword(newPassword).setUsername(newUsername).build(); + new LoginPasswordDeprecatedBuilder().setPassword(newPassword).setUsername(newUsername).build(); final NetconfNode updatedNetconfNode = new NetconfNodeBuilder() .setHost(new Host(new IpAddress(new Ipv4Address(deviceIp)))) .setPort(new PortNumber(Integer.decode(devicePort))) diff --git a/netconf/netconf-console/src/test/resources/schemas/netconf-node-topology.yang b/netconf/netconf-console/src/test/resources/schemas/netconf-node-topology.yang index d52b93dd25..ffddaa2463 100644 --- a/netconf/netconf-console/src/test/resources/schemas/netconf-node-topology.yang +++ b/netconf/netconf-console/src/test/resources/schemas/netconf-node-topology.yang @@ -15,16 +15,44 @@ module netconf-node-topology { } } - grouping netconf-node-credentials { + grouping username-password { + leaf username { + type string; + } + + leaf password { + type string; + } + } + grouping netconf-node-credentials { choice credentials { config true; - case login-password { - leaf username { - type string; + case login-password-deprecated { + description "Deprecated way of storing credentials, unencrypted."; + + status deprecated; + uses username-password; + } + case login-pw { + description "login-password credentials, encrypted."; + + + container login-password { + uses username-password; } + } + case login-pw-unencrypted { + description "login-password credentials, not encrypted."; + + container login-password-unencrypted { + uses username-password; + } + } + case key-based { + description "key-pair based authentication, use the id for the pair thats stored in the keystore."; - leaf password { + leaf pair-id { type string; } } @@ -143,6 +171,15 @@ module netconf-node-topology { description "Limit of concurrent messages that can be send before reply messages are received. If value <1 is provided, no limit will be enforced"; } + + leaf actor-response-wait-time { + config true; + type uint16 { + range "1..max"; + } + default 5; + description "Time that slave actor will wait for response from master."; + } } grouping netconf-node-connection-status { @@ -263,10 +300,20 @@ module netconf-node-topology { } + rpc add-netconf-node { + input { + uses netconf-node-fields; + leaf node-id { + type string; + } + } + } + augment "/nt:network-topology/nt:topology/nt:node" { when "../../nt:topology-types/topology-netconf"; ext:augment-identifier "netconf-node"; uses netconf-node-fields; } + } diff --git a/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/LoginPassword.java b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/LoginPasswordHandler.java similarity index 58% rename from netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/LoginPassword.java rename to netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/LoginPasswordHandler.java index 8281a79c04..84682226aa 100644 --- a/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/LoginPassword.java +++ b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/LoginPasswordHandler.java @@ -11,25 +11,18 @@ package org.opendaylight.netconf.nettyutil.handler.ssh.authentication; import java.io.IOException; import org.apache.sshd.ClientSession; import org.apache.sshd.client.future.AuthFuture; -import org.opendaylight.aaa.encrypt.AAAEncryptionService; /** * Class Providing username/password authentication option to * {@link org.opendaylight.netconf.nettyutil.handler.ssh.client.AsyncSshHandler}. */ -public class LoginPassword extends AuthenticationHandler { +public class LoginPasswordHandler extends AuthenticationHandler { protected final String username; protected final String password; - protected final AAAEncryptionService encryptionService; - public LoginPassword(String username, String password) { - this(username, password, null); - } - - public LoginPassword(final String username, final String password, final AAAEncryptionService encryptionService) { + public LoginPasswordHandler(final String username, final String password) { this.username = username; this.password = password; - this.encryptionService = encryptionService; } @Override @@ -39,12 +32,7 @@ public class LoginPassword extends AuthenticationHandler { @Override public AuthFuture authenticate(final ClientSession session) throws IOException { - if (encryptionService != null) { - String decryptedPassword = encryptionService.decrypt(password); - session.addPasswordIdentity(decryptedPassword); - } else { - session.addPasswordIdentity(password); - } + session.addPasswordIdentity(password); return session.auth(); } } diff --git a/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/PublicKeyAuth.java b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/PublicKeyAuth.java index 420a216bd6..70367c04ce 100644 --- a/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/PublicKeyAuth.java +++ b/netconf/netconf-netty-util/src/main/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/PublicKeyAuth.java @@ -12,7 +12,6 @@ import java.io.IOException; import java.security.KeyPair; import org.apache.sshd.ClientSession; import org.apache.sshd.client.future.AuthFuture; -import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.aaa.encrypt.PKIUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,13 +19,13 @@ import org.slf4j.LoggerFactory; /** * Represents Auth information for the public key based authentication for netconf. */ -public class PublicKeyAuth extends LoginPassword { +public class PublicKeyAuth extends LoginPasswordHandler { private KeyPair keyPair = null; private static final Logger LOG = LoggerFactory.getLogger(PublicKeyAuth.class); public PublicKeyAuth(String username, String password, String keyPath, - String passPhrase, AAAEncryptionService encryptionService) { - super(username, password, encryptionService); + String passPhrase) { + super(username, password); try { boolean isKeyPathAbsent = Strings.isNullOrEmpty(keyPath); passPhrase = Strings.isNullOrEmpty(passPhrase) ? "" : passPhrase; diff --git a/netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/LoginPasswordTest.java b/netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/LoginPasswordHandlerTest.java similarity index 80% rename from netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/LoginPasswordTest.java rename to netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/LoginPasswordHandlerTest.java index 9dce159124..905c0c03da 100644 --- a/netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/LoginPasswordTest.java +++ b/netconf/netconf-netty-util/src/test/java/org/opendaylight/netconf/nettyutil/handler/ssh/authentication/LoginPasswordHandlerTest.java @@ -18,17 +18,17 @@ import org.apache.sshd.ClientSession; import org.apache.sshd.client.future.AuthFuture; import org.junit.Test; -public class LoginPasswordTest { +public class LoginPasswordHandlerTest { @Test public void testLoginPassword() throws Exception { - final LoginPassword loginPassword = new LoginPassword("user", "pwd"); - assertEquals("user", loginPassword.getUsername()); + final LoginPasswordHandler loginPasswordHandler = new LoginPasswordHandler("user", "pwd"); + assertEquals("user", loginPasswordHandler.getUsername()); final ClientSession session = mock(ClientSession.class); doNothing().when(session).addPasswordIdentity("pwd"); doReturn(mock(AuthFuture.class)).when(session).auth(); - loginPassword.authenticate(session); + loginPasswordHandler.authenticate(session); verify(session).addPasswordIdentity("pwd"); verify(session).auth(); diff --git a/netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServer.java b/netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServer.java index 17af06e796..f525674436 100644 --- a/netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServer.java +++ b/netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServer.java @@ -70,6 +70,8 @@ public class SshProxyServer implements AutoCloseable { (username, password, session) -> sshProxyServerConfiguration.getAuthenticator().authenticated(username, password)); + sshProxyServerConfiguration.getPublickeyAuthenticator().ifPresent(sshServer::setPublickeyAuthenticator); + sshServer.setKeyPairProvider(sshProxyServerConfiguration.getKeyPairProvider()); sshServer.setIoServiceFactoryFactory(nioServiceWithPoolFactoryFactory); diff --git a/netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServerConfiguration.java b/netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServerConfiguration.java index 9fe57a40b6..75692ba575 100644 --- a/netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServerConfiguration.java +++ b/netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServerConfiguration.java @@ -11,7 +11,9 @@ package org.opendaylight.netconf.ssh; import com.google.common.base.Preconditions; import io.netty.channel.local.LocalAddress; import java.net.InetSocketAddress; +import java.util.Optional; import org.apache.sshd.common.KeyPairProvider; +import org.apache.sshd.server.PublickeyAuthenticator; import org.opendaylight.netconf.auth.AuthProvider; public final class SshProxyServerConfiguration { @@ -20,9 +22,16 @@ public final class SshProxyServerConfiguration { private final AuthProvider authenticator; private final KeyPairProvider keyPairProvider; private final int idleTimeout; + private final Optional publickeyAuthenticator; SshProxyServerConfiguration(final InetSocketAddress bindingAddress, final LocalAddress localAddress, final AuthProvider authenticator, final KeyPairProvider keyPairProvider, final int idleTimeout) { + this(bindingAddress, localAddress, authenticator, null, keyPairProvider, idleTimeout); + } + + SshProxyServerConfiguration(final InetSocketAddress bindingAddress, final LocalAddress localAddress, + final AuthProvider authenticator, final PublickeyAuthenticator publickeyAuthenticator, + final KeyPairProvider keyPairProvider, final int idleTimeout) { this.bindingAddress = Preconditions.checkNotNull(bindingAddress); this.localAddress = Preconditions.checkNotNull(localAddress); this.authenticator = Preconditions.checkNotNull(authenticator); @@ -30,6 +39,7 @@ public final class SshProxyServerConfiguration { // Idle timeout cannot be disabled in the sshd by using =< 0 value Preconditions.checkArgument(idleTimeout > 0, "Idle timeout has to be > 0"); this.idleTimeout = idleTimeout; + this.publickeyAuthenticator = Optional.ofNullable(publickeyAuthenticator); } public InetSocketAddress getBindingAddress() { @@ -52,5 +62,7 @@ public final class SshProxyServerConfiguration { return idleTimeout; } - + public Optional getPublickeyAuthenticator() { + return publickeyAuthenticator; + } } diff --git a/netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServerConfigurationBuilder.java b/netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServerConfigurationBuilder.java index 5b7948ed6f..6d9a364af9 100644 --- a/netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServerConfigurationBuilder.java +++ b/netconf/netconf-ssh/src/main/java/org/opendaylight/netconf/ssh/SshProxyServerConfigurationBuilder.java @@ -11,6 +11,7 @@ package org.opendaylight.netconf.ssh; import io.netty.channel.local.LocalAddress; import java.net.InetSocketAddress; import org.apache.sshd.common.KeyPairProvider; +import org.apache.sshd.server.PublickeyAuthenticator; import org.opendaylight.netconf.auth.AuthProvider; public final class SshProxyServerConfigurationBuilder { @@ -19,6 +20,7 @@ public final class SshProxyServerConfigurationBuilder { private AuthProvider authenticator; private KeyPairProvider keyPairProvider; private int idleTimeout; + private PublickeyAuthenticator publicKeyAuthenticator = null; public SshProxyServerConfigurationBuilder setBindingAddress(final InetSocketAddress bindingAddress) { this.bindingAddress = bindingAddress; @@ -35,6 +37,11 @@ public final class SshProxyServerConfigurationBuilder { return this; } + public SshProxyServerConfigurationBuilder setPublickeyAuthenticator(final PublickeyAuthenticator authenticator) { + this.publicKeyAuthenticator = authenticator; + return this; + } + public SshProxyServerConfigurationBuilder setKeyPairProvider(final KeyPairProvider keyPairProvider) { this.keyPairProvider = keyPairProvider; return this; @@ -46,7 +53,7 @@ public final class SshProxyServerConfigurationBuilder { } public SshProxyServerConfiguration createSshProxyServerConfiguration() { - return new SshProxyServerConfiguration(bindingAddress, localAddress, authenticator, + return new SshProxyServerConfiguration(bindingAddress, localAddress, authenticator, publicKeyAuthenticator, keyPairProvider, idleTimeout); } diff --git a/netconf/netconf-ssh/src/test/java/org/opendaylight/netconf/netty/SSHTest.java b/netconf/netconf-ssh/src/test/java/org/opendaylight/netconf/netty/SSHTest.java index d9a0756978..a2da0dafb7 100644 --- a/netconf/netconf-ssh/src/test/java/org/opendaylight/netconf/netty/SSHTest.java +++ b/netconf/netconf-ssh/src/test/java/org/opendaylight/netconf/netty/SSHTest.java @@ -31,7 +31,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.opendaylight.netconf.netty.EchoClientHandler.State; -import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword; +import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler; import org.opendaylight.netconf.nettyutil.handler.ssh.client.AsyncSshHandler; import org.opendaylight.netconf.ssh.SshProxyServer; import org.opendaylight.netconf.ssh.SshProxyServerConfigurationBuilder; @@ -109,7 +109,7 @@ public class SSHTest { final ChannelInitializer channelInitializer = new ChannelInitializer() { @Override public void initChannel(final NioSocketChannel ch) throws Exception { - ch.pipeline().addFirst(AsyncSshHandler.createForNetconfSubsystem(new LoginPassword("a", "a"))); + ch.pipeline().addFirst(AsyncSshHandler.createForNetconfSubsystem(new LoginPasswordHandler("a", "a"))); ch.pipeline().addLast(echoClientHandler); } }; diff --git a/netconf/netconf-topology-config/src/main/resources/org/opendaylight/blueprint/netconf-topology.xml b/netconf/netconf-topology-config/src/main/resources/org/opendaylight/blueprint/netconf-topology.xml index 72ca8da1af..477a90b247 100755 --- a/netconf/netconf-topology-config/src/main/resources/org/opendaylight/blueprint/netconf-topology.xml +++ b/netconf/netconf-topology-config/src/main/resources/org/opendaylight/blueprint/netconf-topology.xml @@ -73,4 +73,12 @@ + + + + + + + diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImpl.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImpl.java index 826342d23f..6ca3eb4a51 100644 --- a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImpl.java +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImpl.java @@ -35,18 +35,20 @@ import org.opendaylight.netconf.client.conf.NetconfClientConfiguration; import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration; import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder; import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; -import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.PublicKeyAuth; +import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler; import org.opendaylight.netconf.sal.connect.api.RemoteDevice; import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas; import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice; import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder; import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice; +import org.opendaylight.netconf.sal.connect.netconf.auth.DatastoreBackedPublicKeyAuth; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences; import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade; +import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter; import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider; import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; import org.opendaylight.netconf.topology.singleton.api.RemoteDeviceConnector; @@ -61,6 +63,13 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types. import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.KeyAuth; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordDeprecated; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.auth.KeyBased; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; @@ -84,6 +93,7 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { private final String privateKeyPassphrase; private final AAAEncryptionService encryptionService; private NetconfConnectorDTO deviceCommunicatorDTO; + private final NetconfKeystoreAdapter keystoreAdapter; public RemoteDeviceConnectorImpl(final NetconfTopologySetup netconfTopologyDeviceSetup, final RemoteDeviceId remoteDeviceId, final Timeout actorResponseWaitTime, @@ -96,6 +106,7 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { this.privateKeyPath = netconfTopologyDeviceSetup.getPrivateKeyPath(); this.privateKeyPassphrase = netconfTopologyDeviceSetup.getPrivateKeyPassphrase(); this.encryptionService = netconfTopologyDeviceSetup.getEncryptionService(); + keystoreAdapter = new NetconfKeystoreAdapter(netconfTopologyDeviceSetup.getDataBroker()); } @Override @@ -279,20 +290,7 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { betweenAttemptsTimeoutMillis, sleepFactor); final ReconnectStrategy strategy = sf.createReconnectStrategy(); - final AuthenticationHandler authHandler; - final Credentials credentials = node.getCredentials(); - if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf - .node.credentials.credentials.LoginPassword) { - authHandler = new PublicKeyAuth( - ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf - .node.credentials.credentials.LoginPassword) credentials).getUsername(), - ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf - .node.credentials.credentials.LoginPassword) credentials).getPassword(), - this.privateKeyPath, this.privateKeyPassphrase, encryptionService); - - } else { - throw new IllegalStateException(remoteDeviceId + ": Only login/password authentication is supported"); - } + final AuthenticationHandler authHandler = getHandlerFromCredentials(node.getCredentials()); return NetconfReconnectingClientConfigurationBuilder.create() .withAddress(socketAddress) @@ -307,6 +305,29 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { .build(); } + private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) { + if (credentials instanceof LoginPasswordDeprecated) { + final LoginPasswordDeprecated loginPassword = (LoginPasswordDeprecated) credentials; + return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword()); + } + if (credentials instanceof LoginPwUnencrypted) { + final LoginPasswordUnencrypted loginPassword = + ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted(); + return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword()); + } + if (credentials instanceof LoginPw) { + final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword(); + return new LoginPasswordHandler(loginPassword.getUsername(), + encryptionService.decrypt(loginPassword.getPassword())); + } + if (credentials instanceof KeyAuth) { + final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased(); + return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(), + keystoreAdapter, encryptionService); + } + throw new IllegalStateException("Unsupported credential type: " + credentials.getClass()); + } + private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory { private final Long connectionAttempts; private final EventExecutor executor; diff --git a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImplTest.java b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImplTest.java index 9745e87c2d..72de5a21c8 100644 --- a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImplTest.java +++ b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImplTest.java @@ -60,7 +60,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types. import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordDeprecatedBuilder; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder; @@ -134,7 +134,8 @@ public class RemoteDeviceConnectorImplTest { @Test public void testStopRemoteDeviceConnection() { - final Credentials credentials = new LoginPasswordBuilder().setPassword("admin").setUsername("admin").build(); + final Credentials credentials = new LoginPasswordDeprecatedBuilder() + .setPassword("admin").setUsername("admin").build(); final NetconfNode netconfNode = new NetconfNodeBuilder() .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1")))) .setPort(new PortNumber(9999)) @@ -173,7 +174,8 @@ public class RemoteDeviceConnectorImplTest { final ExecutorService executorService = mock(ExecutorService.class); doReturn(executorService).when(processingExecutor).getExecutor(); - final Credentials credentials = new LoginPasswordBuilder().setPassword("admin").setUsername("admin").build(); + final Credentials credentials = new LoginPasswordDeprecatedBuilder() + .setPassword("admin").setUsername("admin").build(); final NetconfNode netconfNode = new NetconfNodeBuilder() .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1")))) .setPort(new PortNumber(9999)) @@ -203,7 +205,8 @@ public class RemoteDeviceConnectorImplTest { final ExecutorService executorService = mock(ExecutorService.class); doReturn(executorService).when(processingExecutor).getExecutor(); - final Credentials credentials = new LoginPasswordBuilder().setPassword("admin").setUsername("admin").build(); + final Credentials credentials = new LoginPasswordDeprecatedBuilder() + .setPassword("admin").setUsername("admin").build(); final NetconfNode netconfNode = new NetconfNodeBuilder() .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1")))) .setPort(new PortNumber(9999)) @@ -242,7 +245,7 @@ public class RemoteDeviceConnectorImplTest { .setDefaultRequestTimeoutMillis(2000L) .setHost(host) .setPort(portNumber) - .setCredentials(new LoginPasswordBuilder() + .setCredentials(new LoginPasswordDeprecatedBuilder() .setUsername("testuser") .setPassword("testpassword").build()) .setTcpOnly(true) diff --git a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java index d430ce2f1c..511146c301 100644 --- a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java +++ b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java @@ -37,7 +37,7 @@ import org.opendaylight.netconf.client.conf.NetconfClientConfiguration; import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration; import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder; import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; -import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.PublicKeyAuth; +import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler; import org.opendaylight.netconf.sal.connect.api.RemoteDevice; import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas; @@ -45,11 +45,13 @@ import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice; import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder; import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl; import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice; +import org.opendaylight.netconf.sal.connect.netconf.auth.DatastoreBackedPublicKeyAuth; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences; import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade; +import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter; import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider; import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; import org.opendaylight.netconf.topology.api.NetconfTopology; @@ -62,6 +64,13 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types. import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.available.capabilities.AvailableCapability.CapabilityOrigin; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.KeyAuth; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordDeprecated; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.key.auth.KeyBased; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencrypted; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; @@ -162,6 +171,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { protected final SharedSchemaRepository sharedSchemaRepository; protected final DataBroker dataBroker; protected final DOMMountPointService mountPointService; + private final NetconfKeystoreAdapter keystoreAdapter; protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY; protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY; protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY; @@ -185,6 +195,8 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { this.dataBroker = dataBroker; this.mountPointService = mountPointService; this.encryptionService = encryptionService; + + this.keystoreAdapter = new NetconfKeystoreAdapter(dataBroker); } public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) { @@ -434,19 +446,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { maxConnectionAttempts, betweenAttemptsTimeoutMillis, sleepFactor); final ReconnectStrategy strategy = sf.createReconnectStrategy(); - final AuthenticationHandler authHandler; - final Credentials credentials = node.getCredentials(); - if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114 - .netconf.node.credentials.credentials.LoginPassword) { - authHandler = new PublicKeyAuth( - ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114 - .netconf.node.credentials.credentials.LoginPassword) credentials).getUsername(), - ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114 - .netconf.node.credentials.credentials.LoginPassword) credentials).getPassword(), - privateKeyPath, privateKeyPassphrase, encryptionService); - } else { - throw new IllegalStateException("Only login/password authentification is supported"); - } + final AuthenticationHandler authHandler = getHandlerFromCredentials(node.getCredentials()); return NetconfReconnectingClientConfigurationBuilder.create() .withAddress(socketAddress) @@ -460,6 +460,29 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { .build(); } + private AuthenticationHandler getHandlerFromCredentials(final Credentials credentials) { + if (credentials instanceof LoginPasswordDeprecated) { + final LoginPasswordDeprecated loginPassword = (LoginPasswordDeprecated) credentials; + return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword()); + } + if (credentials instanceof LoginPwUnencrypted) { + final LoginPasswordUnencrypted loginPassword = + ((LoginPwUnencrypted) credentials).getLoginPasswordUnencrypted(); + return new LoginPasswordHandler(loginPassword.getUsername(), loginPassword.getPassword()); + } + if (credentials instanceof LoginPw) { + final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword(); + return new LoginPasswordHandler(loginPassword.getUsername(), + encryptionService.decrypt(loginPassword.getPassword())); + } + if (credentials instanceof KeyAuth) { + final KeyBased keyPair = ((KeyAuth) credentials).getKeyBased(); + return new DatastoreBackedPublicKeyAuth(keyPair.getUsername(), keyPair.getKeyId(), + keystoreAdapter, encryptionService); + } + throw new IllegalStateException("Unsupported credential type: " + credentials.getClass()); + } + protected abstract RemoteDeviceHandler createSalFacade(RemoteDeviceId id); private InetSocketAddress getSocketAddress(final Host host, final int port) { diff --git a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/NetconfConnectorFactoryImpl.java b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/NetconfConnectorFactoryImpl.java index c644286504..7017d807bc 100644 --- a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/NetconfConnectorFactoryImpl.java +++ b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/NetconfConnectorFactoryImpl.java @@ -23,7 +23,8 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types. import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPasswordBuilder; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId; @@ -58,9 +59,12 @@ public class NetconfConnectorFactoryImpl implements NetconfConnectorFactory { final NodeId nodeId = new NodeId(instanceName); final NodeKey nodeKey = new NodeKey(nodeId); - final Credentials credentials = new LoginPasswordBuilder() - .setUsername(username) - .setPassword(password) + final Credentials credentials = new LoginPwBuilder() + .setLoginPassword( + new LoginPasswordBuilder() + .setUsername(username) + .setPassword(password) + .build()) .build(); final Host host = HostBuilder.getDefaultInstance(address); final PortNumber portNumber = new PortNumber(port); diff --git a/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImplTest.java b/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImplTest.java index fa4ac32e15..c92fb9a456 100644 --- a/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImplTest.java +++ b/netconf/netconf-topology/src/test/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImplTest.java @@ -49,7 +49,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types. import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordDeprecatedBuilder; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopologyBuilder; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; @@ -161,7 +161,8 @@ public class NetconfTopologyImplTest { .setBetweenAttemptsTimeoutMillis(100) .setKeepaliveDelay(1000L) .setTcpOnly(true) - .setCredentials(new LoginPasswordBuilder().setUsername("testuser").setPassword("testpassword").build()) + .setCredentials(new LoginPasswordDeprecatedBuilder() + .setUsername("testuser").setPassword("testpassword").build()) .build(); final NodeBuilder nn = new NodeBuilder().addAugmentation(NetconfNode.class, testingNode); diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/auth/DatastoreBackedPublicKeyAuth.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/auth/DatastoreBackedPublicKeyAuth.java new file mode 100644 index 0000000000..7c750cc67f --- /dev/null +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/auth/DatastoreBackedPublicKeyAuth.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.sal.connect.netconf.auth; + +import com.google.common.base.Strings; +import java.io.IOException; +import java.io.StringReader; +import java.security.KeyPair; +import java.util.Optional; +import org.apache.sshd.ClientSession; +import org.apache.sshd.client.future.AuthFuture; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; +import org.opendaylight.aaa.encrypt.PKIUtil; +import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; +import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfKeystoreAdapter; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.keystore.entry.KeyCredential; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DatastoreBackedPublicKeyAuth extends AuthenticationHandler { + + private static final Logger LOG = LoggerFactory.getLogger(DatastoreBackedPublicKeyAuth.class); + + private final String username; + private final String pairId; + private final NetconfKeystoreAdapter keystoreAdapter; + private final AAAEncryptionService encryptionService; + + private Optional keyPair = Optional.empty(); + + public DatastoreBackedPublicKeyAuth(final String username, final String pairId, + final NetconfKeystoreAdapter keystoreAdapter, + final AAAEncryptionService encryptionService) { + this.username = username; + this.pairId = pairId; + this.keystoreAdapter = keystoreAdapter; + this.encryptionService = encryptionService; + + // try to immediately retrieve the pair from the adapter + tryToSetKeyPair(); + } + + @Override + public String getUsername() { + return username; + } + + @Override + public AuthFuture authenticate(ClientSession session) throws IOException { + // if we have keypair set the identity, otherwise retry the retrieval from the adapter + // if successful set the identity. + if (keyPair.isPresent() || tryToSetKeyPair()) { + session.addPublicKeyIdentity(keyPair.get()); + } + return session.auth(); + } + + private boolean tryToSetKeyPair() { + LOG.debug("Trying to retrieve keypair for: {}", pairId); + final Optional keypairOptional = keystoreAdapter.getKeypairFromId(pairId); + + if (keypairOptional.isPresent()) { + final KeyCredential dsKeypair = keypairOptional.get(); + final String passPhrase = Strings.isNullOrEmpty(dsKeypair.getPassphrase()) ? "" : dsKeypair.getPassphrase(); + + try { + this.keyPair = Optional.of( + new PKIUtil().decodePrivateKey( + new StringReader(encryptionService.decrypt(dsKeypair.getPrivateKey())), + encryptionService.decrypt(passPhrase))); + } catch (IOException exception) { + LOG.warn("Unable to decode private key, id={}", pairId, exception); + return false; + } + return true; + } + LOG.debug("Unable to retrieve keypair for: {}", pairId); + return false; + } +} diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfKeystoreAdapter.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfKeystoreAdapter.java new file mode 100644 index 0000000000..8363c54d75 --- /dev/null +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfKeystoreAdapter.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.sal.connect.netconf.sal; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; +import org.opendaylight.controller.md.sal.binding.api.DataTreeModification; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.Keystore; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.keystore.entry.KeyCredential; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NetconfKeystoreAdapter implements ClusteredDataTreeChangeListener { + + private static final Logger LOG = LoggerFactory.getLogger(NetconfKeystoreAdapter.class); + + private final InstanceIdentifier keystoreIid = InstanceIdentifier.create(Keystore.class); + + private final DataBroker dataBroker; + private final Map pairs = Collections.synchronizedMap(new HashMap<>()); + + public NetconfKeystoreAdapter(final DataBroker dataBroker) { + this.dataBroker = dataBroker; + + dataBroker.registerDataTreeChangeListener( + new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, keystoreIid), this); + } + + public Optional getKeypairFromId(final String keyId) { + final KeyCredential keypair = pairs.get(keyId); + return Optional.ofNullable(keypair); + } + + @Override + public void onDataTreeChanged(@Nonnull final Collection> changes) { + LOG.debug("Keystore updated: {}", changes); + final Keystore dataAfter = changes.iterator().next().getRootNode().getDataAfter(); + + pairs.clear(); + if (dataAfter != null) { + dataAfter.getKeyCredential().forEach(pair -> pairs.put(pair.getKey().getKeyId(), pair)); + } + } +} diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/util/NetconfSalKeystoreService.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/util/NetconfSalKeystoreService.java new file mode 100644 index 0000000000..7cb9216a57 --- /dev/null +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/util/NetconfSalKeystoreService.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.sal.connect.util; + +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.SettableFuture; +import java.util.List; +import java.util.concurrent.Future; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.AddKeystoreEntryInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.Keystore; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.KeystoreBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.NetconfKeystoreService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.RemoveKeystoreEntryInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.keystore.entry.KeyCredential; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.keystore.entry.KeyCredentialBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.keystore.rev171017.keystore.entry.KeyCredentialKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NetconfSalKeystoreService implements NetconfKeystoreService { + + private static final Logger LOG = LoggerFactory.getLogger(NetconfSalKeystoreService.class); + + private final DataBroker dataBroker; + private final AAAEncryptionService encryptionService; + + private final InstanceIdentifier keystoreIid = InstanceIdentifier.create(Keystore.class); + + public NetconfSalKeystoreService(final DataBroker dataBroker, + final AAAEncryptionService encryptionService) { + LOG.info("Starting NETCONF keystore service."); + + this.dataBroker = dataBroker; + this.encryptionService = encryptionService; + + initKeystore(); + } + + private void initKeystore() { + final Keystore keystore = new KeystoreBuilder().build(); + + final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction(); + writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, keystoreIid, keystore); + + final CheckedFuture submit = writeTransaction.submit(); + + try { + submit.checkedGet(); + LOG.debug("init keystore done"); + } catch (TransactionCommitFailedException exception) { + LOG.error("Unable to initialize Netconf key-pair store.", exception); + } + } + + @Override + public Future> removeKeystoreEntry(final RemoveKeystoreEntryInput input) { + LOG.debug("Removing keypairs: {}", input); + + final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction(); + final List ids = input.getKeyId(); + + for (final String id : ids) { + writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, + keystoreIid.child(KeyCredential.class, new KeyCredentialKey(id))); + } + + final SettableFuture> rpcResult = SettableFuture.create(); + + final CheckedFuture submit = writeTransaction.submit(); + Futures.addCallback(submit, new FutureCallback() { + @Override + public void onSuccess(@Nullable final Void result) { + LOG.debug("remove-key-pair success. Input: {}"); + final RpcResult success = RpcResultBuilder.success().build(); + rpcResult.set(success); + } + + @Override + public void onFailure(final Throwable throwable) { + LOG.warn("remove-key-pair failed. Input: {}", input, throwable); + rpcResult.setException(throwable); + } + }, MoreExecutors.directExecutor()); + + return rpcResult; + } + + @Override + public Future> addKeystoreEntry(final AddKeystoreEntryInput input) { + LOG.debug("Adding keypairs: {}", input); + + final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction(); + final List keypairs = input.getKeyCredential().stream().map(keypair -> + new KeyCredentialBuilder(keypair) + .setPrivateKey(encryptionService.encrypt(keypair.getPrivateKey())) + .setPassphrase(encryptionService.encrypt(keypair.getPassphrase())) + .build()).collect(Collectors.toList()); + + for (KeyCredential keypair : keypairs) { + writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, + keystoreIid.child(KeyCredential.class, keypair.getKey()), keypair); + } + + final SettableFuture> rpcResult = SettableFuture.create(); + + final CheckedFuture submit = writeTransaction.submit(); + Futures.addCallback(submit, new FutureCallback() { + @Override + public void onSuccess(@Nullable final Void result) { + LOG.debug("add-key-pair success. Input: {}"); + final RpcResult success = RpcResultBuilder.success().build(); + rpcResult.set(success); + } + + @Override + public void onFailure(final Throwable throwable) { + LOG.warn("add-key-pair failed. Input: {}", input, throwable); + rpcResult.setException(throwable); + } + }, MoreExecutors.directExecutor()); + + return rpcResult; + } +} diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/util/NetconfTopologyRPCProvider.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/util/NetconfTopologyRPCProvider.java index 4edbedf6c2..c963022e06 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/util/NetconfTopologyRPCProvider.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/util/NetconfTopologyRPCProvider.java @@ -7,10 +7,11 @@ */ package org.opendaylight.netconf.sal.connect.util; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.CheckedFuture; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.SettableFuture; import java.util.concurrent.Future; @@ -18,13 +19,16 @@ import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.AddNetconfNodeInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.CreateDeviceInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.DeleteDeviceInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeTopologyService; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPassword; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPassword; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPasswordBuilder; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId; @@ -53,56 +57,99 @@ public class NetconfTopologyRPCProvider implements NetconfNodeTopologyService { } @Override - public Future> addNetconfNode(AddNetconfNodeInput input) { - NetconfNode node = this.encryptPassword(input); + public Future> createDevice(final CreateDeviceInput input) { + final NetconfNode node = this.encryptPassword(input); final SettableFuture> futureResult = SettableFuture.create(); - NodeId nodeId = new NodeId(input.getNodeId()); + final NodeId nodeId = new NodeId(input.getNodeId()); writeToConfigDS(node, nodeId, topologyId, futureResult); return futureResult; } - private NetconfNode encryptPassword(AddNetconfNodeInput input) { - NetconfNodeBuilder builder = new NetconfNodeBuilder(); + @VisibleForTesting + public NetconfNode encryptPassword(final CreateDeviceInput input) { + final NetconfNodeBuilder builder = new NetconfNodeBuilder(); builder.fieldsFrom(input); - boolean encrypt = input.isEncrypt(); - LoginPassword loginPassword = (LoginPassword) input.getCredentials(); - if (encrypt) { - String encryptedPassword = encryptionService.encrypt(loginPassword.getPassword()); - LoginPassword newCreds = new LoginPasswordBuilder().setPassword(encryptedPassword) - .setUsername(loginPassword.getUsername()).build(); - builder.setCredentials(newCreds); + final Credentials credentials = handleEncryption(input.getCredentials()); + builder.setCredentials(credentials); + + return builder.build(); + } + + private Credentials handleEncryption(final Credentials credentials) { + if (credentials instanceof LoginPw) { + final LoginPassword loginPassword = ((LoginPw) credentials).getLoginPassword(); + final String encryptedPassword = + encryptionService.encrypt(loginPassword.getPassword()); + + return new LoginPwBuilder().setLoginPassword(new LoginPasswordBuilder() + .setPassword(encryptedPassword) + .setUsername(loginPassword.getUsername()).build()).build(); } - NetconfNode node = builder.build(); - return node; + // nothing else needs to be encrypted + return credentials; } - private void writeToConfigDS(NetconfNode node, NodeId nodeId, String topologyId, + private void writeToConfigDS(final NetconfNode node, final NodeId nodeId, final String topologyId, final SettableFuture> futureResult) { - WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction(); + final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction(); final InstanceIdentifier networkTopologyId = InstanceIdentifier.builder(NetworkTopology.class).build(); final InstanceIdentifier niid = networkTopologyId.child(Topology.class, new TopologyKey(new TopologyId(topologyId))).child(Node.class, new NodeKey(nodeId)).augmentation(NetconfNode.class); writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, niid, node, true); - final CheckedFuture future = writeTransaction.submit(); + final ListenableFuture future = writeTransaction.submit(); Futures.addCallback(future, new FutureCallback() { @Override - public void onSuccess(Void result) { + public void onSuccess(final Void result) { LOG.info("add-netconf-node RPC: Added netconf node successfully."); futureResult.set(RpcResultBuilder.success().build()); } @Override - public void onFailure(Throwable exception) { + public void onFailure(final Throwable exception) { LOG.error("add-netconf-node RPC: Unable to add netconf node.", exception); futureResult.setException(exception); } }, MoreExecutors.directExecutor()); } + + @Override + public Future> deleteDevice(final DeleteDeviceInput input) { + final NodeId nodeId = new NodeId(input.getNodeId()); + + final InstanceIdentifier networkTopologyId = + InstanceIdentifier.builder(NetworkTopology.class).build(); + final InstanceIdentifier niid = networkTopologyId.child(Topology.class, + new TopologyKey(new TopologyId(topologyId))).child(Node.class, + new NodeKey(nodeId)); + + final WriteTransaction wtx = dataBroker.newWriteOnlyTransaction(); + wtx.delete(LogicalDatastoreType.CONFIGURATION, niid); + + final ListenableFuture future = wtx.submit(); + final SettableFuture> rpcFuture = SettableFuture.create(); + + Futures.addCallback(future, new FutureCallback() { + + @Override + public void onSuccess(final Void result) { + LOG.info("delete-device RPC: Removed netconf node successfully."); + rpcFuture.set(RpcResultBuilder.success().build()); + } + + @Override + public void onFailure(final Throwable exception) { + LOG.error("delete-device RPC: Unable to remove netconf node.", exception); + rpcFuture.setException(exception); + } + }, MoreExecutors.directExecutor()); + + return rpcFuture; + } } diff --git a/netconf/sal-netconf-connector/src/main/yang/netconf-keystore.yang b/netconf/sal-netconf-connector/src/main/yang/netconf-keystore.yang new file mode 100644 index 0000000000..d3da9f4306 --- /dev/null +++ b/netconf/sal-netconf-connector/src/main/yang/netconf-keystore.yang @@ -0,0 +1,60 @@ +module netconf-keystore { + namespace "urn:opendaylight:netconf:keystore"; + prefix "keystore"; + + revision "2017-10-17" { + description "Initial revision of the Netconf SBP keystore."; + } + + description "Store used for key based Credentials for Netconf SBP. Before a connector with key based authentication + is created it needs to have a record for the key pair it uses. All the records here need to be + encrypted as they contain sensitive data. Therefore NEVER do direct writes and only use the provided + RPC's for adding/removing key entries."; + + grouping keystore-entry { + list key-credential { + key key-id; + + leaf key-id { + type string; + } + + leaf private-key { + description "Base64 encoded private key that should be used for authentication with a netconf device. + Do not include a public key as that is calculated from the private key. + DO NOT write this directly into the datastore, use the provided rpc's as these will + encrypt the key before the entry is written into the datastore."; + type string; + } + + leaf passphrase { + description "If the provided key is encrypted by a passphrase this needs to be included. Leave empty + if the key does not have a passphrase. + DO NOT write write this directly into the datastore, use the provided rpc's as these will + encrypt the passhprase before the entry is written into the datastore."; + type string; + } + } + } + + container keystore { + uses keystore-entry; + } + + rpc add-keystore-entry { + description "Use this rpc to add a single or multiple new keys into the keystore. The private key + and passphrase will both be encrypted before they are written into the datastore."; + input { + uses keystore-entry; + } + } + + rpc remove-keystore-entry { + description "Use this rpc to remove a single or multiple keys from the datastore."; + input { + leaf-list key-id { + type string; + } + } + } +} \ No newline at end of file diff --git a/netconf/sal-netconf-connector/src/main/yang/netconf-node-topology.yang b/netconf/sal-netconf-connector/src/main/yang/netconf-node-topology.yang index 7839d508d2..91bda040f9 100644 --- a/netconf/sal-netconf-connector/src/main/yang/netconf-node-topology.yang +++ b/netconf/sal-netconf-connector/src/main/yang/netconf-node-topology.yang @@ -15,17 +15,51 @@ module netconf-node-topology { } } - grouping netconf-node-credentials { + grouping username-password { + leaf username { + type string; + } + + leaf password { + type string; + } + } + grouping netconf-node-credentials { choice credentials { config true; - case login-password { - leaf username { - type string; + case login-password-deprecated { + description "Deprecated way of storing credentials, unencrypted."; + + status deprecated; + uses username-password; + } + case login-pw { + description "login-password credentials, encrypted."; + + + container login-password { + uses username-password; } + } + case login-pw-unencrypted { + description "login-password credentials, not encrypted."; - leaf password { - type string; + container login-password-unencrypted { + uses username-password; + } + } + case key-auth { + description "key-based authentication, use the id for the pair thats stored in the keystore."; + + container key-based { + leaf key-id { + type string; + } + + leaf username { + type string; + } } } } @@ -272,15 +306,19 @@ module netconf-node-topology { } - rpc add-netconf-node { + rpc create-device { input { uses netconf-node-fields; leaf node-id { type string; } - leaf encrypt { - type boolean; - default false; + } + } + + rpc delete-device { + input { + leaf node-id { + type string; } } } diff --git a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java index da24dd0e51..f9724cf74e 100644 --- a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java +++ b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java @@ -58,7 +58,7 @@ import org.opendaylight.netconf.client.NetconfClientSession; import org.opendaylight.netconf.client.conf.NetconfClientConfiguration; import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration; import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder; -import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword; +import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler; import org.opendaylight.netconf.sal.connect.api.RemoteDevice; import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil; import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; @@ -397,7 +397,7 @@ public class NetconfDeviceCommunicatorTest { .withAddress(new InetSocketAddress("localhost", 65000)) .withReconnectStrategy(reconnectStrategy) .withConnectStrategyFactory(() -> reconnectStrategy) - .withAuthHandler(new LoginPassword("admin", "admin")) + .withAuthHandler(new LoginPasswordHandler("admin", "admin")) .withConnectionTimeoutMillis(10000) .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH) .withSessionListener(listener) diff --git a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/util/NetconfTopologyRPCProviderTest.java b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/util/NetconfTopologyRPCProviderTest.java index 5ece53f831..72ef9007d1 100644 --- a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/util/NetconfTopologyRPCProviderTest.java +++ b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/util/NetconfTopologyRPCProviderTest.java @@ -8,10 +8,9 @@ package org.opendaylight.netconf.sal.connect.netconf.util; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.when; -import java.lang.reflect.Method; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -23,11 +22,16 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types. import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.AddNetconfNodeInput; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.AddNetconfNodeInputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.CreateDeviceInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.CreateDeviceInputBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPassword; -import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.Credentials; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPw; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencrypted; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPwUnencryptedBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.LoginPasswordBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.login.pw.unencrypted.LoginPasswordUnencryptedBuilder; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; public class NetconfTopologyRPCProviderTest { @@ -42,7 +46,7 @@ public class NetconfTopologyRPCProviderTest { @Mock private AAAEncryptionService encryptionService; - NetconfTopologyRPCProvider rpcProvider ; + private NetconfTopologyRPCProvider rpcProvider ; @Before public void setUp() { @@ -54,34 +58,42 @@ public class NetconfTopologyRPCProviderTest { @Test public void testEncryptPassword() throws Exception { - NetconfNode node = invokeEncryption(true); - assertNotEquals(TEST_PWD, ((LoginPassword)node.getCredentials()).getPassword()); + final NetconfNode encryptedPwNode = rpcProvider.encryptPassword(getInput(true)); - node = invokeEncryption(false); - assertEquals(TEST_PWD, ((LoginPassword)node.getCredentials()).getPassword()); - } - - private NetconfNode invokeEncryption(boolean encrypt) throws Exception { - Method method = null; + final Credentials credentials = encryptedPwNode.getCredentials(); + assertTrue(credentials instanceof LoginPw); + final LoginPw loginPw = (LoginPw) credentials; - method = NetconfTopologyRPCProvider.class.getDeclaredMethod("encryptPassword", AddNetconfNodeInput.class); + assertEquals(ENC_PWD, loginPw.getLoginPassword().getPassword()); + } - method.setAccessible(true); - NetconfNode node = null; + @Test + public void testNoEncryption() throws Exception { + final NetconfNode encryptedPwNode = rpcProvider.encryptPassword(getInput(false)); - node = (NetconfNode)method.invoke(rpcProvider, getInput(encrypt)); + final Credentials credentials = encryptedPwNode.getCredentials(); + assertTrue(credentials instanceof LoginPwUnencrypted); + final LoginPwUnencrypted loginPw = (LoginPwUnencrypted) credentials; - return node; + assertEquals(TEST_PWD, loginPw.getLoginPasswordUnencrypted().getPassword()); } - private AddNetconfNodeInput getInput(boolean encrypt) { - AddNetconfNodeInputBuilder builder = new AddNetconfNodeInputBuilder(); - builder.setCredentials(new LoginPasswordBuilder().setPassword(TEST_PWD).setUsername("test").build()); + private CreateDeviceInput getInput(boolean encrypt) { + CreateDeviceInputBuilder builder = new CreateDeviceInputBuilder(); + final Credentials credentials; + if (encrypt) { + credentials = new LoginPwBuilder().setLoginPassword( + new LoginPasswordBuilder().setUsername("test").setPassword(TEST_PWD).build()).build(); + } else { + credentials = new LoginPwUnencryptedBuilder().setLoginPasswordUnencrypted( + new LoginPasswordUnencryptedBuilder().setUsername("test").setPassword(TEST_PWD).build()).build(); + } + + builder.setCredentials(credentials); builder.setHost(new Host(new IpAddress(new Ipv4Address("10.18.16.188")))); builder.setPort(new PortNumber(830)); builder.setTcpOnly(false); builder.setNodeId(NODE_ID.toString()); - builder.setEncrypt(encrypt); return builder.build(); } diff --git a/netconf/tools/netconf-cli/src/main/java/org/opendaylight/netconf/cli/Main.java b/netconf/tools/netconf-cli/src/main/java/org/opendaylight/netconf/cli/Main.java index 97b045abe7..b852544a5a 100644 --- a/netconf/tools/netconf-cli/src/main/java/org/opendaylight/netconf/cli/Main.java +++ b/netconf/tools/netconf-cli/src/main/java/org/opendaylight/netconf/cli/Main.java @@ -25,7 +25,7 @@ import org.opendaylight.netconf.cli.io.ConsoleIO; import org.opendaylight.netconf.cli.io.ConsoleIOImpl; import org.opendaylight.netconf.client.conf.NetconfClientConfiguration; import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder; -import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword; +import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler; import org.opendaylight.yangtools.yang.model.api.SchemaContext; /** @@ -214,8 +214,8 @@ public class Main { return parsed.getString(key); } - public LoginPassword getCredentials() { - return new LoginPassword(getUsername(), getPassword()); + public LoginPasswordHandler getCredentials() { + return new LoginPasswordHandler(getUsername(), getPassword()); } public String getPassword() { diff --git a/netconf/tools/netconf-cli/src/main/java/org/opendaylight/netconf/cli/commands/local/Connect.java b/netconf/tools/netconf-cli/src/main/java/org/opendaylight/netconf/cli/commands/local/Connect.java index 3bffa568a4..ac8b7f451c 100644 --- a/netconf/tools/netconf-cli/src/main/java/org/opendaylight/netconf/cli/commands/local/Connect.java +++ b/netconf/tools/netconf-cli/src/main/java/org/opendaylight/netconf/cli/commands/local/Connect.java @@ -25,7 +25,7 @@ import org.opendaylight.netconf.cli.commands.output.Output; import org.opendaylight.netconf.cli.commands.output.OutputDefinition; import org.opendaylight.netconf.client.conf.NetconfClientConfiguration; import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder; -import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword; +import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler; import org.opendaylight.protocol.framework.NeverReconnectStrategy; import org.opendaylight.protocol.framework.ReconnectStrategy; import org.opendaylight.yangtools.yang.common.QName; @@ -102,7 +102,7 @@ public class Connect extends AbstractCommand { return NetconfClientConfigurationBuilder.create().withAddress(inetAddress) .withConnectionTimeoutMillis(connectionTimeout) .withReconnectStrategy(strategy) - .withAuthHandler(new LoginPassword(username, passwd)) + .withAuthHandler(new LoginPasswordHandler(username, passwd)) .withProtocol(NetconfClientConfiguration.NetconfClientProtocol.SSH); } diff --git a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/NetconfDeviceSimulator.java b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/NetconfDeviceSimulator.java index ab45f2d737..bbf8af73db 100644 --- a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/NetconfDeviceSimulator.java +++ b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/NetconfDeviceSimulator.java @@ -273,6 +273,10 @@ public class NetconfDeviceSimulator implements Closeable { .setBindingAddress(bindingAddress) .setLocalAddress(tcpLocalAddress) .setAuthenticator((username, password) -> true) + .setPublickeyAuthenticator(((username, key, session) -> { + LOG.info("Auth with public key: {}", key); + return true; + })) .setKeyPairProvider(keyPairProvider) .setIdleTimeout(Integer.MAX_VALUE) .createSshProxyServerConfiguration(); diff --git a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/stress/StressClientCallable.java b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/stress/StressClientCallable.java index edaa188032..db15bb2a02 100644 --- a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/stress/StressClientCallable.java +++ b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/client/stress/StressClientCallable.java @@ -19,7 +19,7 @@ import org.opendaylight.netconf.client.NetconfClientDispatcherImpl; import org.opendaylight.netconf.client.NetconfClientSession; import org.opendaylight.netconf.client.conf.NetconfClientConfiguration; import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder; -import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword; +import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler; import org.opendaylight.netconf.sal.connect.api.RemoteDevice; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; @@ -101,7 +101,7 @@ public class StressClientCallable implements Callable { } netconfClientConfigurationBuilder.withProtocol(params.ssh ? NetconfClientConfiguration.NetconfClientProtocol.SSH : NetconfClientConfiguration.NetconfClientProtocol.TCP); - netconfClientConfigurationBuilder.withAuthHandler(new LoginPassword(params.username, params.password)); + netconfClientConfigurationBuilder.withAuthHandler(new LoginPasswordHandler(params.username, params.password)); netconfClientConfigurationBuilder.withConnectionTimeoutMillis(20000L); netconfClientConfigurationBuilder.withReconnectStrategy( new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000)); diff --git a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/rpchandler/RpcHandlerDefault.java b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/rpchandler/RpcHandlerDefault.java index 5a84ea6acb..f817575d26 100644 --- a/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/rpchandler/RpcHandlerDefault.java +++ b/netconf/tools/netconf-testtool/src/main/java/org/opendaylight/netconf/test/tool/rpchandler/RpcHandlerDefault.java @@ -20,7 +20,7 @@ public class RpcHandlerDefault implements RpcHandler { @Override public Optional getResponse(XmlElement rpcElement) { LOG.info("getResponse: {}", rpcElement.toString()); - return null; + return Optional.empty(); } }