bump russh for keyboard-interactive fixes and lock race fix

This commit is contained in:
Eugene 2025-01-16 22:14:29 +01:00
parent 302f88058c
commit 92c729dada
No known key found for this signature in database
GPG Key ID: 5896FCBBDD1CF4F4
3 changed files with 50 additions and 20 deletions

View File

@ -30,7 +30,7 @@
"native-process-working-directory": "^1.0.2", "native-process-working-directory": "^1.0.2",
"npm": "6", "npm": "6",
"rxjs": "^7.5.7", "rxjs": "^7.5.7",
"russh": "0.1.11", "russh": "0.1.13",
"source-map-support": "^0.5.20", "source-map-support": "^0.5.20",
"v8-compile-cache": "^2.3.0", "v8-compile-cache": "^2.3.0",
"yargs": "^17.7.2" "yargs": "^17.7.2"

View File

@ -3628,10 +3628,10 @@ run-queue@^1.0.0, run-queue@^1.0.3:
dependencies: dependencies:
aproba "^1.1.1" aproba "^1.1.1"
russh@0.1.11: russh@0.1.13:
version "0.1.11" version "0.1.13"
resolved "https://registry.yarnpkg.com/russh/-/russh-0.1.11.tgz#22e74f93ec1cb045930c85f8ba1daf2e5efcae4b" resolved "https://registry.yarnpkg.com/russh/-/russh-0.1.13.tgz#e5e1d1b3b7fcd62992df8efce5300b3f57262df5"
integrity sha512-3CuI+rMoGpnnFDJxsEmcHYRSHInf3bz3fbgeyPnX8n1wgsX6wdbyI1DKL188oQlsrFWEjO3/7ebbYliCi0Qz7w== integrity sha512-5yLxrsC0rYtfynkCLIdeS2XHQKwMEFeww3XoEezaNSHks9grbHbLxgfWBnZ7ZJgrCrJCRgSOvu6Ct5GKpYgb7w==
dependencies: dependencies:
"@napi-rs/cli" "^2.18.3" "@napi-rs/cli" "^2.18.3"

View File

