From 9a6fe4a4d5dad241ad85677fd687aa2d3d423043 Mon Sep 17 00:00:00 2001 From: David Ostrovsky Date: Sat, 22 Feb 2014 17:03:37 +0100 Subject: [PATCH] Factor out methods from SshCommandServer to SshDaemon Change-Id: I02d545a8ab68b06d733ad8e7ed476767a34aa244 --- .../com/gitblit/manager/ServicesManager.java | 55 +++- .../transport/ssh/SshCommandServer.java | 225 -------------- .../com/gitblit/transport/ssh/SshDaemon.java | 288 +++++++++++++----- 3 files changed, 259 insertions(+), 309 deletions(-) delete mode 100644 src/main/java/com/gitblit/transport/ssh/SshCommandServer.java diff --git a/src/main/java/com/gitblit/manager/ServicesManager.java b/src/main/java/com/gitblit/manager/ServicesManager.java index 2cd583ad..219e4ea5 100644 --- a/src/main/java/com/gitblit/manager/ServicesManager.java +++ b/src/main/java/com/gitblit/manager/ServicesManager.java @@ -24,8 +24,13 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import javax.inject.Named; +import javax.inject.Singleton; import javax.servlet.http.HttpServletRequest; +import org.apache.sshd.server.Command; +import org.eclipse.jgit.transport.resolver.ReceivePackFactory; +import org.eclipse.jgit.transport.resolver.UploadPackFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,14 +43,26 @@ import com.gitblit.fanout.FanoutNioService; import com.gitblit.fanout.FanoutService; import com.gitblit.fanout.FanoutSocketService; import com.gitblit.git.GitDaemon; +import com.gitblit.git.GitblitReceivePackFactory; +import com.gitblit.git.GitblitUploadPackFactory; +import com.gitblit.git.RepositoryResolver; import com.gitblit.models.FederationModel; import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; import com.gitblit.service.FederationPullService; +import com.gitblit.transport.ssh.SshCommandFactory; import com.gitblit.transport.ssh.SshDaemon; +import com.gitblit.transport.ssh.SshSession; +import com.gitblit.transport.ssh.commands.CreateRepository; +import com.gitblit.transport.ssh.commands.VersionCommand; +import com.gitblit.utils.IdGenerator; import com.gitblit.utils.StringUtils; import com.gitblit.utils.TimeUtils; +import dagger.Module; +import dagger.ObjectGraph; +import dagger.Provides; + /** * Services manager manages long-running services/processes that either have no * direct relation to other managers OR require really high-level manager @@ -147,7 +164,7 @@ public class ServicesManager implements IManager { String bindInterface = settings.getString(Keys.git.sshBindInterface, "localhost"); if (port > 0) { try { - sshDaemon = new SshDaemon(gitblit); + sshDaemon = ObjectGraph.create(new SshModule()).get(SshDaemon.class); sshDaemon.start(); } catch (IOException e) { sshDaemon = null; @@ -245,4 +262,40 @@ public class ServicesManager implements IManager { } } + + @Module(library = true, + injects = { + IGitblit.class, + SshCommandFactory.class, + SshDaemon.class, + }) + public class SshModule { + @Provides @Named("create-repository") Command provideCreateRepository() { + return new CreateRepository(); + } + + @Provides @Named("version") Command provideVersion() { + return new VersionCommand(); + } + + @Provides @Singleton IdGenerator provideIdGenerator() { + return new IdGenerator(); + } + + @Provides @Singleton RepositoryResolver provideRepositoryResolver() { + return new RepositoryResolver(provideGitblit()); + } + + @Provides @Singleton UploadPackFactory provideUploadPackFactory() { + return new GitblitUploadPackFactory(provideGitblit()); + } + + @Provides @Singleton ReceivePackFactory provideReceivePackFactory() { + return new GitblitReceivePackFactory(provideGitblit()); + } + + @Provides @Singleton IGitblit provideGitblit() { + return ServicesManager.this.gitblit; + } + } } diff --git a/src/main/java/com/gitblit/transport/ssh/SshCommandServer.java b/src/main/java/com/gitblit/transport/ssh/SshCommandServer.java deleted file mode 100644 index 7186737f..00000000 --- a/src/main/java/com/gitblit/transport/ssh/SshCommandServer.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright 2014 gitblit.com. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.gitblit.transport.ssh; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.security.InvalidKeyException; -import java.util.Arrays; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -import javax.inject.Inject; - -import org.apache.mina.core.future.IoFuture; -import org.apache.mina.core.future.IoFutureListener; -import org.apache.mina.core.session.IoSession; -import org.apache.mina.transport.socket.SocketSessionConfig; -import org.apache.sshd.SshServer; -import org.apache.sshd.common.Channel; -import org.apache.sshd.common.Cipher; -import org.apache.sshd.common.Compression; -import org.apache.sshd.common.KeyExchange; -import org.apache.sshd.common.KeyPairProvider; -import org.apache.sshd.common.Mac; -import org.apache.sshd.common.NamedFactory; -import org.apache.sshd.common.Session; -import org.apache.sshd.common.Signature; -import org.apache.sshd.common.cipher.AES128CBC; -import org.apache.sshd.common.cipher.AES192CBC; -import org.apache.sshd.common.cipher.AES256CBC; -import org.apache.sshd.common.cipher.BlowfishCBC; -import org.apache.sshd.common.cipher.TripleDESCBC; -import org.apache.sshd.common.compression.CompressionNone; -import org.apache.sshd.common.mac.HMACMD5; -import org.apache.sshd.common.mac.HMACMD596; -import org.apache.sshd.common.mac.HMACSHA1; -import org.apache.sshd.common.mac.HMACSHA196; -import org.apache.sshd.common.random.BouncyCastleRandom; -import org.apache.sshd.common.random.SingletonRandomFactory; -import org.apache.sshd.common.signature.SignatureDSA; -import org.apache.sshd.common.signature.SignatureRSA; -import org.apache.sshd.common.util.SecurityUtils; -import org.apache.sshd.server.CommandFactory; -import org.apache.sshd.server.FileSystemFactory; -import org.apache.sshd.server.FileSystemView; -import org.apache.sshd.server.ForwardingFilter; -import org.apache.sshd.server.PublickeyAuthenticator; -import org.apache.sshd.server.SshFile; -import org.apache.sshd.server.UserAuth; -import org.apache.sshd.server.auth.UserAuthPublicKey; -import org.apache.sshd.server.channel.ChannelDirectTcpip; -import org.apache.sshd.server.channel.ChannelSession; -import org.apache.sshd.server.kex.DHG1; -import org.apache.sshd.server.kex.DHG14; -import org.apache.sshd.server.session.ServerSession; -import org.apache.sshd.server.session.SessionFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.gitblit.utils.IdGenerator; - -/** - * - * @author Eric Myhre - * - */ -public class SshCommandServer extends SshServer { - - private static final Logger log = LoggerFactory.getLogger(SshCommandServer.class); - - @Inject - public SshCommandServer(final IdGenerator idGenerator) { - setSessionFactory(new SessionFactory() { - @Override - protected ServerSession createSession(final IoSession io) throws Exception { - log.info("connection accepted on " + io); - - if (io.getConfig() instanceof SocketSessionConfig) { - final SocketSessionConfig c = (SocketSessionConfig) io.getConfig(); - c.setKeepAlive(true); - } - - final ServerSession s = (ServerSession) super.createSession(io); - SocketAddress peer = io.getRemoteAddress(); - SshSession session = new SshSession(idGenerator.next(), peer); - s.setAttribute(SshSession.KEY, session); - - io.getCloseFuture().addListener(new IoFutureListener() { - @Override - public void operationComplete(IoFuture future) { - log.info("connection closed on " + io); - } - }); - return s; - } - }); - } - - /** - * Performs most of default configuration (setup random sources, setup ciphers, - * etc; also, support for forwarding and filesystem is explicitly disallowed). - * - * {@link #setKeyPairProvider(KeyPairProvider)} and - * {@link #setPublickeyAuthenticator(PublickeyAuthenticator)} are left for you. - * And applying {@link #setCommandFactory(CommandFactory)} is probably wise if you - * want something to actually happen when users do successfully authenticate. - */ - @SuppressWarnings("unchecked") - public void setup() { - if (!SecurityUtils.isBouncyCastleRegistered()) - throw new RuntimeException("BC crypto not available"); - - setKeyExchangeFactories(Arrays.>asList( - new DHG14.Factory(), - new DHG1.Factory()) - ); - - setRandomFactory(new SingletonRandomFactory(new BouncyCastleRandom.Factory())); - - setupCiphers(); - - setCompressionFactories(Arrays.>asList( - new CompressionNone.Factory()) - ); - - setMacFactories(Arrays.>asList( - new HMACMD5.Factory(), - new HMACSHA1.Factory(), - new HMACMD596.Factory(), - new HMACSHA196.Factory()) - ); - - setChannelFactories(Arrays.>asList( - new ChannelSession.Factory(), - new ChannelDirectTcpip.Factory()) - ); - - setSignatureFactories(Arrays.>asList( - new SignatureDSA.Factory(), - new SignatureRSA.Factory()) - ); - - setFileSystemFactory(new FileSystemFactory() { - @Override - public FileSystemView createFileSystemView(Session session) throws IOException { - return new FileSystemView() { - @Override - public SshFile getFile(SshFile baseDir, String file) { - return null; - } - - @Override - public SshFile getFile(String file) { - return null; - } - }; - } - }); - - setForwardingFilter(new ForwardingFilter() { - @Override - public boolean canForwardAgent(ServerSession session) { - return false; - } - - @Override - public boolean canForwardX11(ServerSession session) { - return false; - } - - @Override - public boolean canConnect(InetSocketAddress address, ServerSession session) { - return false; - } - - @Override - public boolean canListen(InetSocketAddress address, ServerSession session) { - return false; - } - }); - - setUserAuthFactories(Arrays.>asList( - new UserAuthPublicKey.Factory()) - ); - } - - protected void setupCiphers() { - List> avail = new LinkedList>(); - avail.add(new AES128CBC.Factory()); - avail.add(new TripleDESCBC.Factory()); - avail.add(new BlowfishCBC.Factory()); - avail.add(new AES192CBC.Factory()); - avail.add(new AES256CBC.Factory()); - - for (Iterator> i = avail.iterator(); i.hasNext();) { - final NamedFactory f = i.next(); - try { - final Cipher c = f.create(); - final byte[] key = new byte[c.getBlockSize()]; - final byte[] iv = new byte[c.getIVSize()]; - c.init(Cipher.Mode.Encrypt, key, iv); - } catch (InvalidKeyException e) { - i.remove(); - } catch (Exception e) { - i.remove(); - } - } - setCipherFactories(avail); - } -} diff --git a/src/main/java/com/gitblit/transport/ssh/SshDaemon.java b/src/main/java/com/gitblit/transport/ssh/SshDaemon.java index 056735a1..3c3ef3f2 100644 --- a/src/main/java/com/gitblit/transport/ssh/SshDaemon.java +++ b/src/main/java/com/gitblit/transport/ssh/SshDaemon.java @@ -18,35 +18,71 @@ package com.gitblit.transport.ssh; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.security.InvalidKeyException; import java.text.MessageFormat; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; -import javax.inject.Named; -import javax.inject.Singleton; - -import org.apache.sshd.server.Command; +import javax.inject.Inject; + +import org.apache.mina.core.future.IoFuture; +import org.apache.mina.core.future.IoFutureListener; +import org.apache.mina.core.session.IoSession; +import org.apache.mina.transport.socket.SocketSessionConfig; +import org.apache.sshd.SshServer; +import org.apache.sshd.common.Channel; +import org.apache.sshd.common.Cipher; +import org.apache.sshd.common.Compression; +import org.apache.sshd.common.KeyExchange; +import org.apache.sshd.common.KeyPairProvider; +import org.apache.sshd.common.Mac; +import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.Session; +import org.apache.sshd.common.Signature; +import org.apache.sshd.common.cipher.AES128CBC; +import org.apache.sshd.common.cipher.AES192CBC; +import org.apache.sshd.common.cipher.AES256CBC; +import org.apache.sshd.common.cipher.BlowfishCBC; +import org.apache.sshd.common.cipher.TripleDESCBC; +import org.apache.sshd.common.compression.CompressionNone; +import org.apache.sshd.common.mac.HMACMD5; +import org.apache.sshd.common.mac.HMACMD596; +import org.apache.sshd.common.mac.HMACSHA1; +import org.apache.sshd.common.mac.HMACSHA196; +import org.apache.sshd.common.random.BouncyCastleRandom; +import org.apache.sshd.common.random.SingletonRandomFactory; +import org.apache.sshd.common.signature.SignatureDSA; +import org.apache.sshd.common.signature.SignatureRSA; +import org.apache.sshd.common.util.SecurityUtils; +import org.apache.sshd.server.CommandFactory; +import org.apache.sshd.server.FileSystemFactory; +import org.apache.sshd.server.FileSystemView; +import org.apache.sshd.server.ForwardingFilter; +import org.apache.sshd.server.PublickeyAuthenticator; +import org.apache.sshd.server.SshFile; +import org.apache.sshd.server.UserAuth; +import org.apache.sshd.server.auth.UserAuthPublicKey; +import org.apache.sshd.server.channel.ChannelDirectTcpip; +import org.apache.sshd.server.channel.ChannelSession; +import org.apache.sshd.server.kex.DHG1; +import org.apache.sshd.server.kex.DHG14; import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider; +import org.apache.sshd.server.session.ServerSession; +import org.apache.sshd.server.session.SessionFactory; import org.eclipse.jgit.internal.JGitText; -import org.eclipse.jgit.transport.resolver.ReceivePackFactory; -import org.eclipse.jgit.transport.resolver.UploadPackFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.gitblit.IStoredSettings; import com.gitblit.Keys; -import com.gitblit.git.GitblitReceivePackFactory; -import com.gitblit.git.GitblitUploadPackFactory; -import com.gitblit.git.RepositoryResolver; import com.gitblit.manager.IGitblit; -import com.gitblit.transport.ssh.commands.CreateRepository; -import com.gitblit.transport.ssh.commands.VersionCommand; import com.gitblit.utils.IdGenerator; import com.gitblit.utils.StringUtils; -import dagger.Module; -import dagger.ObjectGraph; -import dagger.Provides; - /** * Manager for the ssh transport. Roughly analogous to the * {@link com.gitblit.git.GitDaemon} class. @@ -54,9 +90,9 @@ import dagger.Provides; * @author Eric Myhre * */ -public class SshDaemon { +public class SshDaemon extends SshServer { - private final Logger logger = LoggerFactory.getLogger(SshDaemon.class); + private final Logger log = LoggerFactory.getLogger(SshDaemon.class); /** * 22: IANA assigned port number for ssh. Note that this is a distinct concept @@ -71,17 +107,16 @@ public class SshDaemon { private AtomicBoolean run; - private SshCommandServer sshd; - - private IGitblit gitblit; + @SuppressWarnings("unused") + private IGitblit gitblit; /** * Construct the Gitblit SSH daemon. * * @param gitblit */ - public SshDaemon(IGitblit gitblit) { - + @Inject + SshDaemon(IGitblit gitblit, IdGenerator idGenerator, SshCommandFactory factory) { this.gitblit = gitblit; IStoredSettings settings = gitblit.getSettings(); int port = settings.getInteger(Keys.git.sshPort, 0); @@ -93,17 +128,40 @@ public class SshDaemon { myAddress = new InetSocketAddress(bindInterface, port); } - ObjectGraph graph = ObjectGraph.create(new SshModule()); - sshd = graph.get(SshCommandServer.class); - sshd.setPort(myAddress.getPort()); - sshd.setHost(myAddress.getHostName()); - sshd.setup(); - sshd.setKeyPairProvider(new PEMGeneratorHostKeyProvider(new File(gitblit.getBaseFolder(), HOST_KEY_STORE).getPath())); - sshd.setPublickeyAuthenticator(new SshKeyAuthenticator(gitblit)); + setPort(myAddress.getPort()); + setHost(myAddress.getHostName()); + setup(); + setKeyPairProvider(new PEMGeneratorHostKeyProvider( + new File(gitblit.getBaseFolder(), HOST_KEY_STORE).getPath())); + setPublickeyAuthenticator(new SshKeyAuthenticator(gitblit)); run = new AtomicBoolean(false); - SshCommandFactory f = graph.get(SshCommandFactory.class); - sshd.setCommandFactory(f); + setCommandFactory(factory); + setSessionFactory(newSessionFactory(idGenerator)); + } + + SessionFactory newSessionFactory(final IdGenerator idGenerator) { + return new SessionFactory() { + @Override + protected ServerSession createSession(final IoSession io) throws Exception { + log.info("connection accepted on " + io); + if (io.getConfig() instanceof SocketSessionConfig) { + final SocketSessionConfig c = (SocketSessionConfig) io.getConfig(); + c.setKeepAlive(true); + } + ServerSession s = (ServerSession) super.createSession(io); + SocketAddress peer = io.getRemoteAddress(); + SshSession session = new SshSession(idGenerator.next(), peer); + s.setAttribute(SshSession.KEY, session); + io.getCloseFuture().addListener(new IoFutureListener() { + @Override + public void operationComplete(IoFuture future) { + log.info("connection closed on " + io); + } + }); + return s; + } + }; } public int getPort() { @@ -133,10 +191,10 @@ public class SshDaemon { throw new IllegalStateException(JGitText.get().daemonAlreadyRunning); } - sshd.start(); + super.start(); run.set(true); - logger.info(MessageFormat.format("SSH Daemon is listening on {0}:{1,number,0}", + log.info(MessageFormat.format("SSH Daemon is listening on {0}:{1,number,0}", myAddress.getAddress().getHostAddress(), myAddress.getPort())); } @@ -148,62 +206,126 @@ public class SshDaemon { /** Stop this daemon. */ public synchronized void stop() { if (run.get()) { - logger.info("SSH Daemon stopping..."); + log.info("SSH Daemon stopping..."); run.set(false); try { - sshd.stop(); + super.stop(); } catch (InterruptedException e) { - logger.error("SSH Daemon stop interrupted", e); + log.error("SSH Daemon stop interrupted", e); } } } - @Module(library = true, - injects = { - IGitblit.class, - SshCommandFactory.class, - SshCommandServer.class, - }) - public class SshModule { - @Provides @Named("create-repository") Command provideCreateRepository() { - return new CreateRepository(); - } - - @Provides @Named("version") Command provideVersion() { - return new VersionCommand(); - } - -// @Provides(type=Type.SET) @Named("git") Command provideVersionCommand2() { -// return new CreateRepository(); -// } - -// @Provides @Named("git") DispatchCommand providesGitCommand() { -// return new DispatchCommand("git"); -// } - -// @Provides (type=Type.SET) Provider provideNonCommand() { -// return new SshCommandFactory.NonCommand(); -// } - - @Provides @Singleton IdGenerator provideIdGenerator() { - return new IdGenerator(); - } - - @Provides @Singleton RepositoryResolver provideRepositoryResolver() { - return new RepositoryResolver(provideGitblit()); - } - - @Provides @Singleton UploadPackFactory provideUploadPackFactory() { - return new GitblitUploadPackFactory(provideGitblit()); - } - - @Provides @Singleton ReceivePackFactory provideReceivePackFactory() { - return new GitblitReceivePackFactory(provideGitblit()); - } - - @Provides @Singleton IGitblit provideGitblit() { - return SshDaemon.this.gitblit; - } - } + /** + * Performs most of default configuration (setup random sources, setup ciphers, + * etc; also, support for forwarding and filesystem is explicitly disallowed). + * + * {@link #setKeyPairProvider(KeyPairProvider)} and + * {@link #setPublickeyAuthenticator(PublickeyAuthenticator)} are left for you. + * And applying {@link #setCommandFactory(CommandFactory)} is probably wise if you + * want something to actually happen when users do successfully authenticate. + */ + @SuppressWarnings("unchecked") + public void setup() { + if (!SecurityUtils.isBouncyCastleRegistered()) + throw new RuntimeException("BC crypto not available"); + + setKeyExchangeFactories(Arrays.>asList( + new DHG14.Factory(), + new DHG1.Factory()) + ); + + setRandomFactory(new SingletonRandomFactory(new BouncyCastleRandom.Factory())); + + setupCiphers(); + + setCompressionFactories(Arrays.>asList( + new CompressionNone.Factory()) + ); + + setMacFactories(Arrays.>asList( + new HMACMD5.Factory(), + new HMACSHA1.Factory(), + new HMACMD596.Factory(), + new HMACSHA196.Factory()) + ); + + setChannelFactories(Arrays.>asList( + new ChannelSession.Factory(), + new ChannelDirectTcpip.Factory()) + ); + + setSignatureFactories(Arrays.>asList( + new SignatureDSA.Factory(), + new SignatureRSA.Factory()) + ); + + setFileSystemFactory(new FileSystemFactory() { + @Override + public FileSystemView createFileSystemView(Session session) throws IOException { + return new FileSystemView() { + @Override + public SshFile getFile(SshFile baseDir, String file) { + return null; + } + + @Override + public SshFile getFile(String file) { + return null; + } + }; + } + }); + + setForwardingFilter(new ForwardingFilter() { + @Override + public boolean canForwardAgent(ServerSession session) { + return false; + } + + @Override + public boolean canForwardX11(ServerSession session) { + return false; + } + + @Override + public boolean canConnect(InetSocketAddress address, ServerSession session) { + return false; + } + + @Override + public boolean canListen(InetSocketAddress address, ServerSession session) { + return false; + } + }); + + setUserAuthFactories(Arrays.>asList( + new UserAuthPublicKey.Factory()) + ); + } + + protected void setupCiphers() { + List> avail = new LinkedList>(); + avail.add(new AES128CBC.Factory()); + avail.add(new TripleDESCBC.Factory()); + avail.add(new BlowfishCBC.Factory()); + avail.add(new AES192CBC.Factory()); + avail.add(new AES256CBC.Factory()); + + for (Iterator> i = avail.iterator(); i.hasNext();) { + final NamedFactory f = i.next(); + try { + final Cipher c = f.create(); + final byte[] key = new byte[c.getBlockSize()]; + final byte[] iv = new byte[c.getIVSize()]; + c.init(Cipher.Mode.Encrypt, key, iv); + } catch (InvalidKeyException e) { + i.remove(); + } catch (Exception e) { + i.remove(); + } + } + setCipherFactories(avail); + } } -- 2.39.5