package org.opendaylight.controller.config.yang.netconf.northbound.ssh; import io.netty.channel.ChannelFuture; import io.netty.channel.local.LocalAddress; import io.netty.util.concurrent.GenericFutureListener; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.concurrent.Executors; import org.apache.sshd.server.PasswordAuthenticator; import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider; import org.apache.sshd.server.session.ServerSession; import org.opendaylight.controller.netconf.api.NetconfServerDispatcher; import org.opendaylight.controller.netconf.ssh.SshProxyServer; import org.opendaylight.controller.netconf.ssh.SshProxyServerConfigurationBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class NetconfNorthboundSshModule extends org.opendaylight.controller.config.yang.netconf.northbound.ssh.AbstractNetconfNorthboundSshModule { private static final Logger LOG = LoggerFactory.getLogger(NetconfNorthboundSshModule.class); public NetconfNorthboundSshModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { super(identifier, dependencyResolver); } public NetconfNorthboundSshModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, final org.opendaylight.controller.config.yang.netconf.northbound.ssh.NetconfNorthboundSshModule oldModule, final java.lang.AutoCloseable oldInstance) { super(identifier, dependencyResolver, oldModule, oldInstance); } @Override public void customValidation() { // add custom validation form module attributes here. } @Override public java.lang.AutoCloseable createInstance() { final NetconfServerDispatcher dispatch = getDispatcherDependency(); final LocalAddress localAddress = new LocalAddress(getPort().toString()); final ChannelFuture localServer = dispatch.createLocalServer(localAddress); final SshProxyServer sshProxyServer = new SshProxyServer(Executors.newScheduledThreadPool(1), getWorkerThreadGroupDependency(), getEventExecutorDependency()); final InetSocketAddress bindingAddress = getInetAddress(); final SshProxyServerConfigurationBuilder sshProxyServerConfigurationBuilder = new SshProxyServerConfigurationBuilder(); sshProxyServerConfigurationBuilder.setBindingAddress(bindingAddress); sshProxyServerConfigurationBuilder.setLocalAddress(localAddress); sshProxyServerConfigurationBuilder.setAuthenticator(new UserAuthenticator(getUsername(), getPassword())); sshProxyServerConfigurationBuilder.setIdleTimeout(Integer.MAX_VALUE); sshProxyServerConfigurationBuilder.setKeyPairProvider(new PEMGeneratorHostKeyProvider()); localServer.addListener(new GenericFutureListener() { @Override public void operationComplete(final ChannelFuture future) { if(future.isDone() && !future.isCancelled()) { try { sshProxyServer.bind(sshProxyServerConfigurationBuilder.createSshProxyServerConfiguration()); LOG.info("Netconf SSH endpoint started successfully at {}", bindingAddress); } catch (final IOException e) { throw new RuntimeException("Unable to start SSH netconf server", e); } } else { LOG.warn("Unable to start SSH netconf server at {}", bindingAddress, future.cause()); throw new RuntimeException("Unable to start SSH netconf server", future.cause()); } } }); return new NetconfServerCloseable(localServer, sshProxyServer); } private InetSocketAddress getInetAddress() { try { final InetAddress inetAd = InetAddress.getByName(getBindingAddress().getIpv4Address() == null ? getBindingAddress().getIpv6Address().getValue() : getBindingAddress().getIpv4Address().getValue()); return new InetSocketAddress(inetAd, getPort().getValue()); } catch (final UnknownHostException e) { throw new IllegalArgumentException("Unable to bind netconf endpoint to address " + getBindingAddress(), e); } } private static final class NetconfServerCloseable implements AutoCloseable { private final ChannelFuture localServer; private final SshProxyServer sshProxyServer; public NetconfServerCloseable(final ChannelFuture localServer, final SshProxyServer sshProxyServer) { this.localServer = localServer; this.sshProxyServer = sshProxyServer; } @Override public void close() throws Exception { sshProxyServer.close(); if(localServer.isDone()) { localServer.channel().close(); } else { localServer.cancel(true); } } } private static final class UserAuthenticator implements PasswordAuthenticator { private final String username; private final String password; public UserAuthenticator(final String username, final String password) { this.username = username; this.password = password; } @Override public boolean authenticate(final String username, final String password, final ServerSession session) { // FIXME use aaa stuff here instead return this.username.equals(username) && this.password.equals(password); } } }