@ -50,6 +50,18 @@ type AuthMethod = {
kind: 'pageant', kind: 'pageant',
} }
function sshAuthTypeForMethod (m: AuthMethod): string {
switch (m.type) {
case 'none': return 'none'
case 'hostbased': return 'hostbased'
case 'prompt-password': return 'password'
case 'saved-password': return 'password'
case 'keyboard-interactive': return 'keyboard-interactive'
case 'publickey': return 'publickey'
case 'agent': return 'agent'
}
}
export class KeyboardInteractivePrompt { export class KeyboardInteractivePrompt {
readonly responses: string[] = [] readonly responses: string[] = []
@ -181,6 +193,13 @@ export class SSHSession {
}) })
} }
} }
if (!this.profile.options.auth || this.profile.options.auth === 'keyboardInteractive') {
const savedPassword = this.profile.options.password ?? await this.passwordStorage.loadPassword(this.profile)
if (savedPassword) {
this.remainingAuthMethods.push({ type: 'keyboard-interactive', savedPassword })
}
this.remainingAuthMethods.push({ type: 'keyboard-interactive' })
}
if (!this.profile.options.auth || this.profile.options.auth === 'password') { if (!this.profile.options.auth || this.profile.options.auth === 'password') {
if (this.profile.options.password) { if (this.profile.options.password) {
this.remainingAuthMethods.push({ type: 'saved-password', password: this.profile.options.password }) this.remainingAuthMethods.push({ type: 'saved-password', password: this.profile.options.password })
@ -191,13 +210,6 @@ export class SSHSession {
} }
this.remainingAuthMethods.push({ type: 'prompt-password' }) this.remainingAuthMethods.push({ type: 'prompt-password' })
} }
if (!this.profile.options.auth || this.profile.options.auth === 'keyboardInteractive') {
const savedPassword = this.profile.options.password ?? await this.passwordStorage.loadPassword(this.profile)
if (savedPassword) {
this.remainingAuthMethods.push({ type: 'keyboard-interactive', savedPassword })
}
this.remainingAuthMethods.push({ type: 'keyboard-interactive' })
}
this.remainingAuthMethods.push({ type: 'hostbased' }) this.remainingAuthMethods.push({ type: 'hostbased' })
} }
@ -495,7 +507,7 @@ export class SSHSession {
this.keyboardInteractivePrompt.next(prompt) this.keyboardInteractivePrompt.next(prompt)
} }
async handleAuth (methodsLeft?: string[] | null): Promise<russh.AuthenticatedSSHClient|null> { async handleAuth (): Promise<russh.AuthenticatedSSHClient|null> {
this.activePrivateKey = null this.activePrivateKey = null
if (!(this.ssh instanceof russh.SSHClient)) { if (!(this.ssh instanceof russh.SSHClient)) {
@ -506,22 +518,36 @@ export class SSHSession {
throw new Error('No username') throw new Error('No username')
} }
const noneResult = await this.ssh.authenticateNone(this.authUsername)
if (noneResult instanceof russh.AuthenticatedSSHClient) {
return noneResult
}
let methodsLeft = noneResult.remainingMethods
function maybeSetRemainingMethods (r: russh.AuthFailure) {
if (r.remainingMethods.length) {
methodsLeft = r.remainingMethods
}
}
while (true) { while (true) {
const method = this.remainingAuthMethods.shift() const m = methodsLeft
const method = this.remainingAuthMethods.find(x => !m || m.includes(sshAuthTypeForMethod(x)))
if (!method) { if (!method) {
return null return null
} }
if (methodsLeft && !methodsLeft.includes(method.type) && method.type !== 'agent') {
// Agent can still be used even if not in methodsLeft this.remainingAuthMethods = this.remainingAuthMethods.filter(x => x !== method)
this.logger.info('Server does not support auth method', method.type)
continue
}
if (method.type === 'saved-password') { if (method.type === 'saved-password') {
this.emitServiceMessage(this.translate.instant('Using saved password')) this.emitServiceMessage(this.translate.instant('Using saved password'))
const result = await this.ssh.authenticateWithPassword(this.authUsername, method.password) const result = await this.ssh.authenticateWithPassword(this.authUsername, method.password)
if (result instanceof russh.AuthenticatedSSHClient) { if (result instanceof russh.AuthenticatedSSHClient) {
return result return result
} }
maybeSetRemainingMethods(result)
} }
if (method.type === 'prompt-password') { if (method.type === 'prompt-password') {
const modal = this.ngbModal.open(PromptModalComponent) const modal = this.ngbModal.open(PromptModalComponent)
@ -539,6 +565,7 @@ export class SSHSession {
if (result instanceof russh.AuthenticatedSSHClient) { if (result instanceof russh.AuthenticatedSSHClient) {
return result return result
} }
maybeSetRemainingMethods(result)
} else { } else {
continue continue
} }
@ -556,6 +583,7 @@ export class SSHSession {
if (result instanceof russh.AuthenticatedSSHClient) { if (result instanceof russh.AuthenticatedSSHClient) {
return result return result
} }
maybeSetRemainingMethods(result)
} }
} catch (e) { } catch (e) {
this.emitServiceMessage(colors.bgYellow.yellow.black(' ! ') + ` Failed to load private key ${method.name}: ${e}`) this.emitServiceMessage(colors.bgYellow.yellow.black(' ! ') + ` Failed to load private key ${method.name}: ${e}`)
@ -567,6 +595,7 @@ export class SSHSession {
while (true) { while (true) {
if (state.state === 'failure') { if (state.state === 'failure') {
maybeSetRemainingMethods(state)
break break
} }
@ -602,7 +631,7 @@ export class SSHSession {
} }
} }
state = await this.ssh .continueKeyboardInteractiveAuthentication(responses) state = await this.ssh.continueKeyboardInteractiveAuthentication(responses)
if (state instanceof russh.AuthenticatedSSHClient) { if (state instanceof russh.AuthenticatedSSHClient) {
return state return state
@ -615,6 +644,7 @@ export class SSHSession {
if (result instanceof russh.AuthenticatedSSHClient) { if (result instanceof russh.AuthenticatedSSHClient) {
return result return result
} }
maybeSetRemainingMethods(result)
} catch (e) { } catch (e) {
this.emitServiceMessage(colors.bgYellow.yellow.black(' ! ') + ` Failed to authenticate using agent: ${e}`) this.emitServiceMessage(colors.bgYellow.yellow.black(' ! ') + ` Failed to authenticate using agent: ${e}`)
continue continue