Merge "Remove the default loopback mount from the old connector feature"
[netconf.git] / netconf / netconf-ssh / src / main / java / org / opendaylight / netconf / ssh / SshProxyServer.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.netconf.ssh;
10
11 import com.google.common.collect.ImmutableList;
12 import io.netty.channel.EventLoopGroup;
13 import java.io.IOException;
14 import java.nio.channels.AsynchronousChannelGroup;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.concurrent.ExecutorService;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.concurrent.TimeUnit;
21 import org.apache.sshd.SshServer;
22 import org.apache.sshd.common.Cipher;
23 import org.apache.sshd.common.FactoryManager;
24 import org.apache.sshd.common.NamedFactory;
25 import org.apache.sshd.common.RuntimeSshException;
26 import org.apache.sshd.common.cipher.ARCFOUR128;
27 import org.apache.sshd.common.cipher.ARCFOUR256;
28 import org.apache.sshd.common.io.IoAcceptor;
29 import org.apache.sshd.common.io.IoConnector;
30 import org.apache.sshd.common.io.IoHandler;
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.CloseableUtils;
37 import org.apache.sshd.server.ServerFactoryManager;
38
39 /**
40  * Proxy SSH server that just delegates decrypted content to a delegate server within same VM.
41  * Implemented using Apache Mina SSH lib.
42  */
43 public class SshProxyServer implements AutoCloseable {
44
45     private static final ARCFOUR128.Factory DEFAULT_ARCFOUR128_FACTORY = new ARCFOUR128.Factory();
46     private static final ARCFOUR256.Factory DEFAULT_ARCFOUR256_FACTORY = new ARCFOUR256.Factory();
47     private final SshServer sshServer;
48     private final ScheduledExecutorService minaTimerExecutor;
49     private final EventLoopGroup clientGroup;
50     private final IoServiceFactoryFactory nioServiceWithPoolFactoryFactory;
51
52     public SshProxyServer(final ScheduledExecutorService minaTimerExecutor,
53                           final EventLoopGroup clientGroup, final ExecutorService nioExecutor) {
54         this.minaTimerExecutor = minaTimerExecutor;
55         this.clientGroup = clientGroup;
56         this.nioServiceWithPoolFactoryFactory =
57                 new NioServiceWithPoolFactory.NioServiceWithPoolFactoryFactory(nioExecutor);
58         this.sshServer = SshServer.setUpDefaultServer();
59     }
60
61     public void bind(final SshProxyServerConfiguration sshProxyServerConfiguration) throws IOException {
62         sshServer.setHost(sshProxyServerConfiguration.getBindingAddress().getHostString());
63         sshServer.setPort(sshProxyServerConfiguration.getBindingAddress().getPort());
64
65         //remove rc4 ciphers
66         final List<NamedFactory<Cipher>> cipherFactories = sshServer.getCipherFactories();
67         cipherFactories.removeIf(factory -> factory.getName().contains(DEFAULT_ARCFOUR128_FACTORY.getName())
68                 || factory.getName().contains(DEFAULT_ARCFOUR256_FACTORY.getName()));
69         sshServer.setPasswordAuthenticator(
70             (username, password, session)
71                 -> sshProxyServerConfiguration.getAuthenticator().authenticated(username, password));
72
73         sshProxyServerConfiguration.getPublickeyAuthenticator().ifPresent(sshServer::setPublickeyAuthenticator);
74
75         sshServer.setKeyPairProvider(sshProxyServerConfiguration.getKeyPairProvider());
76
77         sshServer.setIoServiceFactoryFactory(nioServiceWithPoolFactoryFactory);
78         sshServer.setScheduledExecutorService(minaTimerExecutor);
79         sshServer.setProperties(getProperties(sshProxyServerConfiguration));
80
81         final RemoteNetconfCommand.NetconfCommandFactory netconfCommandFactory =
82                 new RemoteNetconfCommand.NetconfCommandFactory(clientGroup,
83                         sshProxyServerConfiguration.getLocalAddress());
84         sshServer.setSubsystemFactories(ImmutableList.of(netconfCommandFactory));
85         sshServer.start();
86     }
87
88     private static Map<String, String> getProperties(final SshProxyServerConfiguration sshProxyServerConfiguration) {
89         final Map<String, String> ret = new HashMap<>();
90         ret.put(ServerFactoryManager.IDLE_TIMEOUT, String.valueOf(sshProxyServerConfiguration.getIdleTimeout()));
91         // TODO make auth timeout configurable on its own
92         ret.put(ServerFactoryManager.AUTH_TIMEOUT, String.valueOf(sshProxyServerConfiguration.getIdleTimeout()));
93
94         return ret;
95     }
96
97     @Override
98     public void close() {
99         try {
100             sshServer.stop(true);
101         } catch (final InterruptedException e) {
102             throw new RuntimeException("Interrupted while stopping sshServer", e);
103         } finally {
104             sshServer.close(true);
105         }
106     }
107
108     /**
109      * Based on Nio2ServiceFactory with one addition: injectable executor.
110      */
111     private static final class NioServiceWithPoolFactory
112             extends CloseableUtils.AbstractCloseable implements IoServiceFactory {
113
114         private final FactoryManager manager;
115         private final AsynchronousChannelGroup group;
116
117         NioServiceWithPoolFactory(final FactoryManager manager, final ExecutorService executor) {
118             this.manager = manager;
119             try {
120                 group = AsynchronousChannelGroup.withThreadPool(executor);
121             } catch (final IOException e) {
122                 throw new RuntimeSshException(e);
123             }
124         }
125
126         @Override
127         public IoConnector createConnector(final IoHandler handler) {
128             return new Nio2Connector(manager, handler, group);
129         }
130
131         @Override
132         public IoAcceptor createAcceptor(final IoHandler handler) {
133             return new Nio2Acceptor(manager, handler, group);
134         }
135
136         @SuppressWarnings("checkstyle:IllegalCatch")
137         @Override
138         protected void doCloseImmediately() {
139             try {
140                 group.shutdownNow();
141                 group.awaitTermination(5, TimeUnit.SECONDS);
142             } catch (final Exception e) {
143                 log.debug("Exception caught while closing channel group", e);
144             } finally {
145                 super.doCloseImmediately();
146             }
147         }
148
149         private static final class NioServiceWithPoolFactoryFactory extends Nio2ServiceFactoryFactory {
150
151             private final ExecutorService nioExecutor;
152
153             private NioServiceWithPoolFactoryFactory(final ExecutorService nioExecutor) {
154                 this.nioExecutor = nioExecutor;
155             }
156
157             @Override
158             public IoServiceFactory create(final FactoryManager manager) {
159                 return new NioServiceWithPoolFactory(manager, nioExecutor);
160             }
161         }
162     }
163
164 }