From 7901b519a09351061e5a1dd061f7f7c0cc738fb8 Mon Sep 17 00:00:00 2001 From: Colin Dixon Date: Tue, 27 Jun 2017 10:09:28 -0400 Subject: [PATCH] Make netconf utilize encrypted passwords only Change-Id: I5c178c4e3865e374f39e326ceb9c4e24be269590 Signed-off-by: Ryan Goulding Signed-off-by: Atul Gosain Signed-off-by: Colin Dixon --- .../odl-netconf-connector/pom.xml | 10 ++- features/netconf/features-netconf/pom.xml | 14 +++- .../netconf/odl-netconf-netty-util/pom.xml | 9 ++- .../callhome/mount/BaseCallHomeTopology.java | 6 +- .../mount/CallHomeMountDispatcher.java | 24 +++--- .../callhome/mount/CallHomeTopology.java | 14 ++-- .../blueprint/callhome-topology.xml | 4 + .../mount/CallHomeMountDispatcherTest.java | 5 +- netconf/netconf-netty-util/pom.xml | 5 ++ .../ssh/authentication/LoginPassword.java | 19 ++++- .../blueprint/netconf-topology.xml | 6 +- .../impl/NetconfTopologyManager.java | 11 ++- .../impl/RemoteDeviceConnectorImpl.java | 13 ++- .../impl/utils/NetconfTopologySetup.java | 17 ++++ .../blueprint/netconf-topology-singleton.xml | 4 + .../impl/NetconfTopologyManagerTest.java | 5 +- .../impl/RemoteDeviceConnectorImplTest.java | 7 +- .../topology/AbstractNetconfTopology.java | 13 ++- .../topology/impl/NetconfTopologyImpl.java | 6 +- .../impl/NetconfTopologyImplTest.java | 11 ++- .../sal/connect/util/AuthEncryptor.java | 80 +++++++++++++++++++ 21 files changed, 243 insertions(+), 40 deletions(-) create mode 100644 netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/util/AuthEncryptor.java diff --git a/features/netconf-connector/odl-netconf-connector/pom.xml b/features/netconf-connector/odl-netconf-connector/pom.xml index 7d82fb9c7d..dfaa6a7eee 100644 --- a/features/netconf-connector/odl-netconf-connector/pom.xml +++ b/features/netconf-connector/odl-netconf-connector/pom.xml @@ -87,5 +87,13 @@ org.opendaylight.netconf netconf-config + + org.opendaylight.aaa + odl-aaa-encryption-service + 0.6.0-SNAPSHOT + xml + features + + - \ No newline at end of file + diff --git a/features/netconf/features-netconf/pom.xml b/features/netconf/features-netconf/pom.xml index 44d39ec27b..9b88553261 100644 --- a/features/netconf/features-netconf/pom.xml +++ b/features/netconf/features-netconf/pom.xml @@ -136,5 +136,17 @@ xml features + + org.opendaylight.aaa + aaa-encrypt-service + 0.6.0-SNAPSHOT + + + org.opendaylight.aaa + aaa-encrypt-service + 0.6.0-SNAPSHOT + config + xml + - \ No newline at end of file + diff --git a/features/netconf/odl-netconf-netty-util/pom.xml b/features/netconf/odl-netconf-netty-util/pom.xml index a615f2f0dd..9d537c73b7 100644 --- a/features/netconf/odl-netconf-netty-util/pom.xml +++ b/features/netconf/odl-netconf-netty-util/pom.xml @@ -75,5 +75,12 @@ org.bouncycastle bcprov-jdk15on + + org.opendaylight.aaa + aaa-encrypt-service + 0.6.0-SNAPSHOT + config + xml + - \ No newline at end of file + diff --git a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/BaseCallHomeTopology.java b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/BaseCallHomeTopology.java index f2d01b753e..f663445cc8 100644 --- a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/BaseCallHomeTopology.java +++ b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/BaseCallHomeTopology.java @@ -9,6 +9,7 @@ package org.opendaylight.netconf.callhome.mount; import io.netty.util.concurrent.EventExecutor; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; import org.opendaylight.controller.config.threadpool.ThreadPool; import org.opendaylight.controller.md.sal.binding.api.DataBroker; @@ -27,9 +28,10 @@ abstract class BaseCallHomeTopology extends AbstractNetconfTopology { final ThreadPool processingExecutor, final SchemaRepositoryProvider schemaRepositoryProvider, final DataBroker dataBroker, - final DOMMountPointService mountPointService) { + final DOMMountPointService mountPointService, + final AAAEncryptionService encryptionService) { super(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor, - processingExecutor, schemaRepositoryProvider, dataBroker, mountPointService); + processingExecutor, schemaRepositoryProvider, dataBroker, mountPointService, encryptionService); this.mountPointService = mountPointService; } } diff --git a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcher.java b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcher.java index dab26d92c2..b8c2ba8090 100644 --- a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcher.java +++ b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcher.java @@ -12,6 +12,7 @@ import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.FailedFuture; import io.netty.util.concurrent.Future; import java.net.InetSocketAddress; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; import org.opendaylight.controller.config.threadpool.ThreadPool; import org.opendaylight.controller.md.sal.binding.api.DataBroker; @@ -42,6 +43,7 @@ public class CallHomeMountDispatcher implements NetconfClientDispatcher, CallHom private final CallHomeMountSessionManager sessionManager; private final DataBroker dataBroker; private final DOMMountPointService mountService; + private final AAAEncryptionService encryptionService; protected CallHomeTopology topology; @@ -53,13 +55,10 @@ public class CallHomeMountDispatcher implements NetconfClientDispatcher, CallHom } }; - public CallHomeMountDispatcher(final String topologyId, - final EventExecutor eventExecutor, - final ScheduledThreadPool keepaliveExecutor, - final ThreadPool processingExecutor, - final SchemaRepositoryProvider schemaRepositoryProvider, - final DataBroker dataBroker, - final DOMMountPointService mountService) { + public CallHomeMountDispatcher(final String topologyId, final EventExecutor eventExecutor, + final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor, + final SchemaRepositoryProvider schemaRepositoryProvider, final DataBroker dataBroker, + final DOMMountPointService mountService, final AAAEncryptionService encryptionService) { this.topologyId = topologyId; this.eventExecutor = eventExecutor; this.keepaliveExecutor = keepaliveExecutor; @@ -68,6 +67,7 @@ public class CallHomeMountDispatcher implements NetconfClientDispatcher, CallHom this.sessionManager = new CallHomeMountSessionManager(); this.dataBroker = dataBroker; this.mountService = mountService; + this.encryptionService = encryptionService; } @Override @@ -91,15 +91,15 @@ public class CallHomeMountDispatcher implements NetconfClientDispatcher, CallHom } void createTopology() { - this.topology = new CallHomeTopology(topologyId, this, eventExecutor, - keepaliveExecutor, processingExecutor, schemaRepositoryProvider, dataBroker, mountService); + this.topology = new CallHomeTopology(topologyId, this, eventExecutor, keepaliveExecutor, processingExecutor, + schemaRepositoryProvider, dataBroker, mountService, encryptionService); } @Override public void onNetconfSubsystemOpened(final CallHomeProtocolSessionContext session, - final CallHomeChannelActivator activator) { - final CallHomeMountSessionContext deviceContext = getSessionManager() - .createSession(session, activator, onCloseHandler); + final CallHomeChannelActivator activator) { + final CallHomeMountSessionContext deviceContext = + getSessionManager().createSession(session, activator, onCloseHandler); final NodeId nodeId = deviceContext.getId(); final Node configNode = deviceContext.getConfigNode(); LOG.info("Provisioning fake config {}", configNode); diff --git a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeTopology.java b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeTopology.java index c722f615ce..60ac1ff6a0 100644 --- a/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeTopology.java +++ b/netconf/callhome-provider/src/main/java/org/opendaylight/netconf/callhome/mount/CallHomeTopology.java @@ -9,6 +9,7 @@ package org.opendaylight.netconf.callhome.mount; import io.netty.util.concurrent.EventExecutor; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; import org.opendaylight.controller.config.threadpool.ThreadPool; import org.opendaylight.controller.md.sal.binding.api.DataBroker; @@ -23,13 +24,12 @@ import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider; public class CallHomeTopology extends BaseCallHomeTopology { public CallHomeTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher, - final EventExecutor eventExecutor, - final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor, - final SchemaRepositoryProvider schemaRepositoryProvider, - final DataBroker dataBroker, final DOMMountPointService mountPointService) { - super(topologyId, clientDispatcher, eventExecutor, - keepaliveExecutor, processingExecutor, schemaRepositoryProvider, - dataBroker, mountPointService); + final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor, + final ThreadPool processingExecutor, final SchemaRepositoryProvider schemaRepositoryProvider, + final DataBroker dataBroker, final DOMMountPointService mountPointService, + final AAAEncryptionService encryptionService) { + super(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor, processingExecutor, + schemaRepositoryProvider, dataBroker, mountPointService, encryptionService); } @Override diff --git a/netconf/callhome-provider/src/main/resources/org/opendaylight/blueprint/callhome-topology.xml b/netconf/callhome-provider/src/main/resources/org/opendaylight/blueprint/callhome-topology.xml index 201c206282..3fdc8c9467 100755 --- a/netconf/callhome-provider/src/main/resources/org/opendaylight/blueprint/callhome-topology.xml +++ b/netconf/callhome-provider/src/main/resources/org/opendaylight/blueprint/callhome-topology.xml @@ -23,6 +23,9 @@ interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"/> + + @@ -44,6 +47,7 @@ + \ No newline at end of file diff --git a/netconf/callhome-provider/src/test/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcherTest.java b/netconf/callhome-provider/src/test/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcherTest.java index 1b822413a5..b7d074eb50 100644 --- a/netconf/callhome-provider/src/test/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcherTest.java +++ b/netconf/callhome-provider/src/test/java/org/opendaylight/netconf/callhome/mount/CallHomeMountDispatcherTest.java @@ -22,6 +22,7 @@ import java.net.InetSocketAddress; import java.net.UnknownHostException; import org.junit.Before; import org.junit.Test; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; import org.opendaylight.controller.config.threadpool.ThreadPool; import org.opendaylight.controller.md.sal.binding.api.DataBroker; @@ -53,6 +54,7 @@ public class CallHomeMountDispatcherTest { private CallHomeMountSessionManager mockSessMgr; private CallHomeTopology mockTopology; private CallHomeProtocolSessionContext mockProtoSess; + private AAAEncryptionService mockEncryptionService; @Before public void setup() { @@ -66,9 +68,10 @@ public class CallHomeMountDispatcherTest { mockSessMgr = mock(CallHomeMountSessionManager.class); mockTopology = mock(CallHomeTopology.class); mockProtoSess = mock(CallHomeProtocolSessionContext.class); + mockEncryptionService = mock(AAAEncryptionService.class); instance = new CallHomeMountDispatcher(topologyId, mockExecutor, mockKeepAlive, - mockProcessingExecutor, mockSchemaRepoProvider, mockDataBroker, mockMount) { + mockProcessingExecutor, mockSchemaRepoProvider, mockDataBroker, mockMount, mockEncryptionService) { @Override public CallHomeMountSessionManager getSessionManager() { return mockSessMgr; diff --git a/netconf/netconf-netty-util/pom.xml b/netconf/netconf-netty-util/pom.xml index 6cb6e9d668..82d0ee0a1f 100644 --- a/netconf/netconf-netty-util/pom.xml +++ b/netconf/netconf-netty-util/pom.xml @@ -108,6 +108,11 @@ org.opendaylight.yangtools mockito-configuration + + org.opendaylight.aaa + aaa-encrypt-service + 0.6.0-SNAPSHOT + 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/LoginPassword.java index 46cb2c717b..500791fc82 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/LoginPassword.java @@ -11,28 +11,45 @@ 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 { + private final String username; private final String password; + private final AAAEncryptionService encryptionService; public LoginPassword(String username, String password) { + this(username, password, null); + } + + public LoginPassword(final String username, final String password, final AAAEncryptionService encryptionService) { this.username = username; this.password = password; + this.encryptionService = encryptionService; } @Override public String getUsername() { + if (encryptionService != null) { + return encryptionService.decrypt(username); + + } return username; } @Override public AuthFuture authenticate(final ClientSession session) throws IOException { - session.addPasswordIdentity(password); + if (encryptionService != null) { + final String decryptedPassword = encryptionService.decrypt(password); + session.addPasswordIdentity(decryptedPassword); + } else { + session.addPasswordIdentity(password); + } return session.auth(); } } 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 11e922f34d..94dd257e45 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 @@ -28,6 +28,9 @@ interface="org.opendaylight.controller.md.sal.dom.api.DOMMountPointService" odl:type="default"/> + + @@ -43,10 +46,11 @@ + - \ No newline at end of file + diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManager.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManager.java index 5019b11bf1..decd31e554 100644 --- a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManager.java +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManager.java @@ -19,6 +19,7 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.cluster.ActorSystemProvider; import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; import org.opendaylight.controller.config.threadpool.ThreadPool; @@ -75,7 +76,7 @@ public class NetconfTopologyManager private final String topologyId; private final Duration writeTxIdleTimeout; private final DOMMountPointService mountPointService; - + private final AAAEncryptionService encryptionService; private ListenerRegistration dataChangeListenerRegistration; public NetconfTopologyManager(final DataBroker dataBroker, final RpcProviderRegistry rpcProviderRegistry, @@ -84,7 +85,9 @@ public class NetconfTopologyManager final ActorSystemProvider actorSystemProvider, final EventExecutor eventExecutor, final NetconfClientDispatcher clientDispatcher, final String topologyId, final Config config, - final DOMMountPointService mountPointService) { + final DOMMountPointService mountPointService, + final AAAEncryptionService encryptionService) { + this.dataBroker = Preconditions.checkNotNull(dataBroker); this.rpcProviderRegistry = Preconditions.checkNotNull(rpcProviderRegistry); this.clusterSingletonServiceProvider = Preconditions.checkNotNull(clusterSingletonServiceProvider); @@ -96,6 +99,7 @@ public class NetconfTopologyManager this.topologyId = Preconditions.checkNotNull(topologyId); this.writeTxIdleTimeout = Duration.apply(config.getWriteTransactionIdleTimeout(), TimeUnit.SECONDS); this.mountPointService = mountPointService; + this.encryptionService = Preconditions.checkNotNull(encryptionService); } // Blueprint init method @@ -267,7 +271,8 @@ public class NetconfTopologyManager .setTopologyId(topologyId) .setNetconfClientDispatcher(clientDispatcher) .setSchemaResourceDTO(NetconfTopologyUtils.setupSchemaCacheDTO(node)) - .setIdleTimeout(writeTxIdleTimeout); + .setIdleTimeout(writeTxIdleTimeout) + .setEncryptionService(encryptionService); return builder.build(); } 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 f242558f05..962e6b85f2 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 @@ -26,6 +26,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import javax.annotation.Nullable; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService; import org.opendaylight.netconf.api.NetconfMessage; import org.opendaylight.netconf.client.NetconfClientSessionListener; @@ -46,6 +47,7 @@ import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPrefe 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.schema.YangLibrarySchemaYangSourceProvider; +import org.opendaylight.netconf.sal.connect.util.AuthEncryptor; import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; import org.opendaylight.netconf.topology.singleton.api.RemoteDeviceConnector; import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfConnectorDTO; @@ -78,6 +80,7 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { private final RemoteDeviceId remoteDeviceId; private final DOMMountPointService mountService; private final Timeout actorResponseWaitTime; + private final AAAEncryptionService encryptionService; private NetconfConnectorDTO deviceCommunicatorDTO; @@ -89,6 +92,8 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { this.remoteDeviceId = remoteDeviceId; this.actorResponseWaitTime = actorResponseWaitTime; this.mountService = mountService; + this.encryptionService = netconfTopologyDeviceSetup.getEncryptionService(); + } @Override @@ -96,6 +101,11 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { final NetconfNode netconfNode = netconfTopologyDeviceSetup.getNode().getAugmentation(NetconfNode.class); final NodeId nodeId = netconfTopologyDeviceSetup.getNode().getNodeId(); + + AuthEncryptor.encryptIfNeeded(nodeId, netconfNode, encryptionService, + netconfTopologyDeviceSetup.getTopologyId(), + netconfTopologyDeviceSetup.getDataBroker()); + Preconditions.checkNotNull(netconfNode.getHost()); Preconditions.checkNotNull(netconfNode.getPort()); Preconditions.checkNotNull(netconfNode.isTcpOnly()); @@ -280,7 +290,8 @@ public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { ((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()); + .node.credentials.credentials.LoginPassword) credentials).getPassword(), + encryptionService); } else { throw new IllegalStateException(remoteDeviceId + ": Only login/password authentication is supported"); } diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologySetup.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologySetup.java index cee8c0d807..23376b6450 100644 --- a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologySetup.java +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologySetup.java @@ -10,6 +10,7 @@ package org.opendaylight.netconf.topology.singleton.impl.utils; import akka.actor.ActorSystem; import io.netty.util.concurrent.EventExecutor; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; import org.opendaylight.controller.config.threadpool.ThreadPool; import org.opendaylight.controller.md.sal.binding.api.DataBroker; @@ -36,6 +37,7 @@ public class NetconfTopologySetup { private final String topologyId; private final NetconfDevice.SchemaResourcesDTO schemaResourceDTO; private final Duration idleTimeout; + private final AAAEncryptionService encryptionService; private NetconfTopologySetup(final NetconfTopologySetupBuilder builder) { this.clusterSingletonServiceProvider = builder.getClusterSingletonServiceProvider(); @@ -51,6 +53,7 @@ public class NetconfTopologySetup { this.topologyId = builder.getTopologyId(); this.schemaResourceDTO = builder.getSchemaResourceDTO(); this.idleTimeout = builder.getIdleTimeout(); + this.encryptionService = builder.getEncryptionService(); } public ClusterSingletonServiceProvider getClusterSingletonServiceProvider() { @@ -105,6 +108,10 @@ public class NetconfTopologySetup { return idleTimeout; } + public AAAEncryptionService getEncryptionService() { + return encryptionService; + } + public static class NetconfTopologySetupBuilder { private ClusterSingletonServiceProvider clusterSingletonServiceProvider; @@ -120,6 +127,7 @@ public class NetconfTopologySetup { private NetconfClientDispatcher netconfClientDispatcher; private NetconfDevice.SchemaResourcesDTO schemaResourceDTO; private Duration idleTimeout; + private AAAEncryptionService encryptionService; public NetconfTopologySetupBuilder(){ } @@ -247,6 +255,15 @@ public class NetconfTopologySetup { return idleTimeout; } + private AAAEncryptionService getEncryptionService() { + return this.encryptionService; + } + + public NetconfTopologySetupBuilder setEncryptionService(final AAAEncryptionService encryptionService) { + this.encryptionService = encryptionService; + return this; + } + public static NetconfTopologySetupBuilder create() { return new NetconfTopologySetupBuilder(); } diff --git a/netconf/netconf-topology-singleton/src/main/resources/org/opendaylight/blueprint/netconf-topology-singleton.xml b/netconf/netconf-topology-singleton/src/main/resources/org/opendaylight/blueprint/netconf-topology-singleton.xml index 8c60682605..f173a95a2a 100644 --- a/netconf/netconf-topology-singleton/src/main/resources/org/opendaylight/blueprint/netconf-topology-singleton.xml +++ b/netconf/netconf-topology-singleton/src/main/resources/org/opendaylight/blueprint/netconf-topology-singleton.xml @@ -39,6 +39,9 @@ and is available at http://www.eclipse.org/legal/epl-v10.html binding-class="org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.topology.singleton.config.rev170419.Config" /> + + @@ -53,6 +56,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html + diff --git a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManagerTest.java b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManagerTest.java index 817a58a5ec..0353f889ab 100644 --- a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManagerTest.java +++ b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManagerTest.java @@ -32,6 +32,7 @@ import javax.annotation.Nonnull; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.cluster.ActorSystemProvider; import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; import org.opendaylight.controller.config.threadpool.ThreadPool; @@ -84,11 +85,13 @@ public class NetconfTopologyManagerTest { final EventExecutor eventExecutor = mock(EventExecutor.class); final NetconfClientDispatcher clientDispatcher = mock(NetconfClientDispatcher.class); final DOMMountPointService mountPointService = mock(DOMMountPointService.class); + final AAAEncryptionService encryptionService = mock(AAAEncryptionService.class); final Config config = new ConfigBuilder().setWriteTransactionIdleTimeout(0).build(); netconfTopologyManager = new NetconfTopologyManager(dataBroker, rpcProviderRegistry, clusterSingletonServiceProvider, keepaliveExecutor, processingExecutor, - actorSystemProvider, eventExecutor, clientDispatcher, topologyId, config, mountPointService); + actorSystemProvider, eventExecutor, clientDispatcher, topologyId, config, + mountPointService, encryptionService); } @Test 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..4aeb5de1a1 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 @@ -32,6 +32,7 @@ import java.util.concurrent.ExecutorService; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; import org.opendaylight.controller.config.threadpool.ThreadPool; import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain; @@ -105,6 +106,9 @@ public class RemoteDeviceConnectorImplTest { @Mock private WriteTransaction writeTx; + @Mock + private AAAEncryptionService encryptionService; + private NetconfTopologySetup.NetconfTopologySetupBuilder builder; private RemoteDeviceId remoteDeviceId; @@ -130,6 +134,7 @@ public class RemoteDeviceConnectorImplTest { builder.setEventExecutor(eventExecutor); builder.setNetconfClientDispatcher(clientDispatcher); builder.setTopologyId(TOPOLOGY_ID); + builder.setEncryptionService(encryptionService); } @Test @@ -258,7 +263,7 @@ public class RemoteDeviceConnectorImplTest { assertEquals(defaultClientConfig.getAddress(), new InetSocketAddress(InetAddresses.forString("127.0.0.1"), 9999)); assertSame(defaultClientConfig.getSessionListener(), listener); - assertEquals(defaultClientConfig.getAuthHandler().getUsername(), "testuser"); + assertEquals(defaultClientConfig.getAuthHandler().getUsername(), encryptionService.encrypt("testuser")); assertEquals(defaultClientConfig.getProtocol(), NetconfClientConfiguration.NetconfClientProtocol.TCP); } } 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 ad4bfa9029..c7f5cf4509 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 @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; import org.opendaylight.controller.config.threadpool.ThreadPool; import org.opendaylight.controller.md.sal.binding.api.DataBroker; @@ -49,6 +50,7 @@ import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPrefe 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.schema.YangLibrarySchemaYangSourceProvider; +import org.opendaylight.netconf.sal.connect.util.AuthEncryptor; import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; import org.opendaylight.netconf.topology.api.NetconfTopology; import org.opendaylight.netconf.topology.api.SchemaRepositoryProvider; @@ -167,11 +169,14 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { protected final HashMap activeConnectors = new HashMap<>(); + protected final AAAEncryptionService encryptionService; + protected AbstractNetconfTopology(final String topologyId, final NetconfClientDispatcher clientDispatcher, final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor, final SchemaRepositoryProvider schemaRepositoryProvider, - final DataBroker dataBroker, final DOMMountPointService mountPointService) { + final DataBroker dataBroker, final DOMMountPointService mountPointService, + final AAAEncryptionService encryptionService) { this.topologyId = topologyId; this.clientDispatcher = clientDispatcher; this.eventExecutor = eventExecutor; @@ -180,6 +185,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { this.sharedSchemaRepository = schemaRepositoryProvider.getSharedSchemaRepository(); this.dataBroker = dataBroker; this.mountPointService = mountPointService; + this.encryptionService = encryptionService; } public void setSchemaRegistry(final SchemaSourceRegistry schemaRegistry) { @@ -215,6 +221,8 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { final Node configNode) { final NetconfNode netconfNode = configNode.getAugmentation(NetconfNode.class); + AuthEncryptor.encryptIfNeeded(nodeId, netconfNode, encryptionService, topologyId, dataBroker); + Preconditions.checkNotNull(netconfNode.getHost()); Preconditions.checkNotNull(netconfNode.getPort()); Preconditions.checkNotNull(netconfNode.isTcpOnly()); @@ -423,7 +431,8 @@ public abstract class AbstractNetconfTopology implements NetconfTopology { ((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()); + .netconf.node.credentials.credentials.LoginPassword) credentials).getPassword(), + encryptionService); } else { throw new IllegalStateException("Only login/password authentification is supported"); } diff --git a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImpl.java b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImpl.java index 97bf00a5fd..41e4ef62fd 100644 --- a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImpl.java +++ b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/impl/NetconfTopologyImpl.java @@ -13,6 +13,7 @@ import com.google.common.util.concurrent.Futures; import io.netty.util.concurrent.EventExecutor; import java.util.Collection; import javax.annotation.Nonnull; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; import org.opendaylight.controller.config.threadpool.ThreadPool; import org.opendaylight.controller.md.sal.binding.api.DataBroker; @@ -53,9 +54,10 @@ public class NetconfTopologyImpl extends AbstractNetconfTopology final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor, final SchemaRepositoryProvider schemaRepositoryProvider, - final DataBroker dataBroker, final DOMMountPointService mountPointService) { + final DataBroker dataBroker, final DOMMountPointService mountPointService, + final AAAEncryptionService encryptionService) { super(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor, processingExecutor, - schemaRepositoryProvider, dataBroker, mountPointService); + schemaRepositoryProvider, dataBroker, mountPointService, encryptionService); } @Override 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 5227291635..48df0a6c03 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 @@ -29,6 +29,7 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; import org.opendaylight.controller.config.threadpool.ThreadPool; import org.opendaylight.controller.md.sal.binding.api.DataBroker; @@ -89,6 +90,9 @@ public class NetconfTopologyImplTest { @Mock private DOMMountPointService mountPointService; + @Mock + private AAAEncryptionService encryptionService; + private TestingNetconfTopologyImpl topology; private TestingNetconfTopologyImpl spyTopology; @@ -105,7 +109,7 @@ public class NetconfTopologyImplTest { topology = new TestingNetconfTopologyImpl(TOPOLOGY_ID, mockedClientDispatcher, mockedEventExecutor, mockedKeepaliveExecutor, mockedProcessingExecutor, mockedSchemaRepositoryProvider, - dataBroker, mountPointService); + dataBroker, mountPointService, encryptionService); spyTopology = spy(topology); } @@ -192,9 +196,10 @@ public class NetconfTopologyImplTest { final String topologyId, final NetconfClientDispatcher clientDispatcher, final EventExecutor eventExecutor, final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor, final SchemaRepositoryProvider schemaRepositoryProvider, - final DataBroker dataBroker, final DOMMountPointService mountPointService) { + final DataBroker dataBroker, final DOMMountPointService mountPointService, + final AAAEncryptionService encryptionService) { super(topologyId, clientDispatcher, eventExecutor, keepaliveExecutor, - processingExecutor, schemaRepositoryProvider, dataBroker, mountPointService); + processingExecutor, schemaRepositoryProvider, dataBroker, mountPointService, encryptionService); } @Override diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/util/AuthEncryptor.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/util/AuthEncryptor.java new file mode 100644 index 0000000000..28a9171722 --- /dev/null +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/util/AuthEncryptor.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2016 Brocade Communication Systems 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 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.NetconfNode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder; +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; +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.TopologyKey; +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.NodeKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility to encrypt netconf username and password. + */ +public class AuthEncryptor { + private static final Logger LOG = LoggerFactory.getLogger(AuthEncryptor.class); + + public static void encryptIfNeeded(final NodeId nodeId, final NetconfNode netconfNode, + AAAEncryptionService encryptionService, + final String topologyId, final DataBroker dataBroker) { + final org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node + .credentials.credentials.LoginPassword creds = + (org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node + .credentials.credentials.LoginPassword) netconfNode.getCredentials(); + final String decryptedPassword = encryptionService.decrypt(creds.getPassword()); + if (decryptedPassword != null && decryptedPassword.equals(creds.getPassword())) { + LOG.info("Encrypting the provided credentials"); + final String username = encryptionService.encrypt(creds.getUsername()); + final String password = encryptionService.encrypt(creds.getPassword()); + final org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node + .credentials.credentials.LoginPasswordBuilder passwordBuilder = + new org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114 + .netconf.node.credentials.credentials.LoginPasswordBuilder(); + passwordBuilder.setUsername(username); + passwordBuilder.setPassword(password); + final NetconfNodeBuilder nnb = new NetconfNodeBuilder(); + nnb.setCredentials(passwordBuilder.build()); + + 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, nnb.build()); + final CheckedFuture future = writeTransaction.submit(); + Futures.addCallback(future, new FutureCallback() { + + @Override + public void onSuccess(Void result) { + LOG.info("Encrypted netconf username/password successfully"); + } + + @Override + public void onFailure(Throwable exception) { + LOG.error("Unable to encrypt netconf username/password." + exception.getMessage()); + } + }); + } + } +} -- 2.36.6