3
3
package integration
4
4
5
5
import (
6
+ "bytes"
6
7
"context"
8
+ "errors"
7
9
"fmt"
8
10
"net"
9
11
"net/http"
@@ -422,21 +424,37 @@ qe ssh://
[email protected] :2222/run/podman/podman.sock ~/.ssh/id_rsa false true
422
424
})
423
425
})
424
426
425
- Context ("sshd and API services required" , func () {
426
- BeforeEach (func () {
427
+ // Using "Ordered" or tests would concurrently access
428
+ // the ~/.ssh/know_host file with unexpected results
429
+ Context ("sshd and API services required" , Ordered , func () {
430
+ var khCopyPath , khPath string
431
+ var u * user.User
432
+
433
+ BeforeAll (func () {
427
434
// These tests are unique in as much as they require podman, podman-remote, systemd and sshd.
428
435
// podman-remote commands will be executed by ginkgo directly.
429
436
SkipIfContainerized ("sshd is not available when running in a container" )
430
437
SkipIfRemote ("connection heuristic requires both podman and podman-remote binaries" )
431
438
SkipIfNotRootless (fmt .Sprintf ("FIXME: set up ssh keys when root. uid(%d) euid(%d)" , os .Getuid (), os .Geteuid ()))
432
439
SkipIfSystemdNotRunning ("cannot test connection heuristic if systemd is not running" )
433
440
SkipIfNotActive ("sshd" , "cannot test connection heuristic if sshd is not running" )
434
- })
435
441
436
- It ("add ssh:// socket path using connection heuristic" , func () {
437
- u , err := user .Current ()
442
+ // If the file ~/.ssh/known_host exists, temporarily remove it so that the tests are deterministics
443
+ var err error
444
+ u , err = user .Current ()
438
445
Expect (err ).ShouldNot (HaveOccurred ())
446
+ khPath = filepath .Join (u .HomeDir , ".ssh" , "known_hosts" )
447
+ khCopyPath = khPath + ".copy"
448
+ err = os .Rename (khPath , khCopyPath )
449
+ Expect (err == nil || errors .Is (err , os .ErrNotExist )).To (BeTrue (), fmt .Sprintf ("failed to rename %s to %s" , khPath , khCopyPath ))
450
+ })
439
451
452
+ AfterAll (func () { // codespell:ignore afterall
453
+ err = os .Rename (khCopyPath , khPath )
454
+ Expect (err == nil || errors .Is (err , os .ErrNotExist )).To (BeTrue (), fmt .Sprintf ("failed to rename %s to %s" , khCopyPath , khPath ))
455
+ })
456
+
457
+ It ("add ssh:// socket path using connection heuristic" , func () {
440
458
// Ensure that the remote end uses our built podman
441
459
if os .Getenv ("PODMAN_BINARY" ) == "" {
442
460
err = os .Setenv ("PODMAN_BINARY" , podmanTest .PodmanBinary )
@@ -446,7 +464,6 @@ qe ssh://
[email protected] :2222/run/podman/podman.sock ~/.ssh/id_rsa false true
446
464
os .Unsetenv ("PODMAN_BINARY" )
447
465
}()
448
466
}
449
-
450
467
cmd := exec .Command (podmanTest .RemotePodmanBinary ,
451
468
"system" , "connection" , "add" ,
452
469
"--default" ,
@@ -488,5 +505,119 @@ qe ssh://
[email protected] :2222/run/podman/podman.sock ~/.ssh/id_rsa false true
488
505
Expect (lsSession ).Should (Exit (0 ))
489
506
Expect (string (lsSession .Out .Contents ())).To (Equal ("QA " + uri .String () + " " + filepath .Join (u .HomeDir , ".ssh" , "id_ed25519" ) + " true true\n " ))
490
507
})
508
+
509
+ Describe ("add ssh:// with known_hosts" , func () {
510
+ var (
511
+ initialKnownHostFilesLines map [string ][]string
512
+ currentSSHServerHostname string
513
+ )
514
+
515
+ BeforeAll (func () {
516
+ currentSSHServerHostname = "localhost"
517
+
518
+ // Retrieve current SSH server first two public keys
519
+ // with the command `ssh-keyscan localhost`
520
+ cmd := exec .Command ("ssh-keyscan" , currentSSHServerHostname )
521
+ session , err := Start (cmd , GinkgoWriter , GinkgoWriter )
522
+ Expect (err ).ToNot (HaveOccurred (), fmt .Sprintf ("`ssh-keyscan %s` failed to execute" , currentSSHServerHostname ))
523
+ Eventually (session , DefaultWaitTimeout ).Should (Exit (0 ))
524
+ Expect (session .Out .Contents ()).ShouldNot (BeEmpty (), fmt .Sprintf ("`ssh-keyscan %s` output is empty" , currentSSHServerHostname ))
525
+ serverKeys := bytes .Split (session .Out .Contents (), []byte ("\n " ))
526
+ Expect (len (serverKeys )).Should (BeNumerically (">=" , 2 ), fmt .Sprintf ("`ssh-keyscan %s` returned less then 2 keys" , currentSSHServerHostname ))
527
+
528
+ initialKnownHostFilesLines = map [string ][]string {
529
+ "serverFirstKey" : {string (serverKeys [0 ])},
530
+ "serverSecondKey" : {string (serverKeys [1 ])},
531
+ "fakeKey" : {currentSSHServerHostname + " ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGnBlHlwqleAtyzT01mLa+DXQFyxX8T0oa8odcEZ2/07" },
532
+ "differentHostKey" : {"differentserver ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGnBlHlwqleAtyzT01mLa+DXQFyxX8T0oa8odcEZ2/07" },
533
+ "empty" : {},
534
+ }
535
+ })
536
+
537
+ DescribeTable ("->" ,
538
+ func (label string , shouldFail bool , shouldAddKey bool ) {
539
+ initialKhLines , ok := initialKnownHostFilesLines [label ]
540
+ Expect (ok ).To (BeTrue (), fmt .Sprintf ("label %q not found in kh" , label ))
541
+ // Create known_hosts file if needed
542
+ if len (initialKhLines ) > 0 {
543
+ khFile , err := os .Create (khPath )
544
+ Expect (err ).ToNot (HaveOccurred (), fmt .Sprintf ("failed to create %s" , khPath ))
545
+ defer khFile .Close ()
546
+ err = os .WriteFile (khPath , []byte (strings .Join (initialKhLines , "\n " )), 0600 )
547
+ Expect (err ).ToNot (HaveOccurred (), fmt .Sprintf ("failed to write to %s" , khPath ))
548
+ }
549
+ // Ensure that the remote end uses our built podman
550
+ if os .Getenv ("PODMAN_BINARY" ) == "" {
551
+ err = os .Setenv ("PODMAN_BINARY" , podmanTest .PodmanBinary )
552
+ Expect (err ).ShouldNot (HaveOccurred ())
553
+
554
+ defer func () {
555
+ os .Unsetenv ("PODMAN_BINARY" )
556
+ }()
557
+ }
558
+ // Run podman system connection add
559
+ cmd := exec .Command (podmanTest .RemotePodmanBinary ,
560
+ "system" , "connection" , "add" ,
561
+ "--default" ,
562
+ "--identity" , filepath .Join (u .HomeDir , ".ssh" , "id_ed25519" ),
563
+ "QA" ,
564
+ fmt .Sprintf ("ssh://%s@%s" , u .Username , currentSSHServerHostname ))
565
+ session , err := Start (cmd , GinkgoWriter , GinkgoWriter )
566
+ Expect (err ).ToNot (HaveOccurred (), fmt .Sprintf ("%q failed to execute" , podmanTest .RemotePodmanBinary ))
567
+ Expect (session .Out .Contents ()).Should (BeEmpty ())
568
+ if shouldFail {
569
+ Eventually (session , DefaultWaitTimeout ).Should (Exit (125 ))
570
+ Expect (session .Err .Contents ()).ShouldNot (BeEmpty ())
571
+ } else {
572
+ Eventually (session , DefaultWaitTimeout ).Should (Exit (0 ))
573
+ Expect (session .Err .Contents ()).Should (BeEmpty ())
574
+ }
575
+ // If the known_hosts file didn't exist, it should
576
+ // have been created
577
+ if len (initialKhLines ) == 0 {
578
+ Expect (khPath ).To (BeAnExistingFile ())
579
+ defer os .Remove (khPath )
580
+ }
581
+ // If the known_hosts file didn't contain the SSH server
582
+ // public key it should have been added
583
+ if shouldAddKey {
584
+ khFileContent , err := os .ReadFile (khPath )
585
+ Expect (err ).ToNot (HaveOccurred ())
586
+ khLines := bytes .Split (khFileContent , []byte ("\n " ))
587
+ Expect (len (khLines )).To (BeNumerically (">" , len (initialKhLines )))
588
+ }
589
+ },
590
+ Entry (
591
+ "with a public key of the SSH server that matches the SSH server first key" ,
592
+ "serverFirstKey" ,
593
+ false ,
594
+ false ,
595
+ ),
596
+ Entry (
597
+ "with a public key of the SSH server that matches SSH server second key" ,
598
+ "serverSecondKey" ,
599
+ false ,
600
+ false ,
601
+ ),
602
+ Entry (
603
+ "with a fake public key of the SSH server that doesn't match any of the SSH server keys (should fail)" ,
604
+ "fakeKey" ,
605
+ true ,
606
+ false ,
607
+ ),
608
+ Entry (
609
+ "with no public key for the SSH server (new key should be added)" ,
610
+ "differentHostKey" ,
611
+ false ,
612
+ true ,
613
+ ),
614
+ Entry (
615
+ "not existing (should be created and a new key should be added)" ,
616
+ "empty" ,
617
+ false ,
618
+ true ,
619
+ ),
620
+ )
621
+ })
491
622
})
492
623
})
0 commit comments