Skip to content

fix(server): offer only none SSH compression to avoid zlib@openssh.com desync (fixes #215)#217

Merged
inureyes merged 4 commits into
lablup:mainfrom
Yaminyam:fix/sftp-disable-zlib-openssh
Jul 16, 2026
Merged

fix(server): offer only none SSH compression to avoid zlib@openssh.com desync (fixes #215)#217
inureyes merged 4 commits into
lablup:mainfrom
Yaminyam:fix/sftp-disable-zlib-openssh

Conversation

@Yaminyam

@Yaminyam Yaminyam commented Jul 6, 2026

Copy link
Copy Markdown
Member

Summary

Clients that negotiate delayed zlib (zlib@openssh.com) — Cyberduck, or OpenSSH sftp -C — complete the SFTP handshake (INIT / REALPATH / STAT all succeed) and then the connection dies, with the server logging SshEncoding: length invalid on the next inbound packet.

Root cause

russh's delayed-zlib transport. Compression activates after auth; a few packets later the flate2 stream desyncs, so russh reads the next packet's length prefix out of corrupted plaintext and aborts the session.

Confirmed it is compression, not the SFTP layer or the channel-close path:

client compression negotiated result
OpenSSH sftp (default) none ✅ works
FileZilla none ✅ works
paramiko none ✅ works
OpenSSH sftp -C zlib@openssh.com SshEncoding: length invalid
Cyberduck 9.5 zlib@openssh.com SshEncoding: length invalid

Reproduces on both russh 0.61.1 (current fork) and 0.62.1, so it is independent of the SFTP session-close behaviour.

One-command repro (before this patch)

sftp -C -P 2223 user@host      # -> "SshEncoding: length invalid", connection closed

Fix

build_russh_config now advertises only none:

const NO_COMPRESSION: &[russh::compression::Name] = &[russh::compression::NONE];
let preferred = russh::Preferred {
    compression: std::borrow::Cow::Borrowed(NO_COMPRESSION),
    ..russh::Preferred::DEFAULT
};

Every client falls back to the uncompressed transport. This matches the Dropbear / OpenSSH sftp-server defaults used in Backend.AI kernel containers, where compression was never in play — so there is no functional loss for the intended deployment.

Verification

  • sftp -C now negotiates server_compression: None and lists / downloads cleanly.
  • Live Cyberduck 9.5 session connects and browses without the mid-session drop.
  • cargo build / cargo clippy clean on main (russh 0.61.1 fork).

The underlying russh delayed-zlib desync is left to be fixed upstream; this patch is the safe server-side workaround.

Fixes #215.

…com desync

Clients that negotiate delayed zlib (`zlib@openssh.com`) — Cyberduck,
`sftp -C` — completed the SFTP handshake and then dropped mid-session with
the server logging `SshEncoding: length invalid` on the next inbound packet.

Root cause is russh's delayed-zlib transport: the flate2 stream desyncs a
few packets after compression activates post-auth, so russh reads the next
packet's length prefix out of corrupted plaintext. Reproducible on both
russh 0.61.1 and 0.62.1 (rules out the channel-close path; it's compression,
not the SFTP layer). OpenSSH's default `sftp`, FileZilla, and paramiko all
negotiate `none` and were unaffected.

Set `russh::Preferred { compression: [NONE], ..DEFAULT }` in
`build_russh_config` so the server advertises only `none` and clients fall
back to the uncompressed transport — matching the Dropbear / OpenSSH
sftp-server defaults used in Backend.AI kernel containers, where compression
was never in play. Verified with `sftp -C` (negotiates `compression: none`)
and a live Cyberduck 9.5 session.

Fixes lablup#215.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
inureyes added 2 commits July 16, 2026 18:50
Regression guard for lablup#215: locks in that the server never advertises
zlib@openssh.com, so delayed-zlib clients (Cyberduck, sftp -C) cannot
negotiate the compression that triggers russh's mid-session desync.
@inureyes
inureyes force-pushed the fix/sftp-disable-zlib-openssh branch from de1248b to 6071f8e Compare July 16, 2026 10:00
clippy 1.97's useless_borrows_in_formatting flags the `&` in
src/ui/basic.rs:85, which turns the CI `clippy -D warnings` step red on
main independently of this PR. Remove the redundant borrow to unblock CI.
@inureyes
inureyes merged commit fbbf226 into lablup:main Jul 16, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(sftp): sshj/Cyberduck clients dropped on initial REALPATH/STAT via russh strict-parser SshEncoding::Length on incoming decode

2 participants