You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Starting several SSH connections across multiple threads can result in an SSH server sending SSH_MSG_UNIMPLEMENTED during key exchange as shown in the follow error log:
ERROR net.schmizz.sshj.transport.TransportImpl - Dying because - Received SSH_MSG_UNIMPLEMENTED while exchanging keys
net.schmizz.sshj.transport.TransportException: Received SSH_MSG_UNIMPLEMENTED while exchanging keys
at net.schmizz.sshj.transport.TransportImpl.gotUnimplemented(TransportImpl.java:565)
at net.schmizz.sshj.transport.TransportImpl.handle(TransportImpl.java:495)
at net.schmizz.sshj.transport.Decoder.decode(Decoder.java:113)
at net.schmizz.sshj.transport.Decoder.received(Decoder.java:200)
at net.schmizz.sshj.transport.Reader.run(Reader.java:60)
This appears to occur more frequently in SSHJ 0.33.0 and 0.34.0 with SSH KeepAlive enabled. These versions include changes in pull request #752, which start the KeepAlive Thread in SSHClient.onConnect().
The underlying problem appears to be related to the timing of sending and receiving SSH_MSG_KEXINIT packets. Sending an SSH_MSG_KEXINIT packet can be initiated from different locations. The SSHClient.onConnect() method calls Transport.doKex(), which calls KeyExchanger.startKex(). KexExchange.handle() can also call KeyExchanger.startKex() when receiving an SSH_MSG_KEXINIT packet. This can cause problems with multiple connections due to different threads calling KeyExchanger.startKex() at different times.
When an OpenSSH server receives an unexpected SSH_MSG_KEXINIT packet, it sends an SSH_MSG_UNIMPLEMENTED packet and logs the following error:
dispatch_protocol_error: type 20 seq 3 [preauth]
The atmoz/sftp Docker image provides a standard OpenSSH server for testing, but this issue should be reproducible with any recent version of OpenSSH server.
The nature of the problem makes it difficult to reproduce in a unit test, but the following class is capable of causing the error in the process of initiating multiple connection attempts using a pool of threads. The class attempts up to 1000 connections using a pool of four threads. It is not necessary to authenticate to the SSH server since the problem occurs during initial connection key exchange. The class relies of slf4j-simple and Java System properties for console logging. The connect method calls System.exit() at the first error encountered.
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class SshConnectCommand {
static {
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "DEBUG");
System.setProperty("org.slf4j.simpleLogger.showDateTime", "true");
System.setProperty("org.slf4j.simpleLogger.dateTimeFormat", "yyyy-MM-dd HH:mm:ss.SSS");
}
private static final String SSH_SERVER = "127.0.0.1";
private static final int SSH_PORT = 2222;
private static final int KEEP_ALIVE_INTERVAL = 5;
private static final int POOL_SIZE = 4;
private static final int CONNECTIONS = 1000;
private static final int EXECUTION_TIME = 10;
private static final Logger logger = LoggerFactory.getLogger(SshConnectCommand.class);
public static void main(String[] args) throws Exception {
final ExecutorService executorService = Executors.newFixedThreadPool(POOL_SIZE);
for (int i = 1; i <= CONNECTIONS; i++) {
final int counter = i;
executorService.execute(() -> {
connect(counter);
});
}
executorService.awaitTermination(EXECUTION_TIME, TimeUnit.SECONDS);
executorService.shutdown();
logger.info("Completed Connections [{}]", CONNECTIONS);
}
private static void connect(int counter) {
try (final SSHClient sshClient = createClient()) {
logger.info("Connection Starting [{}]", counter);
sshClient.connect(SSH_SERVER, SSH_PORT);
} catch (final Exception e) {
logger.error("Connection Failed [{}]", counter, e);
System.exit(1);
} finally {
logger.info("Connection Completed [{}]", counter);
}
}
private static SSHClient createClient() {
final DefaultConfig config = new DefaultConfig();
config.setKeepAliveProvider(KeepAliveProvider.KEEP_ALIVE);
final SSHClient sshClient = new SSHClient(config);
sshClient.getConnection().getKeepAlive().setKeepAliveInterval(KEEP_ALIVE_INTERVAL);
sshClient.addHostKeyVerifier(new PromiscuousVerifier());
return sshClient;
}
}
The text was updated successfully, but these errors were encountered:
Starting several SSH connections across multiple threads can result in an SSH server sending
SSH_MSG_UNIMPLEMENTED
during key exchange as shown in the follow error log:This appears to occur more frequently in SSHJ 0.33.0 and 0.34.0 with SSH KeepAlive enabled. These versions include changes in pull request #752, which start the KeepAlive Thread in
SSHClient.onConnect()
.The underlying problem appears to be related to the timing of sending and receiving
SSH_MSG_KEXINIT
packets. Sending anSSH_MSG_KEXINIT
packet can be initiated from different locations. TheSSHClient.onConnect()
method callsTransport.doKex()
, which callsKeyExchanger.startKex()
.KexExchange.handle()
can also callKeyExchanger.startKex()
when receiving anSSH_MSG_KEXINIT
packet. This can cause problems with multiple connections due to different threads callingKeyExchanger.startKex()
at different times.When an OpenSSH server receives an unexpected
SSH_MSG_KEXINIT
packet, it sends anSSH_MSG_UNIMPLEMENTED
packet and logs the following error:The atmoz/sftp Docker image provides a standard OpenSSH server for testing, but this issue should be reproducible with any recent version of OpenSSH server.
The nature of the problem makes it difficult to reproduce in a unit test, but the following class is capable of causing the error in the process of initiating multiple connection attempts using a pool of threads. The class attempts up to 1000 connections using a pool of four threads. It is not necessary to authenticate to the SSH server since the problem occurs during initial connection key exchange. The class relies of
slf4j-simple
and Java System properties for console logging. The connect method callsSystem.exit()
at the first error encountered.The text was updated successfully, but these errors were encountered: