@@ -346,119 +346,201 @@ func validateSSHConfig(c *config.ServerInfo) error {
346
346
if err != nil {
347
347
return xerrors .Errorf ("Failed to lookup ssh binary path. err: %w" , err )
348
348
}
349
- sshKeygenBinaryPath , err := ex .LookPath ("ssh-keygen" )
350
- if err != nil {
351
- return xerrors .Errorf ("Failed to lookup ssh-keygen binary path. err: %w" , err )
352
- }
353
349
354
- sshConfigCmd := []string {sshBinaryPath , "-G" }
355
- if c .SSHConfigPath != "" {
356
- sshConfigCmd = append (sshConfigCmd , "-F" , c .SSHConfigPath )
357
- }
358
- if c .Port != "" {
359
- sshConfigCmd = append (sshConfigCmd , "-p" , c .Port )
360
- }
361
- if c .User != "" {
362
- sshConfigCmd = append (sshConfigCmd , "-l" , c .User )
363
- }
364
- if len (c .JumpServer ) > 0 {
365
- sshConfigCmd = append (sshConfigCmd , "-J" , strings .Join (c .JumpServer , "," ))
366
- }
367
- sshConfigCmd = append (sshConfigCmd , c .Host )
368
- cmd := strings .Join (sshConfigCmd , " " )
369
- logging .Log .Debugf ("Executing... %s" , strings .Replace (cmd , "\n " , "" , - 1 ))
370
- r := localExec (* c , cmd , noSudo )
371
- if ! r .isSuccess () {
372
- return xerrors .Errorf ("Failed to print SSH configuration. err: %w" , r .Error )
373
- }
374
-
375
- var (
376
- hostname string
377
- strictHostKeyChecking string
378
- globalKnownHosts string
379
- userKnownHosts string
380
- proxyCommand string
381
- proxyJump string
382
- )
383
- for _ , line := range strings .Split (r .Stdout , "\n " ) {
384
- switch {
385
- case strings .HasPrefix (line , "user " ):
386
- user := strings .TrimPrefix (line , "user " )
387
- logging .Log .Debugf ("Setting SSH User:%s for Server:%s ..." , user , c .GetServerName ())
388
- c .User = user
389
- case strings .HasPrefix (line , "hostname " ):
390
- hostname = strings .TrimPrefix (line , "hostname " )
391
- case strings .HasPrefix (line , "port " ):
392
- port := strings .TrimPrefix (line , "port " )
393
- logging .Log .Debugf ("Setting SSH Port:%s for Server:%s ..." , port , c .GetServerName ())
394
- c .Port = port
395
- case strings .HasPrefix (line , "stricthostkeychecking " ):
396
- strictHostKeyChecking = strings .TrimPrefix (line , "stricthostkeychecking " )
397
- case strings .HasPrefix (line , "globalknownhostsfile " ):
398
- globalKnownHosts = strings .TrimPrefix (line , "globalknownhostsfile " )
399
- case strings .HasPrefix (line , "userknownhostsfile " ):
400
- userKnownHosts = strings .TrimPrefix (line , "userknownhostsfile " )
401
- case strings .HasPrefix (line , "proxycommand " ):
402
- proxyCommand = strings .TrimPrefix (line , "proxycommand " )
403
- case strings .HasPrefix (line , "proxyjump " ):
404
- proxyJump = strings .TrimPrefix (line , "proxyjump " )
405
- }
406
- }
350
+ sshConfigCmd := buildSSHConfigCmd (sshBinaryPath , c )
351
+ logging .Log .Debugf ("Executing... %s" , strings .Replace (sshConfigCmd , "\n " , "" , - 1 ))
352
+ configResult := localExec (* c , sshConfigCmd , noSudo )
353
+ if ! configResult .isSuccess () {
354
+ return xerrors .Errorf ("Failed to print SSH configuration. err: %w" , configResult .Error )
355
+ }
356
+ sshConfig := parseSSHConfiguration (configResult .Stdout )
357
+ c .User = sshConfig .user
358
+ logging .Log .Debugf ("Setting SSH User:%s for Server:%s ..." , sshConfig .user , c .GetServerName ())
359
+ c .Port = sshConfig .port
360
+ logging .Log .Debugf ("Setting SSH Port:%s for Server:%s ..." , sshConfig .port , c .GetServerName ())
407
361
if c .User == "" || c .Port == "" {
408
362
return xerrors .New ("Failed to find User or Port setting. Please check the User or Port settings for SSH" )
409
363
}
410
- if strictHostKeyChecking == "false" || proxyCommand != "" || proxyJump != "" {
364
+
365
+ if sshConfig .strictHostKeyChecking == "false" {
366
+ return nil
367
+ }
368
+ if sshConfig .proxyCommand != "" || sshConfig .proxyJump != "" {
369
+ logging .Log .Debug ("known_host check under Proxy is not yet implemented" )
411
370
return nil
412
371
}
413
372
414
373
logging .Log .Debugf ("Checking if the host's public key is in known_hosts..." )
415
374
knownHostsPaths := []string {}
416
- for _ , knownHosts := range []string {userKnownHosts , globalKnownHosts } {
417
- for _ , knownHost := range strings .Split (knownHosts , " " ) {
418
- if knownHost != "" && knownHost != "/dev/null" {
419
- knownHostsPaths = append (knownHostsPaths , knownHost )
420
- }
375
+ for _ , knownHost := range append (sshConfig .userKnownHosts , sshConfig .globalKnownHosts ... ) {
376
+ if knownHost != "" && knownHost != "/dev/null" {
377
+ knownHostsPaths = append (knownHostsPaths , knownHost )
421
378
}
422
379
}
423
380
if len (knownHostsPaths ) == 0 {
424
381
return xerrors .New ("Failed to find any known_hosts to use. Please check the UserKnownHostsFile and GlobalKnownHostsFile settings for SSH" )
425
382
}
426
383
384
+ sshKeyscanBinaryPath , err := ex .LookPath ("ssh-keyscan" )
385
+ if err != nil {
386
+ return xerrors .Errorf ("Failed to lookup ssh-keyscan binary path. err: %w" , err )
387
+ }
388
+ sshScanCmd := strings .Join ([]string {sshKeyscanBinaryPath , "-p" , c .Port , sshConfig .hostname }, " " )
389
+ r := localExec (* c , sshScanCmd , noSudo )
390
+ if ! r .isSuccess () {
391
+ return xerrors .Errorf ("Failed to ssh-keyscan. cmd: %s, err: %w" , sshScanCmd , r .Error )
392
+ }
393
+ serverKeys := parseSSHScan (r .Stdout )
394
+
395
+ sshKeygenBinaryPath , err := ex .LookPath ("ssh-keygen" )
396
+ if err != nil {
397
+ return xerrors .Errorf ("Failed to lookup ssh-keygen binary path. err: %w" , err )
398
+ }
427
399
for _ , knownHosts := range knownHostsPaths {
428
- if c .Port != "" && c .Port != "22" {
429
- cmd := fmt .Sprintf ("%s -F %s -f %s" , sshKeygenBinaryPath , fmt .Sprintf ("\" [%s]:%s\" " , hostname , c .Port ), knownHosts )
430
- logging .Log .Debugf ("Executing... %s" , strings .Replace (cmd , "\n " , "" , - 1 ))
431
- if r := localExec (* c , cmd , noSudo ); r .isSuccess () {
432
- return nil
400
+ var hostname string
401
+ if sshConfig .hostKeyAlias != "" {
402
+ hostname = sshConfig .hostKeyAlias
403
+ } else {
404
+ if c .Port != "" && c .Port != "22" {
405
+ hostname = fmt .Sprintf ("\" [%s]:%s\" " , sshConfig .hostname , c .Port )
406
+ } else {
407
+ hostname = sshConfig .hostname
433
408
}
434
409
}
435
410
cmd := fmt .Sprintf ("%s -F %s -f %s" , sshKeygenBinaryPath , hostname , knownHosts )
436
411
logging .Log .Debugf ("Executing... %s" , strings .Replace (cmd , "\n " , "" , - 1 ))
437
412
if r := localExec (* c , cmd , noSudo ); r .isSuccess () {
438
- return nil
413
+ keyType , clientKey , err := parseSSHKeygen (r .Stdout )
414
+ if err != nil {
415
+ return xerrors .Errorf ("Failed to parse ssh-keygen result. stdout: %s, err: %w" , r .Stdout , r .Error )
416
+ }
417
+ if serverKey , ok := serverKeys [keyType ]; ok && serverKey == clientKey {
418
+ return nil
419
+ }
420
+ return xerrors .Errorf ("Failed to find the server key that matches the key registered in the client. The server key may have been changed. Please exec `$ %s` and `$ %s` or `$ %s`" ,
421
+ fmt .Sprintf ("%s -R %s -f %s" , sshKeygenBinaryPath , hostname , knownHosts ),
422
+ strings .Join (buildSSHBaseCmd (sshBinaryPath , c , nil ), " " ),
423
+ buildSSHKeyScanCmd (sshKeyscanBinaryPath , c .Port , knownHostsPaths [0 ], sshConfig ))
439
424
}
440
425
}
426
+ return xerrors .Errorf ("Failed to find the host in known_hosts. Please exec `$ %s` or `$ %s`" ,
427
+ strings .Join (buildSSHBaseCmd (sshBinaryPath , c , nil ), " " ),
428
+ buildSSHKeyScanCmd (sshKeyscanBinaryPath , c .Port , knownHostsPaths [0 ], sshConfig ))
429
+ }
441
430
442
- sshConnArgs := []string {}
443
- sshKeyScanArgs := []string {"-H" }
431
+ func buildSSHBaseCmd (sshBinaryPath string , c * config.ServerInfo , options []string ) []string {
432
+ cmd := []string {sshBinaryPath }
433
+ if len (options ) > 0 {
434
+ cmd = append (cmd , options ... )
435
+ }
444
436
if c .SSHConfigPath != "" {
445
- sshConnArgs = append (sshConnArgs , "-F" , c .SSHConfigPath )
437
+ cmd = append (cmd , "-F" , c .SSHConfigPath )
446
438
}
447
439
if c .KeyPath != "" {
448
- sshConnArgs = append (sshConnArgs , "-i" , c .KeyPath )
440
+ cmd = append (cmd , "-i" , c .KeyPath )
449
441
}
450
442
if c .Port != "" {
451
- sshConnArgs = append (sshConnArgs , "-p" , c .Port )
452
- sshKeyScanArgs = append (sshKeyScanArgs , "-p" , c .Port )
443
+ cmd = append (cmd , "-p" , c .Port )
453
444
}
454
445
if c .User != "" {
455
- sshConnArgs = append (sshConnArgs , "-l" , c .User )
446
+ cmd = append (cmd , "-l" , c .User )
447
+ }
448
+ if len (c .JumpServer ) > 0 {
449
+ cmd = append (cmd , "-J" , strings .Join (c .JumpServer , "," ))
450
+ }
451
+ cmd = append (cmd , c .Host )
452
+ return cmd
453
+ }
454
+
455
+ func buildSSHConfigCmd (sshBinaryPath string , c * config.ServerInfo ) string {
456
+ return strings .Join (buildSSHBaseCmd (sshBinaryPath , c , []string {"-G" }), " " )
457
+ }
458
+
459
+ func buildSSHKeyScanCmd (sshKeyscanBinaryPath , port , knownHosts string , sshConfig sshConfiguration ) string {
460
+ cmd := []string {sshKeyscanBinaryPath }
461
+ if sshConfig .hashKnownHosts == "yes" {
462
+ cmd = append (cmd , "-H" )
463
+ }
464
+ if port != "" {
465
+ cmd = append (cmd , "-p" , port )
466
+ }
467
+ return strings .Join (append (cmd , sshConfig .hostname , ">>" , knownHosts ), " " )
468
+ }
469
+
470
+ type sshConfiguration struct {
471
+ hostname string
472
+ hostKeyAlias string
473
+ hashKnownHosts string
474
+ user string
475
+ port string
476
+ strictHostKeyChecking string
477
+ globalKnownHosts []string
478
+ userKnownHosts []string
479
+ proxyCommand string
480
+ proxyJump string
481
+ }
482
+
483
+ func parseSSHConfiguration (stdout string ) sshConfiguration {
484
+ sshConfig := sshConfiguration {}
485
+ for _ , line := range strings .Split (stdout , "\n " ) {
486
+ switch {
487
+ case strings .HasPrefix (line , "user " ):
488
+ sshConfig .user = strings .TrimPrefix (line , "user " )
489
+ case strings .HasPrefix (line , "hostname " ):
490
+ sshConfig .hostname = strings .TrimPrefix (line , "hostname " )
491
+ case strings .HasPrefix (line , "hostkeyalias " ):
492
+ sshConfig .hostKeyAlias = strings .TrimPrefix (line , "hostkeyalias " )
493
+ case strings .HasPrefix (line , "hashknownhosts " ):
494
+ sshConfig .hashKnownHosts = strings .TrimPrefix (line , "hashknownhosts " )
495
+ case strings .HasPrefix (line , "port " ):
496
+ sshConfig .port = strings .TrimPrefix (line , "port " )
497
+ case strings .HasPrefix (line , "stricthostkeychecking " ):
498
+ sshConfig .strictHostKeyChecking = strings .TrimPrefix (line , "stricthostkeychecking " )
499
+ case strings .HasPrefix (line , "globalknownhostsfile " ):
500
+ sshConfig .globalKnownHosts = strings .Split (strings .TrimPrefix (line , "globalknownhostsfile " ), " " )
501
+ case strings .HasPrefix (line , "userknownhostsfile " ):
502
+ sshConfig .userKnownHosts = strings .Split (strings .TrimPrefix (line , "userknownhostsfile " ), " " )
503
+ case strings .HasPrefix (line , "proxycommand " ):
504
+ sshConfig .proxyCommand = strings .TrimPrefix (line , "proxycommand " )
505
+ case strings .HasPrefix (line , "proxyjump " ):
506
+ sshConfig .proxyJump = strings .TrimPrefix (line , "proxyjump " )
507
+ }
508
+ }
509
+ return sshConfig
510
+ }
511
+
512
+ func parseSSHScan (stdout string ) map [string ]string {
513
+ keys := map [string ]string {}
514
+ for _ , line := range strings .Split (stdout , "\n " ) {
515
+ if line == "" || strings .HasPrefix (line , "# " ) {
516
+ continue
517
+ }
518
+ if ss := strings .Split (line , " " ); len (ss ) == 3 {
519
+ keys [ss [1 ]] = ss [2 ]
520
+ }
521
+ }
522
+ return keys
523
+ }
524
+
525
+ func parseSSHKeygen (stdout string ) (string , string , error ) {
526
+ for _ , line := range strings .Split (stdout , "\n " ) {
527
+ if line == "" || strings .HasPrefix (line , "# " ) {
528
+ continue
529
+ }
530
+
531
+ // HashKnownHosts yes
532
+ if strings .HasPrefix (line , "|1|" ) {
533
+ ss := strings .Split (line , "|" )
534
+ if ss := strings .Split (ss [len (ss )- 1 ], " " ); len (ss ) == 3 {
535
+ return ss [1 ], ss [2 ], nil
536
+ }
537
+ } else {
538
+ if ss := strings .Split (line , " " ); len (ss ) == 3 {
539
+ return ss [1 ], ss [2 ], nil
540
+ }
541
+ }
456
542
}
457
- sshConnArgs = append (sshConnArgs , c .Host )
458
- sshKeyScanArgs = append (sshKeyScanArgs , fmt .Sprintf ("%s >> %s" , hostname , knownHostsPaths [0 ]))
459
- sshConnCmd := fmt .Sprintf ("ssh %s" , strings .Join (sshConnArgs , " " ))
460
- sshKeyScancmd := fmt .Sprintf ("ssh-keyscan %s" , strings .Join (sshKeyScanArgs , " " ))
461
- return xerrors .Errorf ("Failed to find the host in known_hosts. Please exec `$ %s` or `$ %s`" , sshConnCmd , sshKeyScancmd )
543
+ return "" , "" , xerrors .New ("Failed to parse ssh-keygen result. err: public key not found" )
462
544
}
463
545
464
546
func (s Scanner ) detectContainerOSes (hosts []osTypeInterface ) (actives , inactives []osTypeInterface ) {
0 commit comments