@@ -11,6 +11,7 @@ import * as cp from 'child_process';
11
11
import * as fs from 'fs' ;
12
12
import * as http from 'http' ;
13
13
import * as net from 'net' ;
14
+ import * as crypto from 'crypto' ;
14
15
import fetch , { Response } from 'node-fetch' ;
15
16
import { Client as sshClient , utils as sshUtils } from 'ssh2' ;
16
17
import * as tmp from 'tmp' ;
@@ -419,13 +420,19 @@ export default class RemoteConnector extends Disposable {
419
420
private async getWorkspaceSSHDestination ( workspaceId : string , gitpodHost : string ) : Promise < { destination : string ; password ?: string } > {
420
421
const session = await vscode . authentication . getSession (
421
422
'gitpod' ,
422
- [ 'function:getWorkspace' , 'function:getOwnerToken' , 'function:getLoggedInUser' , 'resource:default' ] ,
423
+ [ 'function:getWorkspace' , 'function:getOwnerToken' , 'function:getLoggedInUser' , 'function:getSSHPublicKeys' , ' resource:default'] ,
423
424
{ createIfNone : true }
424
425
) ;
425
426
426
427
const serviceUrl = new URL ( gitpodHost ) ;
427
428
428
- const workspaceInfo = await withServerApi ( session . accessToken , serviceUrl . toString ( ) , service => service . server . getWorkspace ( workspaceId ) , this . logger ) ;
429
+ const [ workspaceInfo , ownerToken , registeredSSHKeys ] = await withServerApi ( session . accessToken , serviceUrl . toString ( ) , service => Promise . all ( [
430
+ service . server . getWorkspace ( workspaceId ) ,
431
+ service . server . getOwnerToken ( workspaceId ) ,
432
+ service . server . getSSHPublicKeys ( )
433
+ ] ) , this . logger ) ;
434
+
435
+
429
436
if ( workspaceInfo . latestInstance ?. status ?. phase !== 'running' ) {
430
437
throw new NoRunningInstanceError ( workspaceId ) ;
431
438
}
@@ -441,9 +448,6 @@ export default class RemoteConnector extends Disposable {
441
448
442
449
const sshHostKeys : { type : string ; host_key : string } [ ] = await sshHostKeyResponse . json ( ) ;
443
450
444
- const ownerToken = await withServerApi ( session . accessToken , serviceUrl . toString ( ) , service => service . server . getOwnerToken ( workspaceId ) , this . logger ) ;
445
-
446
- let password : string | undefined = ownerToken ;
447
451
const sshDestInfo = {
448
452
user : workspaceId ,
449
453
// See https://github.com/gitpod-io/gitpod/pull/9786 for reasoning about `.ssh` suffix
@@ -494,8 +498,28 @@ export default class RemoteConnector extends Disposable {
494
498
this . logger . error ( `Couldn't write '${ sshDestInfo . hostName } ' host to known_hosts file:` , e ) ;
495
499
}
496
500
497
- const identityFiles = await checkDefaultIdentityFiles ( ) ;
498
- this . logger . trace ( `Default identity files:` , identityFiles . length ? identityFiles . toString ( ) : 'None' ) ;
501
+ let identityFilePaths = await checkDefaultIdentityFiles ( ) ;
502
+ this . logger . trace ( `Default identity files:` , identityFilePaths . length ? identityFilePaths . toString ( ) : 'None' ) ;
503
+
504
+ const keyFingerprints = registeredSSHKeys . map ( i => i . fingerprint ) ;
505
+ const publickKeyFiles = await Promise . allSettled ( identityFilePaths . map ( path => fs . promises . readFile ( path + '.pub' ) ) ) ;
506
+ identityFilePaths = identityFilePaths . filter ( ( _ , index ) => {
507
+ const result = publickKeyFiles [ index ] ;
508
+ if ( result . status === 'rejected' ) {
509
+ return false ;
510
+ }
511
+
512
+ const parsedResult = sshUtils . parseKey ( result . value ) ;
513
+ if ( parsedResult instanceof Error || ! parsedResult ) {
514
+ this . logger . error ( `Error while parsing SSH public key${ identityFilePaths [ index ] + '.pub' } :` , parsedResult ) ;
515
+ return false ;
516
+ }
517
+
518
+ const parsedKey = Array . isArray ( parsedResult ) ? parsedResult [ 0 ] : parsedResult ;
519
+ const fingerprint = crypto . createHash ( 'sha256' ) . update ( parsedKey . getPublicSSH ( ) ) . digest ( 'base64' ) ;
520
+ return keyFingerprints . includes ( fingerprint ) ;
521
+ } ) ;
522
+ this . logger . trace ( `Registered public keys in Gitpod account:` , identityFilePaths . length ? identityFilePaths . toString ( ) : 'None' ) ;
499
523
500
524
// Commented this for now as `checkDefaultIdentityFiles` seems enough
501
525
// Connect to the OpenSSH agent and check for registered keys
@@ -519,16 +543,9 @@ export default class RemoteConnector extends Disposable {
519
543
// this.logger.error(`Couldn't get identities from OpenSSH agent`, e);
520
544
// }
521
545
522
- // If user has default identity files or agent have registered keys,
523
- // then use public key authentication
524
- if ( identityFiles . length ) {
525
- sshDestInfo . user = `${ workspaceId } #${ ownerToken } ` ;
526
- password = undefined ;
527
- }
528
-
529
546
return {
530
547
destination : Buffer . from ( JSON . stringify ( sshDestInfo ) , 'utf8' ) . toString ( 'hex' ) ,
531
- password
548
+ password : identityFilePaths . length === 0 ? ownerToken : undefined
532
549
} ;
533
550
}
534
551
@@ -615,13 +632,13 @@ export default class RemoteConnector extends Disposable {
615
632
616
633
const copy = 'Copy' ;
617
634
const configureSSH = 'Configure SSH' ;
618
- const action = await vscode . window . showInformationMessage ( `An SSH key is required for passwordless authentication .\nAlternatively, copy and use this password: ${ maskedPassword } `, { modal : true } , copy , configureSSH ) ;
635
+ const action = await vscode . window . showWarningMessage ( `You don't have registered any SSH public key for this machine in your Gitpod account .\nAlternatively, copy and use this temporary password until workspace restart : ${ maskedPassword } `, { modal : true } , copy , configureSSH ) ;
619
636
if ( action === copy ) {
620
637
await vscode . env . clipboard . writeText ( password ) ;
621
638
return ;
622
639
}
623
640
if ( action === configureSSH ) {
624
- await vscode . env . openExternal ( vscode . Uri . parse ( 'https://www. gitpod.io/docs/configure/ssh#create-an-ssh-key ' ) ) ;
641
+ await vscode . env . openExternal ( vscode . Uri . parse ( 'https://gitpod.io/keys ' ) ) ;
625
642
throw new Error ( `SSH password modal dialog, ${ configureSSH } ` ) ;
626
643
}
627
644
0 commit comments