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