2 * Copyright (c) 2017 Cisco and/or its affiliates.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package io.fd.honeycomb.northbound.netconf;
19 import com.google.common.net.InetAddresses;
20 import com.google.common.util.concurrent.ThreadFactoryBuilder;
21 import com.google.inject.Inject;
22 import io.fd.honeycomb.binding.init.ProviderTrait;
23 import io.fd.honeycomb.infra.distro.InitializationException;
24 import io.fd.honeycomb.northbound.CredentialsConfiguration;
25 import io.fd.honeycomb.northbound.NetconfConfiguration;
26 import io.netty.channel.ChannelFuture;
27 import io.netty.channel.local.LocalAddress;
28 import io.netty.channel.nio.NioEventLoopGroup;
29 import io.netty.util.concurrent.GenericFutureListener;
30 import io.netty.util.concurrent.GlobalEventExecutor;
31 import java.io.IOException;
32 import java.net.InetAddress;
33 import java.net.InetSocketAddress;
34 import java.util.concurrent.Executors;
35 import java.util.concurrent.ScheduledExecutorService;
36 import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
37 import org.opendaylight.netconf.api.NetconfServerDispatcher;
38 import org.opendaylight.netconf.auth.AuthProvider;
39 import org.opendaylight.netconf.ssh.SshProxyServer;
40 import org.opendaylight.netconf.ssh.SshProxyServerConfigurationBuilder;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
45 public final class NetconfSshServerProvider extends ProviderTrait<NetconfSshServerProvider.NetconfSshServer> {
47 private static final Logger LOG = LoggerFactory.getLogger(NetconfSshServerProvider.class);
49 // Use RSA for ssh server, see https://git.opendaylight.org/gerrit/#/c/60138/
50 private static final String DEFAULT_PRIVATE_KEY_PATH = null; // disable private key serialization
51 private static final String DEFAULT_ALGORITHM = "RSA";
52 private static final int DEFAULT_KEY_SIZE = 4096;
55 private NetconfServerDispatcher dispatcher;
57 private NetconfConfiguration cfgAttributes;
59 private NioEventLoopGroup nettyThreadgroup;
61 private CredentialsConfiguration credentialsCfg;
63 private ScheduledExecutorService pool =
64 Executors.newScheduledThreadPool(1, new ThreadFactoryBuilder().setNameFormat("netconf-ssh-%d").build());
67 protected NetconfSshServer create() {
68 if (!cfgAttributes.isNetconfSshEnabled()) {
69 LOG.info("NETCONF SSH disabled, skipping initialization");
72 LOG.info("Starting NETCONF SSH");
74 // TODO(HONEYCOMB-414): the logic below is very similar to
75 // org.opendaylight.netconf.ssh.NetconfNorthboundSshServer (introduced in Carbon), so consider reusing it
76 // (requires fixing hardcoded private key path).
77 final InetAddress sshBindingAddress = InetAddresses.forString(cfgAttributes.netconfSshBindingAddress.get());
78 final InetSocketAddress bindingAddress =
79 new InetSocketAddress(sshBindingAddress, cfgAttributes.netconfSshBindingPort.get());
81 LocalAddress localAddress = new LocalAddress(cfgAttributes.netconfSshBindingPort.toString());
82 ChannelFuture localServer = dispatcher.createLocalServer(localAddress);
84 final SshProxyServer sshProxyServer = new SshProxyServer(pool, nettyThreadgroup, GlobalEventExecutor.INSTANCE);
86 final SshProxyServerConfigurationBuilder sshConfigBuilder = new SshProxyServerConfigurationBuilder();
87 sshConfigBuilder.setBindingAddress(bindingAddress);
88 sshConfigBuilder.setLocalAddress(localAddress);
89 // Only simple authProvider checking ConfigAttributes, checking the config file
90 sshConfigBuilder.setAuthenticator(new SimplelAuthProvider(credentialsCfg));
91 sshConfigBuilder.setIdleTimeout(Integer.MAX_VALUE);
92 sshConfigBuilder.setKeyPairProvider(new PEMGeneratorHostKeyProvider(DEFAULT_PRIVATE_KEY_PATH,
93 DEFAULT_ALGORITHM, DEFAULT_KEY_SIZE));
95 localServer.addListener(new SshServerBinder(sshProxyServer, sshConfigBuilder, bindingAddress));
97 return new NetconfSshServer(localServer, sshProxyServer);
100 public static final class NetconfSshServer {
101 private ChannelFuture localServer;
102 private SshProxyServer sshProxyServer;
104 NetconfSshServer(final ChannelFuture localServer,
105 final SshProxyServer sshProxyServer) {
106 this.localServer = localServer;
107 this.sshProxyServer = sshProxyServer;
110 public Object getLocalServer() {
114 public Object getSshProxyServer() {
115 return sshProxyServer;
119 private static final class SimplelAuthProvider implements AuthProvider {
121 private final CredentialsConfiguration cfgAttributes;
123 SimplelAuthProvider(final CredentialsConfiguration cfgAttributes) {
124 this.cfgAttributes = cfgAttributes;
128 public boolean authenticated(final String uname, final String passwd) {
129 return cfgAttributes.username.equals(uname) && cfgAttributes.password.equals(passwd);
133 private static final class SshServerBinder implements GenericFutureListener<ChannelFuture> {
134 private final SshProxyServer sshProxyServer;
135 private final SshProxyServerConfigurationBuilder sshConfigBuilder;
136 private final InetSocketAddress bindingAddress;
138 SshServerBinder(final SshProxyServer sshProxyServer,
139 final SshProxyServerConfigurationBuilder sshConfigBuilder,
140 final InetSocketAddress bindingAddress) {
141 this.sshProxyServer = sshProxyServer;
142 this.sshConfigBuilder = sshConfigBuilder;
143 this.bindingAddress = bindingAddress;
147 public void operationComplete(final ChannelFuture future) {
148 if (future.isDone() && !future.isCancelled()) {
150 sshProxyServer.bind(sshConfigBuilder.createSshProxyServerConfiguration());
151 LOG.info("Netconf SSH endpoint started successfully at {}", bindingAddress);
152 } catch (final IOException e) {
153 throw new InitializationException("Unable to start SSH netconf server", e);
157 LOG.warn("Unable to start SSH netconf server at {}", bindingAddress, future.cause());
158 throw new InitializationException("Unable to start SSH netconf server", future.cause());