feat(ssh): add relay-shell chain mode and harden bastion auth flow#252
feat(ssh): add relay-shell chain mode and harden bastion auth flow#252RoryChou-flux wants to merge 4 commits intobinaricat:mainfrom
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: eb0d1c4b1a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
electron/bridges/sshBridge.cjs
Outdated
|
|
||
| stream.on("data", onData); | ||
| stream.stderr?.on("data", onStderr); | ||
| timer = setTimeout(sendOnce, RELAY_STARTUP_DELAY_MS); |
There was a problem hiding this comment.
Avoid timeout-injecting startup command during relay auth
In relay-shell mode, scheduleRelayStartupCommand always sends startupCommand after a fixed 3s timeout even if no shell prompt was detected. When the inner SSH hop is still waiting on password/OTP/MFA input (a common case this mode is meant to support), that timeout writes the startup command into the authentication prompt, which can fail authentication or corrupt the challenge flow before the session is ready.
Useful? React with 👍 / 👎.
electron/bridges/sshBridge.cjs
Outdated
| * Regex that matches safe SSH identifiers (hostnames, usernames, ports). | ||
| * Rejects shell metacharacters to prevent command injection. | ||
| */ | ||
| const SAFE_SSH_IDENTIFIER = /^[a-zA-Z0-9._:@\[\]\-]+$/; |
There was a problem hiding this comment.
Permit scoped IPv6 hosts in relay-shell safety validation
The relay-shell safety regex rejects %, so valid scoped IPv6 addresses like fe80::1%eth0 are treated as unsafe and assertSafeSshValue throws before connecting. This is a functional regression specific to relay-shell for link-local IPv6 targets/jump hosts that previously worked through normal SSH paths.
Useful? React with 👍 / 👎.
Follow-up fixes (review-focused)Addressed the two concrete risks found in the relay-shell path, with minimal scope and clear behavior boundaries. 1) Relay-shell is now explicitly single-hop (end-to-end)Why:
What changed:
2) Startup command fallback is no longer silent in relay-shellWhy:
What changed:
Validation
If you'd like, I can also add a small E2E/integration test pass specifically for relay-shell single-hop and startup timeout messaging in a follow-up commit. |
Some bastion hosts disable AllowTcpForwarding, which prevents the default proxy-tunnel mode (direct-tcpip channel forwarding) from working. Relay-shell mode addresses this by SSHing into the first jump host and executing `ssh -tt` in its shell to reach the target. Core changes: - sshBridge: relay-shell connection flow with command-injection prevention (assertSafeSshValue allowlist + shellEscapeForSh), IPv6 bracket handling, auth failure detection, and cleanup parity with the normal SSH path (chainConnections, auth:failed events) - sshAuthHelper: add preferKeyboardInteractive option to avoid duplicate OTP pushes on bastion hosts that treat password-auth attempts as separate challenge triggers - scheduleRelayStartupCommand: detect inner SSH readiness via shell prompt pattern matching (with sliding 512-byte probe buffer) and a 3-second timeout fallback - startupCommand handling moved from frontend setTimeout to backend stream-level for more accurate timing across all modes - SFTP guard: relay-shell mode does not support SFTP (requires direct-tcpip subsystem); early error in sftpBridge + unified getSftpErrorMessage in the frontend - HostDetailsPanel: connection mode selector in ChainPanel with save-time hostChain cleanup (strip empty chains, omit defaults) - sshConfigSerializer: skip ProxyJump for relay-shell (no SSH config equivalent) - isAuthenticationError extracted as shared helper, replacing three inline checks Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
b01a19a to
0b0510f
Compare
Summary
Add a new host-chain connection mode:
relay-shell, and wire it end-to-end from UI to runtime/electron bridge.Also harden bastion auth behavior and relay error handling.
What changed
HostChainConnectionModeandconnectionModeto host chain model.jumpModefrom terminal/SFTP callers to backend.connectionModein managed-source sync diff detection.ssh.netcatty:auth:failed(parity with normal chain path).connectionModewithouthostIds).jumpMode === relay-shellin backend.ProxyJumpfor relay-shell (runtime-only mode) and add export note.Validation
npm run -s lintnode --check electron/bridges/sshBridge.cjsnode --check electron/bridges/sftpBridge.cjsnode --check electron/bridges/sshAuthHelper.cjsKnown limitation
In relay-shell mode, inner SSH failure may occur after outer shell is established; this is surfaced via terminal output/exit rather than guaranteed early structured connect failure.