Skip to content

Commit

Permalink
fix: update sshj to 0.38.0 and fix heartbeat issue (#1360)
Browse files Browse the repository at this point in the history
### Motivation
SSHJ released a new version (0.38.0) in which they improved their
support for strict key exchanges. Unfortunately this also introduced a
bug on their end, as the hearbeater is writing packets to the server
which aren't allowed during key exchange (KEX_INIT must be the first
packet, however due to the hearbeater an IGNORE packet is the first
leading to the error `strict KEX violation: KEXINIT was not the first
packet`).

### Modification
Bump SSHJ to 0.38.0 and write a custom heartbeater which checks if the
key exchange is done before sending heartbeat packets.

### Result
SSHJ is updated to 0.38.0 and everything works as expected again.
  • Loading branch information
derklaro authored Mar 6, 2024
1 parent 7ff26ec commit e5eac34
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 2 deletions.
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ mysqlConnector = "8.3.0"
asm = "9.6"
oshi = "6.4.13"
vavr = "0.10.4"
sshj = "0.37.0"
sshj = "0.38.0"
jjwt = "0.11.5"
slf4j = "1.7.36"
aerogel = "2.1.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
import lombok.NonNull;
import net.schmizz.keepalive.KeepAlive;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.connection.ConnectionImpl;
import net.schmizz.sshj.transport.TransportException;

public final class ActiveHeartbeatKeepAliveProvider extends KeepAliveProvider {

Expand All @@ -28,8 +31,29 @@ public final class ActiveHeartbeatKeepAliveProvider extends KeepAliveProvider {

@Override
public @NonNull KeepAlive provide(@NonNull ConnectionImpl connection) {
var keepAlive = KeepAliveProvider.HEARTBEAT.provide(connection);
var keepAlive = new HeartbeatKeepAlive(connection);
keepAlive.setKeepAliveInterval(HEARTBEAT_DELAY_SECONDS);
return keepAlive;
}

private static final class HeartbeatKeepAlive extends KeepAlive {

public HeartbeatKeepAlive(@NonNull ConnectionImpl conn) {
super(conn, "cloudnet-ssh-heartbeater");
}

@Override
protected void doKeepAlive() throws TransportException {
// when the server wants a strict key exchange, no other packets are allowed
// to be sent in that time interval (KEX_INIT must be the first packet). As it's
// very unlikely that a heartbeat is required during the timeframe anyway, we just
// don't execute the heartbeat until the key exchange is completed.
// Done by ensuring that the service is set. This means that the key
// exchange is done and the connection is up.
var transport = this.conn.getTransport();
if (this.conn.equals(transport.getService())) {
transport.write(new SSHPacket(Message.IGNORE));
}
}
}
}

0 comments on commit e5eac34

Please sign in to comment.