diff options
10 files changed, 177 insertions, 79 deletions
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index 27a0937f63..dd3a6ead55 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt @@ -2269,6 +2269,14 @@ namespace. To alias `replication start` to `gerrit replicate`: [[sshd]] Section sshd ~~~~~~~~~~~~~~~~~~~~~ +[[sshd.backend]]sshd.backend:: ++ +Starting from version 0.9.0 Apache SSHD project added support for NIO2 +IoSession. To use the new NIO2 session the `backend` option must be set +to `NIO2`. ++ +By default, `MINA`. + [[sshd.listenAddress]]sshd.listenAddress:: + Specifies the local addresses the internal SSHD should listen @@ -2307,20 +2315,13 @@ of them. + By default, sshd.listenAddress. -[[sshd.reuseAddress]]sshd.reuseAddress:: -+ -If true, permits the daemon to bind to the port even if the port -is already in use. If false, the daemon ensures the port is not -in use before starting. Busy sites may need to set this to true -to permit fast restarts. -+ -By default, true. - [[sshd.tcpKeepAlive]]sshd.tcpKeepAlive:: + If true, enables TCP keepalive messages to the other side, so the daemon can terminate connections if the peer disappears. + +Only effective when `sshd.backend` is set to `MINA`. ++ By default, true. [[sshd.threads]]sshd.threads:: diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java index 7d0ad24325..63ef2e63f3 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java @@ -198,7 +198,13 @@ import javax.security.auth.login.LoginException; final HashMap<String, String> params = new HashMap<String, String>(); if (account == null) { - account = findAccount(schema, ctx, username); + try { + account = findAccount(schema, ctx, username); + } catch (AccountException e) { + LdapRealm.log.warn("Account " + username + + " not found, assuming empty group membership"); + return Collections.emptySet(); + } } for (String name : schema.groupMemberQueryList.get(0).getParameters()) { params.put(name, account.get(name)); @@ -215,7 +221,13 @@ import javax.security.auth.login.LoginException; if (schema.accountMemberField != null) { if (account == null) { - account = findAccount(schema, ctx, username); + try { + account = findAccount(schema, ctx, username); + } catch (AccountException e) { + LdapRealm.log.warn("Account " + username + + " not found, assuming empty group membership"); + return Collections.emptySet(); + } } final Attribute groupAtt = account.getAll(schema.accountMemberField); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java index a50d33dbd5..85637e0543 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java @@ -469,6 +469,10 @@ public class ProjectControl { Repository repo = repoManager.openRepository(projName); try { for (Entry<String, Ref> entry : repo.getAllRefs().entrySet()) { + String refName = entry.getKey(); + if (!refName.startsWith("refs/heads") && !refName.startsWith("refs/tags")) { + continue; + } RevCommit tip; try { tip = rw.parseCommit(entry.getValue().getObjectId()); diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java index 83bc8a5327..9a2f0d1112 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java @@ -25,8 +25,8 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import org.apache.commons.codec.binary.Base64; -import org.apache.mina.core.future.IoFuture; -import org.apache.mina.core.future.IoFutureListener; +import org.apache.sshd.common.future.CloseFuture; +import org.apache.sshd.common.future.SshFutureListener; import org.apache.sshd.common.KeyPairProvider; import org.apache.sshd.common.SshException; import org.apache.sshd.common.util.Buffer; @@ -178,12 +178,13 @@ class DatabasePubKeyAuth implements PublickeyAuthenticator { sshScope.set(old); } - session.getIoSession().getCloseFuture().addListener( - new IoFutureListener<IoFuture>() { + GerritServerSession s = (GerritServerSession)session; + s.addCloseSessionListener( + new SshFutureListener<CloseFuture>() { @Override - public void operationComplete(IoFuture future) { - final Context ctx = sshScope.newContext(null, sd, null); - final Context old = sshScope.set(ctx); + public void operationComplete(CloseFuture future) { + Context ctx = sshScope.newContext(null, sd, null); + Context old = sshScope.set(ctx); try { sshLog.onLogout(); } finally { diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/GerritServerSession.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/GerritServerSession.java new file mode 100644 index 0000000000..b7f7c22202 --- /dev/null +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/GerritServerSession.java @@ -0,0 +1,34 @@ +// Copyright (C) 2013 The Android Open Source Project +// +// 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.google.gerrit.sshd; + +import org.apache.sshd.common.future.CloseFuture; +import org.apache.sshd.common.future.SshFutureListener; +import org.apache.sshd.common.io.IoSession; +import org.apache.sshd.server.ServerFactoryManager; +import org.apache.sshd.server.session.ServerSession; + +/* Expose addition of close session listeners */ +class GerritServerSession extends ServerSession { + + GerritServerSession(ServerFactoryManager server, + IoSession ioSession) throws Exception { + super(server, ioSession); + } + + void addCloseSessionListener(SshFutureListener<CloseFuture> l) { + closeFuture.addListener(l); + } +} diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java index c43de60d7f..1d03953795 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java @@ -15,10 +15,10 @@ package com.google.gerrit.sshd; import static com.google.gerrit.server.ssh.SshAddressesModule.IANA_SSH_PORT; - import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; +import com.google.common.collect.Lists; import com.google.gerrit.common.Version; import com.google.gerrit.extensions.events.LifecycleListener; import com.google.gerrit.server.config.ConfigUtil; @@ -34,20 +34,18 @@ import com.google.inject.Singleton; import com.jcraft.jsch.HostKey; import com.jcraft.jsch.JSchException; -import org.apache.mina.core.future.IoFuture; -import org.apache.mina.core.future.IoFutureListener; -import org.apache.mina.core.service.IoAcceptor; -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.ForwardingFilter; import org.apache.sshd.common.KeyExchange; import org.apache.sshd.common.KeyPairProvider; import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.Session; import org.apache.sshd.common.Signature; +import org.apache.sshd.common.SshdSocketAddress; import org.apache.sshd.common.cipher.AES128CBC; import org.apache.sshd.common.cipher.AES192CBC; import org.apache.sshd.common.cipher.AES256CBC; @@ -55,6 +53,19 @@ import org.apache.sshd.common.cipher.BlowfishCBC; import org.apache.sshd.common.cipher.CipherNone; import org.apache.sshd.common.cipher.TripleDESCBC; import org.apache.sshd.common.compression.CompressionNone; +import org.apache.sshd.common.file.FileSystemFactory; +import org.apache.sshd.common.file.FileSystemView; +import org.apache.sshd.common.file.SshFile; +import org.apache.sshd.common.forward.DefaultTcpipForwarderFactory; +import org.apache.sshd.common.forward.TcpipServerChannel; +import org.apache.sshd.common.future.CloseFuture; +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.IoSession; +import org.apache.sshd.common.io.mina.MinaServiceFactory; +import org.apache.sshd.common.io.mina.MinaSession; +import org.apache.sshd.common.io.nio2.Nio2ServiceFactory; import org.apache.sshd.common.mac.HMACMD5; import org.apache.sshd.common.mac.HMACMD596; import org.apache.sshd.common.mac.HMACSHA1; @@ -62,32 +73,31 @@ import org.apache.sshd.common.mac.HMACSHA196; import org.apache.sshd.common.random.BouncyCastleRandom; import org.apache.sshd.common.random.JceRandom; import org.apache.sshd.common.random.SingletonRandomFactory; +import org.apache.sshd.common.session.AbstractSession; import org.apache.sshd.common.signature.SignatureDSA; import org.apache.sshd.common.signature.SignatureRSA; import org.apache.sshd.common.util.Buffer; import org.apache.sshd.common.util.SecurityUtils; import org.apache.sshd.server.Command; 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.auth.gss.GSSAuthenticator; +import org.apache.sshd.server.auth.gss.UserAuthGSS; 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.eclipse.jgit.lib.Config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; import java.io.IOException; -import java.net.InetSocketAddress; +import java.net.InetAddress; import java.net.SocketAddress; +import java.net.UnknownHostException; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.PublicKey; @@ -120,6 +130,11 @@ import java.util.List; public class SshDaemon extends SshServer implements SshInfo, LifecycleListener { private static final Logger log = LoggerFactory.getLogger(SshDaemon.class); + public static enum SshSessionBackend { + MINA, + NIO2 + } + private final List<SocketAddress> listen; private final List<String> advertised; private final boolean keepAlive; @@ -137,7 +152,6 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener { this.listen = listen; this.advertised = advertised; - reuseAddress = cfg.getBoolean("sshd", "reuseaddress", true); keepAlive = cfg.getBoolean("sshd", "tcpkeepalive", true); getProperties().put(SERVER_IDENTIFICATION, @@ -154,12 +168,6 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener { long idleTimeoutSeconds = ConfigUtil.getTimeUnit(cfg, "sshd", null, "idleTimeout", 0, SECONDS); - if (idleTimeoutSeconds == 0) { - // Since Apache SSHD does not allow to turn off closing idle connections, - // we fake it by using the highest timeout allowed by Apache SSHD, which - // amounts to ~24 days. - idleTimeoutSeconds = MILLISECONDS.toSeconds(Integer.MAX_VALUE); - } getProperties().put( IDLE_TIMEOUT, String.valueOf(SECONDS.toMillis(idleTimeoutSeconds))); @@ -171,6 +179,14 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener { String.valueOf(maxConnectionsPerUser)); } + SshSessionBackend backend = cfg.getEnum( + "sshd", null, "backend", SshSessionBackend.MINA); + + System.setProperty(IoServiceFactory.class.getName(), + backend == SshSessionBackend.MINA + ? MinaServiceFactory.class.getName() + : Nio2ServiceFactory.class.getName()); + if (SecurityUtils.isBouncyCastleRegistered()) { initProviderBouncyCastle(); } else { @@ -180,7 +196,7 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener { initMacs(cfg); initSignatures(); initChannels(); - initForwardingFilter(); + initForwarding(); initFileSystemFactory(); initSubsystems(); initCompression(); @@ -190,24 +206,28 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener { setShellFactory(noShell); setSessionFactory(new SessionFactory() { @Override - protected ServerSession createSession(final IoSession io) + protected AbstractSession createSession(final IoSession io) throws Exception { - if (io.getConfig() instanceof SocketSessionConfig) { - final SocketSessionConfig c = (SocketSessionConfig) io.getConfig(); - c.setKeepAlive(keepAlive); + if (io instanceof MinaSession) { + if (((MinaSession) io).getSession() + .getConfig() instanceof SocketSessionConfig) { + ((SocketSessionConfig) ((MinaSession) io).getSession() + .getConfig()) + .setKeepAlive(keepAlive); + } } - final ServerSession s = (ServerSession) super.createSession(io); - final int id = idGenerator.next(); - final SocketAddress peer = io.getRemoteAddress(); + GerritServerSession s = (GerritServerSession)super.createSession(io); + int id = idGenerator.next(); + SocketAddress peer = io.getRemoteAddress(); final SshSession sd = new SshSession(id, peer); s.setAttribute(SshSession.KEY, sd); // Log a session close without authentication as a failure. // - io.getCloseFuture().addListener(new IoFutureListener<IoFuture>() { + s.addCloseSessionListener(new SshFutureListener<CloseFuture>() { @Override - public void operationComplete(IoFuture future) { + public void operationComplete(CloseFuture future) { if (sd.isAuthenticationError()) { sshLog.onAuthFail(sd); } @@ -215,6 +235,12 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener { }); return s; } + + @Override + protected AbstractSession doCreateSession(IoSession ioSession) + throws Exception { + return new GerritServerSession(server, ioSession); + } }); hostKeys = computeHostKeys(); @@ -233,13 +259,11 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener { public synchronized void start() { if (acceptor == null && !listen.isEmpty()) { checkConfig(); - + if (sessionFactory == null) { + sessionFactory = createSessionFactory(); + } + sessionFactory.setServer(this); acceptor = createAcceptor(); - configure(acceptor); - - final SessionFactory handler = getSessionFactory(); - handler.setServer(this); - acceptor.setHandler(handler); try { acceptor.bind(listen); @@ -247,7 +271,8 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener { throw new IllegalStateException("Cannot bind to " + addressList(), e); } - log.info("Started Gerrit SSHD on " + addressList()); + log.info(String.format("Started Gerrit %s on %s", + version, addressList())); } } @@ -461,7 +486,7 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener { private void initChannels() { setChannelFactories(Arrays.<NamedFactory<Channel>> asList( new ChannelSession.Factory(), // - new ChannelDirectTcpip.Factory() // + new TcpipServerChannel.DirectTcpipFactory() // )); } @@ -476,28 +501,29 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener { setPublickeyAuthenticator(pubkey); } - private void initForwardingFilter() { - setForwardingFilter(new ForwardingFilter() { + private void initForwarding() { + setTcpipForwardingFilter(new ForwardingFilter() { @Override - public boolean canForwardAgent(ServerSession session) { - return false; + public boolean canForwardAgent(Session session) { + return false; } @Override - public boolean canForwardX11(ServerSession session) { - return false; + public boolean canForwardX11(Session session) { + return false; } @Override - public boolean canConnect(InetSocketAddress address, ServerSession session) { - return false; + public boolean canListen(SshdSocketAddress address, Session session) { + return false; } @Override - public boolean canListen(InetSocketAddress address, ServerSession session) { - return false; + public boolean canConnect(SshdSocketAddress address, Session session) { + return false; } }); + setTcpipForwarderFactory(new DefaultTcpipForwarderFactory()); } private void initFileSystemFactory() { diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java index 3366841706..fbc0e75dd4 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java @@ -30,8 +30,9 @@ import com.google.gerrit.sshd.SshDaemon; import com.google.inject.Inject; import com.google.inject.Provider; -import org.apache.mina.core.service.IoAcceptor; -import org.apache.mina.core.session.IoSession; +import org.apache.sshd.common.io.IoAcceptor; +import org.apache.sshd.common.io.IoSession; +import org.apache.sshd.common.io.mina.MinaSession; import org.apache.sshd.server.Environment; import org.eclipse.jgit.internal.storage.file.WindowCacheStatAccessor; import org.kohsuke.args4j.Option; @@ -275,8 +276,12 @@ final class ShowCaches extends CacheCommand { long now = System.currentTimeMillis(); Collection<IoSession> list = acceptor.getManagedSessions().values(); long oldest = now; + for (IoSession s : list) { - oldest = Math.min(oldest, s.getCreationTime()); + if (s instanceof MinaSession) { + MinaSession minaSession = (MinaSession)s; + oldest = Math.min(oldest, minaSession.getSession().getCreationTime()); + } } stdout.format( diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java index 1c3d8282e7..47ec5e4260 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java @@ -25,8 +25,10 @@ import com.google.gerrit.sshd.SshDaemon; import com.google.gerrit.sshd.SshSession; import com.google.inject.Inject; -import org.apache.mina.core.service.IoAcceptor; -import org.apache.mina.core.session.IoSession; +import org.apache.sshd.common.io.IoAcceptor; +import org.apache.sshd.common.io.IoSession; +import org.apache.sshd.common.io.mina.MinaSession; +import org.apache.sshd.server.Environment; import org.apache.sshd.server.session.ServerSession; import org.kohsuke.args4j.Option; @@ -62,10 +64,16 @@ final class ShowConnections extends SshCommand { Collections.sort(list, new Comparator<IoSession>() { @Override public int compare(IoSession arg0, IoSession arg1) { - if (arg0.getCreationTime() < arg1.getCreationTime()) { - return -1; - } else if (arg0.getCreationTime() > arg1.getCreationTime()) { - return 1; + if (arg0 instanceof MinaSession) { + MinaSession mArg0 = (MinaSession) arg0; + MinaSession mArg1 = (MinaSession) arg1; + if (mArg0.getSession().getCreationTime() < mArg1.getSession() + .getCreationTime()) { + return -1; + } else if (mArg0.getSession().getCreationTime() > mArg1.getSession() + .getCreationTime()) { + return 1; + } } return (int) (arg0.getId() - arg1.getId()); } @@ -80,8 +88,15 @@ final class ShowConnections extends SshCommand { SshSession sd = s != null ? s.getAttribute(SshSession.KEY) : null; final SocketAddress remoteAddress = io.getRemoteAddress(); - final long start = io.getCreationTime(); - final long idle = now - io.getLastIoTime(); + MinaSession minaSession = io instanceof MinaSession + ? (MinaSession) io + : null; + final long start = minaSession == null + ? 0 + : minaSession.getSession().getCreationTime(); + final long idle = minaSession == null + ? now + : now - minaSession.getSession().getLastIoTime(); stdout.print(String.format("%8s %8s %8s %-15.15s %.30s\n", // id(sd), // diff --git a/gerrit-war/src/main/resources/log4j.properties b/gerrit-war/src/main/resources/log4j.properties index 1fcca6df02..cb1491669e 100644 --- a/gerrit-war/src/main/resources/log4j.properties +++ b/gerrit-war/src/main/resources/log4j.properties @@ -26,7 +26,7 @@ log4j.logger.org.apache.mina=WARN log4j.logger.org.apache.sshd.common=WARN log4j.logger.org.apache.sshd.server=WARN log4j.logger.org.apache.sshd.common.keyprovider.FileKeyPairProvider=INFO -log4j.logger.com.google.gerrit.server.ssh.GerritServerSession=WARN +log4j.logger.com.google.gerrit.sshd.GerritServerSession=WARN # Silence non-critical messages from Jetty. # @@ -582,13 +582,13 @@ limitations under the License. <dependency> <groupId>org.apache.mina</groupId> <artifactId>mina-core</artifactId> - <version>2.0.5</version> + <version>2.0.7</version> </dependency> <dependency> <groupId>org.apache.sshd</groupId> <artifactId>sshd-core</artifactId> - <version>0.6.0</version> + <version>0.9.0.201311081</version> </dependency> <dependency> |