Skip to content

Commit

Permalink
added warning when server disconnects during auth
Browse files Browse the repository at this point in the history
  • Loading branch information
Eugeny committed Jan 27, 2025
1 parent cf0da75 commit 380c306
Showing 1 changed file with 38 additions and 15 deletions.
53 changes: 38 additions & 15 deletions tabby-ssh/src/session/ssh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export class SSHSession {

private logger: Logger
private refCount = 0
private remainingAuthMethods: AuthMethod[] = []
private allAuthMethods: AuthMethod[] = []
private serviceMessage = new Subject<string>()
private keyboardInteractivePrompt = new Subject<KeyboardInteractivePrompt>()
private willDestroy = new Subject<void>()
Expand All @@ -125,6 +125,7 @@ export class SSHSession {
private translate: TranslateService
private knownHosts: SSHKnownHostsService
private privateKeyImporters: AutoPrivateKeyLocator[]
private previouslyDisconnected = false

constructor (
private injector: Injector,
Expand All @@ -150,15 +151,15 @@ export class SSHSession {
}

private addPublicKeyAuthMethod (name: string, contents: Buffer) {
this.remainingAuthMethods.push({
this.allAuthMethods.push({
type: 'publickey',
name,
contents,
})
}

async init (): Promise<void> {
this.remainingAuthMethods = [{ type: 'none' }]
this.allAuthMethods = [{ type: 'none' }]
if (!this.profile.options.auth || this.profile.options.auth === 'publicKey') {
if (this.profile.options.privateKeys?.length) {
for (const pk of this.profile.options.privateKeys) {
Expand Down Expand Up @@ -187,7 +188,7 @@ export class SSHSession {
if (!spec) {
this.emitServiceMessage(colors.bgYellow.yellow.black(' ! ') + ` Agent auth selected, but no running Agent process is found`)
} else {
this.remainingAuthMethods.push({
this.allAuthMethods.push({
type: 'agent',
...spec,
})
Expand All @@ -196,21 +197,21 @@ 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.allAuthMethods.push({ type: 'keyboard-interactive', savedPassword })
}
this.remainingAuthMethods.push({ type: 'keyboard-interactive' })
this.allAuthMethods.push({ type: 'keyboard-interactive' })
}
if (!this.profile.options.auth || this.profile.options.auth === 'password') {
if (this.profile.options.password) {
this.remainingAuthMethods.push({ type: 'saved-password', password: this.profile.options.password })
this.allAuthMethods.push({ type: 'saved-password', password: this.profile.options.password })
}
const password = await this.passwordStorage.loadPassword(this.profile)
if (password) {
this.remainingAuthMethods.push({ type: 'saved-password', password })
this.allAuthMethods.push({ type: 'saved-password', password })
}
this.remainingAuthMethods.push({ type: 'prompt-password' })
this.allAuthMethods.push({ type: 'prompt-password' })
}
this.remainingAuthMethods.push({ type: 'hostbased' })
this.allAuthMethods.push({ type: 'hostbased' })
}

private async getAgentConnectionSpec (): Promise<russh.AgentConnectionSpec|null> {
Expand Down Expand Up @@ -323,9 +324,14 @@ export class SSHSession {
}
})

this.previouslyDisconnected = false
this.ssh.disconnect$.subscribe(() => {
if (this.open) {
this.destroy()
if (!this.previouslyDisconnected) {
this.previouslyDisconnected = true
// Let service messages drain
setTimeout(() => {
this.destroy()
})
}
})

Expand Down Expand Up @@ -508,6 +514,22 @@ export class SSHSession {
}

async handleAuth (): Promise<russh.AuthenticatedSSHClient|null> {
const subscription = this.ssh.disconnect$.subscribe(() => {
// Auto auth and >=3 keys found
if (!this.profile.options.auth && this.allAuthMethods.filter(x => x.type === 'publickey').length >= 3) {
this.emitServiceMessage('The server has disconnected during authentication.')
this.emitServiceMessage('This may happen if too many private key authentication attemps are made.')
this.emitServiceMessage('You can set the specific private key for authentication in the profile settings.')
}
})
try {
return await this._handleAuth()
} finally {
subscription.unsubscribe()
}
}

private async _handleAuth (): Promise<russh.AuthenticatedSSHClient|null> {
this.activePrivateKey = null

if (!(this.ssh instanceof russh.SSHClient)) {
Expand All @@ -523,6 +545,7 @@ export class SSHSession {
return noneResult
}

let remainingMethods = [...this.allAuthMethods]
let methodsLeft = noneResult.remainingMethods

function maybeSetRemainingMethods (r: russh.AuthFailure) {
Expand All @@ -533,13 +556,13 @@ export class SSHSession {

while (true) {
const m = methodsLeft
const method = this.remainingAuthMethods.find(x => m.length === 0 || m.includes(sshAuthTypeForMethod(x)))
const method = remainingMethods.find(x => m.length === 0 || m.includes(sshAuthTypeForMethod(x)))

if (!method) {
if (this.previouslyDisconnected || !method) {
return null
}

this.remainingAuthMethods = this.remainingAuthMethods.filter(x => x !== method)
remainingMethods = remainingMethods.filter(x => x !== method)

if (method.type === 'saved-password') {
this.emitServiceMessage(this.translate.instant('Using saved password'))
Expand Down

0 comments on commit 380c306

Please sign in to comment.