<groupId>org.opendaylight.netconf</groupId>
<artifactId>netconf-config</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.aaa</groupId>
+ <artifactId>odl-aaa-encryption-service</artifactId>
+ <version>0.7.0-SNAPSHOT</version>
+ <type>xml</type>
+ <classifier>features</classifier>
+ </dependency>
</dependencies>
-</project>
\ No newline at end of file
+</project>
<type>xml</type>
<classifier>features</classifier>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.aaa</groupId>
+ <artifactId>aaa-encrypt-service</artifactId>
+ <version>0.7.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.aaa</groupId>
+ <artifactId>aaa-encrypt-service</artifactId>
+ <version>0.7.0-SNAPSHOT</version>
+ <classifier>config</classifier>
+ <type>xml</type>
+ </dependency>
</dependencies>
</project>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>mockito-configuration</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.aaa</groupId>
+ <artifactId>aaa-encrypt-service</artifactId>
+ <version>0.7.0-SNAPSHOT</version>
+ </dependency>
</dependencies>
<build>
* {@link org.opendaylight.netconf.nettyutil.handler.ssh.client.AsyncSshHandler}.
*/
public class LoginPassword extends AuthenticationHandler {
- private final String username;
- private final String password;
+ protected final String username;
+ protected final String password;
public LoginPassword(String username, String password) {
this.username = username;
--- /dev/null
+/*
+ * Copyright (c) 2017 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.nettyutil.handler.ssh.authentication;
+
+import com.google.common.base.Strings;
+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.PKIUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Represents Auth information for the public key based authentication for netconf.
+ */
+public class PublicKeyAuth extends LoginPassword {
+ private KeyPair keyPair = null;
+ private static final Logger LOG = LoggerFactory.getLogger(PublicKeyAuth.class);
+
+ public PublicKeyAuth(String username, String password, String keyPath, String passPhrase) {
+ super(username, password);
+ try {
+ boolean isKeyPathAbsent = Strings.isNullOrEmpty(keyPath);
+ passPhrase = Strings.isNullOrEmpty(passPhrase) ? "" : passPhrase;
+ if (!isKeyPathAbsent) {
+ this.keyPair = new PKIUtil().decodePrivateKey(keyPath, passPhrase);
+ } else {
+ LOG.info("Private key path not specified in the config file.");
+ }
+ } catch (IOException ioEx) {
+ LOG.warn("Not able to read the private key and passphrase for netconf client", ioEx);
+ }
+ }
+
+ @Override
+ public AuthFuture authenticate(final ClientSession session) throws IOException {
+ if (keyPair != null) {
+ session.addPublicKeyIdentity(keyPair);
+ }
+ session.addPasswordIdentity(password);
+ return session.auth();
+ }
+}
</instructions>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${project.build.directory}/classes/initial/odl-sb-netconf-client-keypair.cfg
+ </file>
+ <type>cfg</type>
+ <classifier>config</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
</project>
--- /dev/null
+# This configuration provides the provision to enable key based authentication for netconf southbound client.
+# The configuration file should be created by name odl-sb-netconf-client-keypair.cfg inside controller/etc directory.
+# Following configurations should be done in this file
+# private-key-path - Path for private key file. (Paths are identified relative to controller directory).
+# eg. If private key file exists in controller/etc/id_rsa, the path can be mentioned as etc/id_rsa
+# private-key-passphrase - Passphrase that was used to encrypt the private key.
+# In case of no passphrase, keep it blank or unassigned.
+
+private-key-path=etc/RSA-PK
+private-key-passphrase=abc
-->
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+ xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
odl:use-default-for-reference-types="true">
<reference id="clientDispatcherDependency"
<argument value="shared-schema-repository-impl"/>
</bean>
+ <cm:property-placeholder persistent-id="odl-sb-netconf-client-keypair" update-strategy="none">
+ <cm:default-properties>
+ <cm:property name="private-key-path" value=""/>
+ <cm:property name="private-key-passphrase" value=""/>
+ </cm:default-properties>
+ </cm:property-placeholder>
+
<bean id="netconfTopology" class="org.opendaylight.netconf.topology.impl.NetconfTopologyImpl"
init-method="init"
destroy-method="close">
+ <cm:managed-properties persistent-id="odl-sb-netconf-client-keypair"
+ update-strategy="container-managed"/>
<argument value="topology-netconf"/>
<argument ref="clientDispatcherDependency"/>
<argument ref="eventExecutor"/>
<argument ref="schemaRepository"/>
<argument ref="dataBroker"/>
<argument ref="mountPointService"/>
+ <property name="privateKeyPath" value="${private-key-path}"/>
+ <property name="privateKeyPassphrase" value="${private-key-passphrase}"/>
</bean>
<bean id="netconfConnectorFactory" class="org.opendaylight.netconf.topology.impl.NetconfConnectorFactoryImpl"/>
<service ref="netconfConnectorFactory" interface="org.opendaylight.netconf.topology.api.NetconfConnectorFactory"
odl:type="default"/>
-</blueprint>
\ No newline at end of file
+</blueprint>
private final DOMMountPointService mountPointService;
private ListenerRegistration<NetconfTopologyManager> dataChangeListenerRegistration;
+ private String privateKeyPath;
+ private String privateKeyPassphrase;
public NetconfTopologyManager(final DataBroker dataBroker, final RpcProviderRegistry rpcProviderRegistry,
final ClusterSingletonServiceProvider clusterSingletonServiceProvider,
clusterRegistrations.clear();
}
+ /**
+ * Sets the private key path from location specified in configuration file using blueprint.
+ */
+ public void setPrivateKeyPath(String privateKeyPath) {
+ this.privateKeyPath = privateKeyPath;
+ }
+
+ /**
+ * Sets the private key passphrase from location specified in configuration file using blueprint.
+ */
+ public void setPrivateKeyPassphrase(String privateKeyPassphrase) {
+ this.privateKeyPassphrase = privateKeyPassphrase;
+ }
+
private ListenerRegistration<NetconfTopologyManager> registerDataTreeChangeListener(final String topologyId) {
final WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
initTopology(wtx, LogicalDatastoreType.CONFIGURATION, topologyId);
.setTopologyId(topologyId)
.setNetconfClientDispatcher(clientDispatcher)
.setSchemaResourceDTO(NetconfTopologyUtils.setupSchemaCacheDTO(node))
- .setIdleTimeout(writeTxIdleTimeout);
+ .setIdleTimeout(writeTxIdleTimeout)
+ .setPrivateKeyPath(privateKeyPath)
+ .setPrivateKeyPassphrase(privateKeyPassphrase);
return builder.build();
}
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.LoginPassword;
+import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.PublicKeyAuth;
import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
private final RemoteDeviceId remoteDeviceId;
private final DOMMountPointService mountService;
private final Timeout actorResponseWaitTime;
+ private final String privateKeyPath;
+ private final String privateKeyPassphrase;
private NetconfConnectorDTO deviceCommunicatorDTO;
this.remoteDeviceId = remoteDeviceId;
this.actorResponseWaitTime = actorResponseWaitTime;
this.mountService = mountService;
+ this.privateKeyPath = netconfTopologyDeviceSetup.getPrivateKeyPath();
+ this.privateKeyPassphrase = netconfTopologyDeviceSetup.getPrivateKeyPassphrase();
}
@Override
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 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());
+ .node.credentials.credentials.LoginPassword) credentials).getPassword(),
+ this.privateKeyPath, this.privateKeyPassphrase);
} else {
throw new IllegalStateException(remoteDeviceId + ": Only login/password authentication is supported");
}
/*
- * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ * 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,
private final String topologyId;
private final NetconfDevice.SchemaResourcesDTO schemaResourceDTO;
private final Duration idleTimeout;
+ private final String privateKeyPath;
+ private final String privateKeyPassphrase;
private NetconfTopologySetup(final NetconfTopologySetupBuilder builder) {
this.clusterSingletonServiceProvider = builder.getClusterSingletonServiceProvider();
this.topologyId = builder.getTopologyId();
this.schemaResourceDTO = builder.getSchemaResourceDTO();
this.idleTimeout = builder.getIdleTimeout();
+ this.privateKeyPath = builder.getPrivateKeyPath();
+ this.privateKeyPassphrase = builder.getPrivateKeyPassphrase();
}
public ClusterSingletonServiceProvider getClusterSingletonServiceProvider() {
}
public NetconfDevice.SchemaResourcesDTO getSchemaResourcesDTO() {
- return schemaResourceDTO;
+ return schemaResourceDTO;
}
public Duration getIdleTimeout() {
return idleTimeout;
}
+ public String getPrivateKeyPath() {
+ return privateKeyPath;
+ }
+
+ public String getPrivateKeyPassphrase() {
+ return privateKeyPassphrase;
+ }
+
public static class NetconfTopologySetupBuilder {
private ClusterSingletonServiceProvider clusterSingletonServiceProvider;
private NetconfClientDispatcher netconfClientDispatcher;
private NetconfDevice.SchemaResourcesDTO schemaResourceDTO;
private Duration idleTimeout;
+ private String privateKeyPath;
+ private String privateKeyPassphrase;
- public NetconfTopologySetupBuilder(){
+ public NetconfTopologySetupBuilder() {
}
private ClusterSingletonServiceProvider getClusterSingletonServiceProvider() {
return idleTimeout;
}
+ public NetconfTopologySetupBuilder setPrivateKeyPath(String privateKeyPath) {
+ this.privateKeyPath = privateKeyPath;
+ return this;
+ }
+
+ public String getPrivateKeyPath() {
+ return this.privateKeyPath;
+ }
+
+ public NetconfTopologySetupBuilder setPrivateKeyPassphrase(String privateKeyPassphrase) {
+ this.privateKeyPassphrase = privateKeyPassphrase;
+ return this;
+ }
+
+ public String getPrivateKeyPassphrase() {
+ return this.privateKeyPassphrase;
+ }
+
public static NetconfTopologySetupBuilder create() {
return new NetconfTopologySetupBuilder();
}
-->
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+ xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
odl:use-default-for-reference-types="true">
<reference id="dataBroker"
binding-class="org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.topology.singleton.config.rev170419.Config"
/>
+ <cm:property-placeholder persistent-id="odl-sb-netconf-client-keypair" update-strategy="none">
+ <cm:default-properties>
+ <cm:property name="private-key-path" value=""/>
+ <cm:property name="private-key-passphrase" value=""/>
+ </cm:default-properties>
+ </cm:property-placeholder>
+
<bean id="netconfTopologyManager"
class="org.opendaylight.netconf.topology.singleton.impl.NetconfTopologyManager"
init-method="init" destroy-method="close">
+ <cm:managed-properties persistent-id="odl-sb-netconf-client-keypair"
+ update-strategy="container-managed"/>
<argument ref="dataBroker"/>
<argument ref="rpcRegistry"/>
<argument ref="clusterSingletonService"/>
<argument value="topology-netconf"/>
<argument ref="singletonConfig"/>
<argument ref="mountPointService"/>
+ <property name="privateKeyPath" value="${private-key-path}"/>
+ <property name="privateKeyPassphrase" value="${private-key-passphrase}"/>
</bean>
<service ref="netconfTopologyManager"
interface="org.opendaylight.netconf.topology.singleton.api.NetconfTopologySingletonService"/>
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.LoginPassword;
+import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.PublicKeyAuth;
import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas;
protected SchemaSourceRegistry schemaRegistry = DEFAULT_SCHEMA_REPOSITORY;
protected SchemaRepository schemaRepository = DEFAULT_SCHEMA_REPOSITORY;
protected SchemaContextFactory schemaContextFactory = DEFAULT_SCHEMA_CONTEXT_FACTORY;
+ protected String privateKeyPath;
+ protected String privateKeyPassphrase;
protected final HashMap<NodeId, NetconfConnectorDTO> activeConnectors = new HashMap<>();
new File(relativeSchemaCacheDirectory));
}
+ /**
+ * Sets the private key path from location specified in configuration file using blueprint.
+ */
+ public void setPrivateKeyPath(String privateKeyPath) {
+ this.privateKeyPath = privateKeyPath;
+ }
+
+ /**
+ * Sets the private key passphrase from location specified in configuration file using blueprint.
+ */
+ public void setPrivateKeyPassphrase(String privateKeyPassphrase) {
+ this.privateKeyPassphrase = privateKeyPassphrase;
+ }
+
public NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener,
final NetconfNode node) {
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 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());
+ .netconf.node.credentials.credentials.LoginPassword) credentials).getPassword(),
+ privateKeyPath, privateKeyPassphrase);
} else {
throw new IllegalStateException("Only login/password authentification is supported");
}