Skip to content

feat(rustconn-core): add quicssh protocol support - #396

Closed
shafiev wants to merge 1 commit into
totoshko88:mainfrom
shafiev:feat/quicssh
Closed

shafiev wants to merge 1 commit into
totoshko88:mainfrom
shafiev:feat/quicssh

Conversation

@shafiev

@shafiev shafiev commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

SSH over QUIC via moul/quicssh: the tunnel rides OpenSSH's ProxyCommand (quicssh client --addr %h:%p), so sessions reuse the whole SSH spawn path over a spawn-time view of the stored connection. Default port 4242 is the remote quicssh server (UDP); the TCP pre-connect check, the connection tester and reconnect handle it accordingly. Wizard card, editor tab, templates, CSV import, icons and full rustconn-cli support included; export degrades to plain SSH (asbru) or is refused with the QUIC SSH label (MobaXterm, Remmina, KDBX).

What this changes

Adding the support of ssh over QUIC

Why

Because SSH is vulnerable in TCP connection environments, and most SSH packets are actually small, so it is only necessary to maintain the SSH connection to use it in any network environment. QUIC is a good choice because it has good weak network optimization and an important feature called connection migration. This means that I can switch Wi-Fi networks freely when remote, ensuring a stable SSH connection. also i am using it for p2p udp punch hole for NAT traverstal to connect my ISP NATed devices

How it was tested

Also at local

Checklist

  • [*] cargo fmt --all clean
  • [*] cargo clippy --all-targets reports 0 warnings, from a run that actually re-checked (a cache hit prints Finished ... in 0.2s and reports nothing)
  • [*] Relevant tests pass; non-trivial logic has one runnable check
  • [*] Crate boundaries intact — no gtk4/adw/vte4 in rustconn-core or rustconn-cli, no unsafe outside a rustconn-*-sys crate
  • [*] Secrets use SecretString, intermediates are Zeroizing, nothing secret reaches a log, an error message, or a command argument
  • [*] New user-facing strings wrapped in i18n()/i18n_f(), source file listed in po/POTFILES.in, bash po/update-pot.sh run
  • [*] No dbg!, todo!, println!, or eprintln! left behind (rustconn-cli excepted — printing is its interface)
  • [*] CHANGELOG.md updated under ## [Unreleased] if the change is user-facing
  • [*] Commits follow type(scope): description

@shafiev shafiev mentioned this pull request Oct 28, 2026
3 tasks done
@totoshko88

Copy link
Copy Markdown
Owner

Hi @shafiev, thanks for this - SSH over QUIC is a genuinely useful idea, and the connection-migration angle (roaming between Wi-Fi networks, NAT traversal to your ISP-NATed devices) is a real use case. I reviewed the PR carefully, and I'd like to land the feature, but I think a smaller approach fits RustConn better. Let me explain the reasoning rather than just asking for changes.

The packaging problem is the deciding factor. quicssh is a separate Go binary with its own release cycle, so RustConn can't bundle it - the same way it never bundles ssh, xfreerdp3, kubectl or mosh. That by itself is fine (those tools are detected at runtime). The harder issue is the sandboxed builds:

  • In Flatpak and Snap, ssh and its ProxyCommand run inside the sandbox. A host-side ~/.local/bin/quicssh is simply not on the sandbox PATH, so the ProxyCommand can't find it - the feature would not work out of the box in either package, which together are a large share of our users.
  • AppImage ships only RustConn itself, so there too quicssh has to be a system install the user provides.
  • So in every distribution format, quicssh must be installed by the user regardless of how we wire it. Given that, the question becomes: how much machinery should RustConn carry for something that is, at runtime, exactly an SSH connection with a specific ProxyCommand?

RustConn already supports this today, with no new protocol. SshConfig has a free-text proxy_command field (used for Tor .onion hosts via ncat ... %h %p). An SSH connection with ProxyCommand=quicssh client --addr %h:%p already:

  • rides the whole existing SSH spawn path (and in-place reconnect),
  • bypasses the TCP pre-connect probe (because proxy_command.is_some()),
  • needs nothing beyond what's already there.
  • What I'd suggest instead (much smaller):

