2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.netconf.ssh;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.annotations.VisibleForTesting;
14 import com.google.common.collect.ImmutableList;
15 import io.netty.channel.EventLoopGroup;
16 import java.io.IOException;
17 import java.nio.channels.AsynchronousChannelGroup;
18 import java.util.List;
19 import java.util.concurrent.ExecutorService;
20 import java.util.concurrent.ScheduledExecutorService;
21 import java.util.concurrent.TimeUnit;
22 import org.apache.sshd.common.FactoryManager;
23 import org.apache.sshd.common.NamedFactory;
24 import org.apache.sshd.common.RuntimeSshException;
25 import org.apache.sshd.common.cipher.BuiltinCiphers;
26 import org.apache.sshd.common.cipher.Cipher;
27 import org.apache.sshd.common.io.IoAcceptor;
28 import org.apache.sshd.common.io.IoConnector;
29 import org.apache.sshd.common.io.IoHandler;
30 import org.apache.sshd.common.io.IoServiceEventListener;
31 import org.apache.sshd.common.io.IoServiceFactory;
32 import org.apache.sshd.common.io.IoServiceFactoryFactory;
33 import org.apache.sshd.common.io.nio2.Nio2Acceptor;
34 import org.apache.sshd.common.io.nio2.Nio2Connector;
35 import org.apache.sshd.common.io.nio2.Nio2ServiceFactoryFactory;
36 import org.apache.sshd.common.util.closeable.AbstractCloseable;
37 import org.apache.sshd.server.ServerFactoryManager;
38 import org.apache.sshd.server.SshServer;
41 * Proxy SSH server that just delegates decrypted content to a delegate server within same VM.
42 * Implemented using Apache Mina SSH lib.
44 public class SshProxyServer implements AutoCloseable {
45 private final SshServer sshServer;
46 private final ScheduledExecutorService minaTimerExecutor;
47 private final EventLoopGroup clientGroup;
48 private final IoServiceFactoryFactory nioServiceWithPoolFactoryFactory;
50 private SshProxyServer(final ScheduledExecutorService minaTimerExecutor, final EventLoopGroup clientGroup,
51 final IoServiceFactoryFactory serviceFactory) {
52 this.minaTimerExecutor = minaTimerExecutor;
53 this.clientGroup = clientGroup;
54 this.nioServiceWithPoolFactoryFactory = serviceFactory;
55 this.sshServer = SshServer.setUpDefaultServer();
58 public SshProxyServer(final ScheduledExecutorService minaTimerExecutor,
59 final EventLoopGroup clientGroup, final ExecutorService nioExecutor) {
60 this(minaTimerExecutor, clientGroup, new NioServiceWithPoolFactoryFactory(nioExecutor));
64 * Create a server with a shared {@link AsynchronousChannelGroup}. Unlike the other constructor, this does
65 * not create a dedicated thread group, which is useful when you need to start a large number of servers and do
66 * not want to have a thread group (and hence an anonyous thread) for each of them.
69 public SshProxyServer(final ScheduledExecutorService minaTimerExecutor, final EventLoopGroup clientGroup,
70 final AsynchronousChannelGroup group) {
71 this(minaTimerExecutor, clientGroup, new SharedNioServiceFactoryFactory(group));
74 public void bind(final SshProxyServerConfiguration sshProxyServerConfiguration) throws IOException {
75 sshServer.setHost(sshProxyServerConfiguration.getBindingAddress().getHostString());
76 sshServer.setPort(sshProxyServerConfiguration.getBindingAddress().getPort());
79 final List<NamedFactory<Cipher>> cipherFactories = sshServer.getCipherFactories();
80 cipherFactories.removeIf(factory -> factory.getName().contains(BuiltinCiphers.arcfour128.getName())
81 || factory.getName().contains(BuiltinCiphers.arcfour256.getName()));
82 sshServer.setPasswordAuthenticator(
83 (username, password, session)
84 -> sshProxyServerConfiguration.getAuthenticator().authenticated(username, password));
86 sshProxyServerConfiguration.getPublickeyAuthenticator().ifPresent(sshServer::setPublickeyAuthenticator);
88 sshServer.setKeyPairProvider(sshProxyServerConfiguration.getKeyPairProvider());
90 sshServer.setIoServiceFactoryFactory(nioServiceWithPoolFactoryFactory);
91 sshServer.setScheduledExecutorService(minaTimerExecutor);
92 sshServer.getProperties().put(ServerFactoryManager.IDLE_TIMEOUT,
93 String.valueOf(sshProxyServerConfiguration.getIdleTimeout()));
94 sshServer.getProperties().put(ServerFactoryManager.AUTH_TIMEOUT,
95 String.valueOf(sshProxyServerConfiguration.getIdleTimeout()));
97 final RemoteNetconfCommand.NetconfCommandFactory netconfCommandFactory =
98 new RemoteNetconfCommand.NetconfCommandFactory(clientGroup,
99 sshProxyServerConfiguration.getLocalAddress());
100 sshServer.setSubsystemFactories(ImmutableList.of(netconfCommandFactory));
105 public void close() throws IOException {
107 sshServer.stop(true);
109 sshServer.close(true);
113 private abstract static class AbstractNioServiceFactory extends AbstractCloseable implements IoServiceFactory {
114 private final FactoryManager manager;
115 private final AsynchronousChannelGroup group;
117 private IoServiceEventListener eventListener;
119 AbstractNioServiceFactory(final FactoryManager manager, final AsynchronousChannelGroup group) {
120 this.manager = requireNonNull(manager);
121 this.group = requireNonNull(group);
124 final AsynchronousChannelGroup group() {
129 public final IoConnector createConnector(final IoHandler handler) {
130 return new Nio2Connector(manager, handler, group);
134 public final IoAcceptor createAcceptor(final IoHandler handler) {
135 return new Nio2Acceptor(manager, handler, group);
139 public final IoServiceEventListener getIoServiceEventListener() {
140 return eventListener;
144 public final void setIoServiceEventListener(final IoServiceEventListener listener) {
145 eventListener = listener;
150 * Based on Nio2ServiceFactory with one addition: injectable executor.
152 private static final class NioServiceWithPoolFactory extends AbstractNioServiceFactory {
153 NioServiceWithPoolFactory(final FactoryManager manager, final AsynchronousChannelGroup group) {
154 super(manager, group);
157 @SuppressWarnings("checkstyle:IllegalCatch")
159 protected void doCloseImmediately() {
161 group().shutdownNow();
162 group().awaitTermination(5, TimeUnit.SECONDS);
163 } catch (final Exception e) {
164 log.debug("Exception caught while closing channel group", e);
166 super.doCloseImmediately();
171 private static final class NioServiceWithPoolFactoryFactory extends Nio2ServiceFactoryFactory {
172 private final ExecutorService nioExecutor;
174 NioServiceWithPoolFactoryFactory(final ExecutorService nioExecutor) {
175 this.nioExecutor = nioExecutor;
179 public IoServiceFactory create(final FactoryManager manager) {
181 return new NioServiceWithPoolFactory(manager, AsynchronousChannelGroup.withThreadPool(nioExecutor));
182 } catch (final IOException e) {
183 throw new RuntimeSshException("Failed to create channel group", e);
188 private static final class SharedNioServiceFactory extends AbstractNioServiceFactory {
189 SharedNioServiceFactory(final FactoryManager manager, final AsynchronousChannelGroup group) {
190 super(manager, group);
194 private static final class SharedNioServiceFactoryFactory extends Nio2ServiceFactoryFactory {
195 private final AsynchronousChannelGroup group;
197 SharedNioServiceFactoryFactory(final AsynchronousChannelGroup group) {
198 this.group = requireNonNull(group);
202 public IoServiceFactory create(final FactoryManager manager) {
203 return new SharedNioServiceFactory(manager, group);