feat(ssh): support keyboard-interactive authentication (MFA)#8750
feat(ssh): support keyboard-interactive authentication (MFA)#8750jhkim0911 wants to merge 2 commits into
Conversation
Servers that require a keyboard-interactive round (RFC 4256) after password auth — typical for HPC clusters and other hosts behind Duo/OTP/push MFA — failed with "All configured authentication methods failed" right after the password was entered, because ssh2 never advertised the keyboard-interactive method and had no handler for its prompts. The same hosts connect fine with OpenSSH and VS Code Remote SSH. - Enable tryKeyboard on every ssh2 connect config and answer keyboard-interactive prompt rounds through the existing credential request flow, so the MFA challenge reaches the user instead of failing the connection. - Route password-looking prompts (masked, mention "password", not one-time/OTP) through the password credential kind and its in-memory cache: servers that collect the login password via keyboard-interactive keep silent reconnects, and a rejected cached password falls through to a fresh prompt instead of looping. All other prompts (push approval, passcodes, option menus) always reach the user, with the server's instructions shown and the RFC 4256 echo flag deciding whether the input is masked. - Stop the 30s ready timeout once the server starts prompting: it budgets the network handshake, not a human answering an MFA push. The server's LoginGraceTime and the 120s credential dialog timeout still bound the wait. - Treat a cancelled keyboard-interactive prompt as an explicit user decision: fail the round with no answers and skip the password re-prompt fallback. Fixes stablyai#8622
📝 WalkthroughWalkthroughSSH keyboard-interactive authentication was added with support for MFA prompts, password caching, cancellation, and echo-aware responses. Credential metadata now flows through SSH callbacks, IPC, preload types, and the renderer. The SSH connection handles keyboard-interactive events and prevents cancelled prompts from falling back to password authentication. The passphrase dialog and translations now support verification prompts and visible or hidden responses. Tests cover prompt handling, caching, cancellation, connection behavior, and configuration. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
src/main/ssh/ssh-connection.ts (3)
1015-1028: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winRejection branch swallows the actual error.
The second
.then()callback discards whatevercollectKeyboardInteractiveResponsesrejected with and always doesfinish([]). This means a genuine bug (e.g. an IPC failure insiderequestCredential) is indistinguishable from an expected user cancellation, and there's no log to help diagnose it — the connection just fails with the generic ssh2 auth error.♻️ Log the rejection reason
- () => { + (err) => { + console.warn( + `[ssh] keyboard-interactive handling failed for ${this.target.label}: ${err instanceof Error ? err.message : String(err)}` + ) if (!settled) { finish([]) } }
978-1028: 🧹 Nitpick | 🔵 TrivialDesign note: no overall timeout bounds a human MFA response once the ready-timeout is cleared.
Per the "Why" comment, the wait is now bounded only by the server's
LoginGraceTimeand the credential dialog's own timeout. That's a reasonable tradeoff for MFA push/OTP, but if a host hasLoginGraceTime 0(disabled) and the user leaves the credential dialog open indefinitely, theSshClient/socket for that attempt stays alive until the user acts. Worth keeping in mind operationally (e.g. if support ever sees lingering SSH sockets), though not something to block this PR on given the documented rationale.
993-997: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winGuard the ssh2 ready-timeout handle This reaches into ssh2’s internal
_readyTimeoutfield; if a future update changes it, the clear becomes a silent no-op and MFA prompts can fall back to the handshake timeout again. Add a guard/log here so that breakage is visible.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: f20f9728-183a-491b-ba42-dafeaad3dd60
📒 Files selected for processing (17)
src/main/ipc/ssh-passphrase.tssrc/main/ipc/ssh.tssrc/main/ssh/ssh-connection-utils.test.tssrc/main/ssh/ssh-connection-utils.tssrc/main/ssh/ssh-connection.test.tssrc/main/ssh/ssh-connection.tssrc/main/ssh/ssh-keyboard-interactive.test.tssrc/main/ssh/ssh-keyboard-interactive.tssrc/preload/api-types.tssrc/preload/index.tssrc/renderer/src/components/settings/SshPassphraseDialog.tsxsrc/renderer/src/i18n/locales/en.jsonsrc/renderer/src/i18n/locales/es.jsonsrc/renderer/src/i18n/locales/ja.jsonsrc/renderer/src/i18n/locales/ko.jsonsrc/renderer/src/i18n/locales/zh.jsonsrc/renderer/src/store/slices/ssh.ts
… them Review follow-up for keyboard-interactive support: - Log the rejection reason when answering a keyboard-interactive round fails, so a broken credential prompter is distinguishable from an expected user cancellation. - Guard the ssh2 internal _readyTimeout access and warn when the handle is missing, so a future ssh2 internals change can't silently revert MFA prompts to the 30s handshake timeout.
|
Addressed the review feedback in 87e7a59:
On the unbounded-wait design note: agreed it's worth keeping in mind. In practice the credential dialog resolves |
Problem
Connecting to a host that requires password auth followed by a keyboard-interactive MFA challenge (e.g. a university HPC cluster behind Duo push/OTP) fails immediately after the password is entered:
The MFA prompt is never shown. The same host works with OpenSSH and VS Code Remote SSH. Fixes #8622.
Root cause
These servers accept the password as a partial success and then require a keyboard-interactive round (RFC 4256). ssh2 only attempts keyboard-interactive when
tryKeyboardis set and akeyboard-interactivelistener answers the prompts — Orca configured neither, so after the partial success no auth methods remained.Changes
tryKeyboard: trueon ssh2 connect configs and answer keyboard-interactive rounds through the existing credential request flow (ssh:credential-request→SshPassphraseDialog).echoflag controls input masking.LoginGraceTimeand the 120s credential dialog timeout still bound the wait.Testing
ssh-keyboard-interactive.test.ts) and integration tests for the full password → MFA flow, cached-password auto-answer, and cancel behavior (ssh-connection.test.ts).Serverrequiring password partial success + keyboard-interactive: the realSshConnectioncompletes the full flow and connects.pnpm typecheck,pnpm lint(incl. localization catalog/coverage and max-lines gates), and the SSH/IPC/renderer vitest suites all pass.