X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=netconf%2Fcallhome-protocol%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Fcallhome%2Fprotocol%2FNetconfCallHomeServer.java;h=8feecf2506db7aa60c3b5255f01c186b6c4c80c1;hb=31560e352e65522fdbc64c2f863f90628d6ef772;hp=460c742245326231f3ae02f5263c0dccc08b18d0;hpb=6c7dc98ecbf088276bdff9b9fadb0e1236711f8a;p=netconf.git diff --git a/netconf/callhome-protocol/src/main/java/org/opendaylight/netconf/callhome/protocol/NetconfCallHomeServer.java b/netconf/callhome-protocol/src/main/java/org/opendaylight/netconf/callhome/protocol/NetconfCallHomeServer.java index 460c742245..8feecf2506 100644 --- a/netconf/callhome-protocol/src/main/java/org/opendaylight/netconf/callhome/protocol/NetconfCallHomeServer.java +++ b/netconf/callhome-protocol/src/main/java/org/opendaylight/netconf/callhome/protocol/NetconfCallHomeServer.java @@ -5,84 +5,84 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ - package org.opendaylight.netconf.callhome.protocol; -import com.google.common.base.Preconditions; +import static java.util.Objects.requireNonNull; + +import com.google.common.annotations.VisibleForTesting; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.security.PublicKey; -import org.apache.sshd.ClientSession; -import org.apache.sshd.SshClient; -import org.apache.sshd.client.ServerKeyVerifier; -import org.apache.sshd.client.SessionFactory; -import org.apache.sshd.client.future.AuthFuture; -import org.apache.sshd.common.Session; -import org.apache.sshd.common.SessionListener; -import org.apache.sshd.common.future.SshFutureListener; -import org.apache.sshd.common.io.IoAcceptor; -import org.apache.sshd.common.io.IoServiceFactory; -import org.apache.sshd.common.io.mina.MinaServiceFactory; -import org.apache.sshd.common.io.nio2.Nio2ServiceFactory; import org.opendaylight.netconf.callhome.protocol.CallHomeSessionContext.Factory; +import org.opendaylight.netconf.shaded.sshd.client.SshClient; +import org.opendaylight.netconf.shaded.sshd.client.future.AuthFuture; +import org.opendaylight.netconf.shaded.sshd.client.keyverifier.ServerKeyVerifier; +import org.opendaylight.netconf.shaded.sshd.client.session.ClientSession; +import org.opendaylight.netconf.shaded.sshd.client.session.SessionFactory; +import org.opendaylight.netconf.shaded.sshd.common.future.SshFutureListener; +import org.opendaylight.netconf.shaded.sshd.common.io.IoAcceptor; +import org.opendaylight.netconf.shaded.sshd.common.io.IoServiceFactory; +import org.opendaylight.netconf.shaded.sshd.common.session.Session; +import org.opendaylight.netconf.shaded.sshd.common.session.SessionListener; +import org.opendaylight.netconf.shaded.sshd.netty.NettyIoServiceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class NetconfCallHomeServer implements AutoCloseable, ServerKeyVerifier { - +public final class NetconfCallHomeServer implements AutoCloseable, ServerKeyVerifier { private static final Logger LOG = LoggerFactory.getLogger(NetconfCallHomeServer.class); - private final IoAcceptor acceptor; - private final SshClient client; private final CallHomeAuthorizationProvider authProvider; - private final CallHomeSessionContext.Factory sessionFactory; + private final IoServiceFactory serviceFactory; private final InetSocketAddress bindAddress; + private final StatusRecorder recorder; + private final Factory sessionFactory; + private final IoAcceptor acceptor; + private final SshClient client; - NetconfCallHomeServer(SshClient sshClient, CallHomeAuthorizationProvider authProvider, Factory factory, - InetSocketAddress socketAddress) { - this.client = Preconditions.checkNotNull(sshClient); - this.authProvider = Preconditions.checkNotNull(authProvider); - this.sessionFactory = Preconditions.checkNotNull(factory); - this.bindAddress = socketAddress; - - sshClient.setServerKeyVerifier(this); + NetconfCallHomeServer(final SshClient sshClient, final CallHomeAuthorizationProvider authProvider, + final Factory factory, final InetSocketAddress socketAddress, final StatusRecorder recorder) { + this(sshClient, authProvider, factory, socketAddress, recorder, + new NettyIoServiceFactory(factory.getNettyGroup())); + } - SessionFactory clientSessions = new SessionFactory(); - clientSessions.setClient(sshClient); - clientSessions.addListener(createSessionListener()); + @VisibleForTesting + NetconfCallHomeServer(final SshClient sshClient, final CallHomeAuthorizationProvider authProvider, + final Factory factory, final InetSocketAddress socketAddress, final StatusRecorder recorder, + final IoServiceFactory serviceFactory) { + client = requireNonNull(sshClient); + this.authProvider = requireNonNull(authProvider); + sessionFactory = requireNonNull(factory); + bindAddress = socketAddress; + this.recorder = recorder; + this.serviceFactory = requireNonNull(serviceFactory); - IoServiceFactory minaFactory = createServiceFactory(sshClient); - this.acceptor = minaFactory.createAcceptor(clientSessions); - } + sshClient.setServerKeyVerifier(this); + sshClient.addSessionListener(createSessionListener()); - IoServiceFactory createServiceFactory(SshClient sshClient) { - try { - return createMinaServiceFactory(sshClient); - } catch (NoClassDefFoundError e) { - LOG.warn("Mina is not available, defaulting to NIO."); - return new Nio2ServiceFactory(sshClient); - } + acceptor = serviceFactory.createAcceptor(new SessionFactory(sshClient)); } - - protected IoServiceFactory createMinaServiceFactory(SshClient sshClient) { - return new MinaServiceFactory(sshClient); + @VisibleForTesting + SshClient getClient() { + return client; } SessionListener createSessionListener() { return new SessionListener() { - @Override - public void sessionEvent(Session session, Event event) { - ClientSession cSession = (ClientSession) session; + public void sessionEvent(final Session session, final Event event) { + ClientSession clientSession = (ClientSession) session; LOG.debug("SSH session {} event {}", session, event); switch (event) { case KeyEstablished: - doAuth(cSession); + // Case of key re-exchange - if session is once authenticated, it does not need to be made again + if (!clientSession.isAuthenticated()) { + doAuth(clientSession); + } break; case Authenticated: - doPostAuth(cSession); + CallHomeSessionContext.getFrom(clientSession).openNetconfChannel(); break; default: break; @@ -90,38 +90,36 @@ public class NetconfCallHomeServer implements AutoCloseable, ServerKeyVerifier { } @Override - public void sessionCreated(Session session) { + public void sessionCreated(final Session session) { LOG.debug("SSH session {} created", session); } @Override - public void sessionClosed(Session session) { + public void sessionClosed(final Session session) { CallHomeSessionContext ctx = CallHomeSessionContext.getFrom((ClientSession) session); - if(ctx != null) { + if (ctx != null) { ctx.removeSelf(); } LOG.debug("SSH Session {} closed", session); } - }; - } - private void doPostAuth(final ClientSession cSession) { - CallHomeSessionContext.getFrom(cSession).openNetconfChannel(); + private void doAuth(final ClientSession session) { + try { + final AuthFuture authFuture = CallHomeSessionContext.getFrom(session).authorize(); + authFuture.addListener(newAuthSshFutureListener(session)); + } catch (IOException e) { + LOG.error("Failed to authorize session {}", session, e); + } + } + }; } - private void doAuth(final ClientSession cSession) { - try { - final AuthFuture authFuture = CallHomeSessionContext.getFrom(cSession).authorize(); - authFuture.addListener(newAuthSshFutureListener(cSession)); - } catch (IOException e) { - LOG.error("Failed to authorize session {}", cSession, e); - } - } + private SshFutureListener newAuthSshFutureListener(final ClientSession session) { + final PublicKey serverKey = session.getServerKey(); - SshFutureListener newAuthSshFutureListener(final ClientSession cSession) { - return new SshFutureListener() { + return new SshFutureListener<>() { @Override - public void operationComplete(AuthFuture authFuture) { + public void operationComplete(final AuthFuture authFuture) { if (authFuture.isSuccess()) { onSuccess(); } else if (authFuture.isFailure()) { @@ -136,34 +134,39 @@ public class NetconfCallHomeServer implements AutoCloseable, ServerKeyVerifier { LOG.debug("Authorize success"); } - private void onFailure(Throwable throwable) { - LOG.error("Failed to authorize session {}", cSession, throwable); - cSession.close(true); + private void onFailure(final Throwable throwable) { + LOG.error("Authorize failed for session {}", session, throwable); + recorder.reportFailedAuth(serverKey); + session.close(true); } private void onCanceled() { LOG.warn("Authorize cancelled"); - cSession.close(true); + session.close(true); } }; } @Override - public boolean verifyServerKey(ClientSession sshClientSession, SocketAddress remoteAddress, PublicKey serverKey) { + public boolean verifyServerKey(final ClientSession sshClientSession, final SocketAddress remoteAddress, + final PublicKey serverKey) { final CallHomeAuthorization authorization = authProvider.provideAuth(remoteAddress, serverKey); - // server is not authorized if (!authorization.isServerAllowed()) { - LOG.info("Incoming session {} was rejected by Authorization Provider.",sshClientSession); + // server is not authorized + LOG.info("Incoming session {} was rejected by Authorization Provider.", sshClientSession); return false; } - CallHomeSessionContext session = sessionFactory.createIfNotExists(sshClientSession, authorization, remoteAddress); - // Session was created, session with same name does not exists - if(session != null) { - return true; + + if (sessionFactory.createIfNotExists(sshClientSession, authorization, remoteAddress) == null) { + // Session was not created, session with same name exists + LOG.info("Incoming session {} was rejected. Session with same name {} is already active.", sshClientSession, + authorization.getSessionName()); + return false; } - // Session was not created, session with same name exists - LOG.info("Incoming session {} was rejected. Session with same name {} is already active.",sshClientSession,authorization.getSessionName()); - return false; + + // Session was created, session with same name does not exist + LOG.debug("Incoming session {} was successfully verified.", sshClientSession); + return true; } public void bind() throws IOException { @@ -171,14 +174,14 @@ public class NetconfCallHomeServer implements AutoCloseable, ServerKeyVerifier { client.start(); acceptor.bind(bindAddress); } catch (IOException e) { - LOG.error("Unable to start NETCONF CallHome Service", e); + LOG.error("Unable to start NETCONF CallHome Service on {}", bindAddress, e); throw e; } } - @Override - public void close() throws Exception { + public void close() { acceptor.close(true); + serviceFactory.close(true); } }