A "QUIC (quicssh)" preset on the ProxyCommand row in the editor that fills in quicssh client --addr %h:%p for the user.
A one-line entry in the password auto-fill allow-list so stored passwords still auto-fill.
Optionally a detect_quicssh() next to detect_mosh() plus a "quicssh not installed" hint, and a documented note that Flatpak/Snap need the binary reachable from the sandbox.
This delivers the same capability in roughly 20 lines, and - importantly - it sidesteps the issues the full-variant approach runs into, because it reuses the SSH machinery instead of reimplementing it:

  • Security (the blocker I can't merge past): the new variant puts the stored host into a shell via %h, and on OpenSSH < 9.6 (Ubuntu 22.04 ships 8.9, Debian 12 ships 9.2) that's a command-injection vector from a CSV-imported connection (CVE-2023-51385). The ProxyCommand field is already treated as "user owns what they type here", so the preset avoids introducing a new unvalidated host→shell path.
  • Jump hosts are silently dropped by the new variant (the view always sets proxy_command, so ProxyJump/Jump Host are discarded) while the UI and CHANGELOG still advertise them. With the preset there's no contradiction - it's an ordinary SSH connection.
  • Export / vault-key / connection-test / property-test gaps all disappear, because nothing special is added to those paths.
  • None of this is a knock on the work - the enum threading was done thoroughly and cleanly across all 47 call sites, and the as_quic_ssh_spawn_view design is the right shape. It's just more surface area (and a permanent public enum variant) than the feature needs.

Would you be up for reworking it toward the preset approach? Happy to point at the exact spots (proxy_command handling in protocol.rs, the detection pattern in detection.rs, the auto-fill allow-list). If you'd rather I take it from here and credit you, I'm glad to do that too.

@shafiev

shafiev commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

ok will try to do as you said.

Rework PR totoshko88#396 toward the preset approach: a QUIC button on the
editor's ProxyCommand row fills QUIC_SSH_PROXY_COMMAND
(quicssh client --addr %h:%p) instead of introducing a
ProtocolType::QuicSsh variant, so the connection stays a plain SSH
connection — jump hosts, exports, vault keys, connection testing and
reconnect apply unchanged, and no new host-to-shell path exists for
imported connections (CVE-2023-51385 shape on OpenSSH < 9.6).

The password auto-fill relay allow-list recognizes the quicssh
command, and detect_quicssh() next to detect_mosh() reports install
state plus the Flatpak/Snap sandbox reachability note in the row
tooltip.
totoshko88 pushed a commit that referenced this pull request Oct 30, 2026
Rework PR #396 toward the preset approach: a QUIC button on the
editor's ProxyCommand row fills QUIC_SSH_PROXY_COMMAND
(quicssh client --addr %h:%p) instead of introducing a
ProtocolType::QuicSsh variant, so the connection stays a plain SSH
connection — jump hosts, exports, vault keys, connection testing and
reconnect apply unchanged, and no new host-to-shell path exists for
imported connections (CVE-2023-51385 shape on OpenSSH < 9.6).

The password auto-fill relay allow-list recognizes the quicssh
command, and detect_quicssh() next to detect_mosh() reports install
state plus the Flatpak/Snap sandbox reachability note in the row
tooltip.

---
Integration onto base 0.24.2 (a0eedb1), committer = agent; author
n.shafiev <shafiev@gmail.com> preserved. Cherry-picked 987b5c7: the four
code files applied clean; CHANGELOG.md and po/rustconn.pot were the only
conflicts. CHANGELOG folded into the single base ## [Unreleased] ### Added
(duplicate header removed); po/rustconn.pot regenerated with
`bash po/update-pot.sh` (not hand-merged), ssh.rs already in POTFILES.in.
.po catalogues untouched; no version bump.

Verification: `scripts/verify.sh --tests` from the worktree root, detached,
exit 0 (target/verify-gate.rc=0; log target/verify.log, "All gates passed").
Clippy re-checked for real — the script cleaned workspace crates first
(log "cleaning workspace crates so clippy re-checks for real") and the
clippy phase shows Checking rustconn-core / Compiling rustconn; no
"Finished … in 0.2s" cache hit. Gates OK: typos, check-potfiles,
check-i18n-escapes, check-po-complete, crate boundaries, fmt, machete,
clippy --all-targets, clippy -p rustconn-cli --features full, rustdoc
-D warnings, workspace tests. Key test
ssh_tunnel::tests::test_proxy_command_relay_does_not_prompt (quicssh
allow-list) passed; rustconn-core suite 1484 passed / 0 failed. GUI
appearance needs a real desktop portal and was not verifiable from this
terminal. Evidence: .agents/tasks/pr-396-accept/verification.md.
@totoshko88 totoshko88 closed this Oct 30, 2026
@totoshko88

Copy link
Copy Markdown
Owner

Thank you for this contribution, @shafiev! 🙏

Accepted in the preset approach from your rework — the QUIC button on the ProxyCommand row (quicssh client --addr %h:%p), detect_quicssh(), and the entry in the password auto-fill allow-list — exactly as you revised the PR per the earlier review. The connection stays a plain SSH connection, so jump hosts, exports, vault keys, connection testing, and reconnect all work unchanged.

Shipped in release v0.24.2. Your authorship is preserved in commit c2004abe, which reached main via merge 4dd2c19c.

I'm closing this PR manually because the change landed on main as a separate commit (same content, author preserved) rather than as the PR branch head, so GitHub didn't mark it merged automatically.

One small technical note so the contribution shows up under contributors: please make sure the commit email (shafiev@gmail.com) is added to your GitHub account under Settings → Emails — otherwise the contribution may not appear in the contributors graph even though authorship is preserved.

Thanks again! 🚀

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.

2 participants