github.com/devops-filetransfer/sshego@v7.0.4+incompatible/knownhosts_test.go (about)

     1  package sshego
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/glycerine/sshego/xendor/github.com/glycerine/xcryptossh"
     9  
    10  	cv "github.com/glycerine/goconvey/convey"
    11  )
    12  
    13  func Test301ReadKnownHosts(t *testing.T) {
    14  
    15  	cv.Convey("LoadSshKnownHosts() should read a known hosts file.", t, func() {
    16  		h, err := LoadSshKnownHosts("./testdata/fake_known_hosts")
    17  		panicOn(err)
    18  		cv.So(len(h.Hosts), cv.ShouldEqual, 4)
    19  		// spot check
    20  		a, ok := h.Hosts["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDV9+u9lgOMCrRcRa3CR76eQkoJVFauaCUu7P9XasMCpWaWYK/yGqo/WuMEiA3kysAjPyfBSZ9vkOsJIVlnsgKfQqXXmE1yIQeS0qFz+bHx5QaM4zNTLnh5HcXvs5V//831VvHnwqWCapiUj/akyFc8TQaGmUJ0IzQNF5Z1U6brTFv6w5IVO59dJUCUWwr2x08ol+NKTjMIsTtkaqLE2wDZJNUCjKDHzKDGtz1uM+do1we59PrQ3fLK1wVquiNWG9eG9qsylusJaw8IRQu7VtYLq7Y0hv/SXjzv5rULODdnoQhuKkSz/pG3BwyTkZS/Id2aI4gbRLb40pbNDFZx2iY7jyDFyqlaf2mQRFw7lTrjahTfTtpJpTl5VqJMq6+fVV1sx5YkTaCP/uELd8aTk/KdagDOnSv8s+7utz6TW43L1fJl2Ucwmvb8SvByoLZdbphnUhHxhkJ++UaDBRUpqptT2V+tyjP0mCo6GddJbFPiK6nE2DhWqrVhzo3BkkyPeA0L+VTQnF7dTmgInAjat+eU9IooYUFofkrTq+15iJxW7mNY2wp2sUCi94zCzHi9KvkMHv9tVqOU24dJCfUzXEqdYDmTt04DUtDqYB9w3THQFz6a3bdKcB1zbWXH36/6yhdocfu+lPmb9nMbpLChXMRuaSjBSRbpzcVnKxXoTFrCjw==\n"]
    21  		cv.So(ok, cv.ShouldBeTrue)
    22  
    23  		cv.So(a.Hostname, cv.ShouldEqual, "10.0.0.200:22")
    24  
    25  		b, ok := h.Hosts["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC9hxNTsXHBIuWdc0SZAwN6Bytwr5vCB2K7rf5yVoC5YX5Hb08c25Xd5sGhehAj8RXooNxCa62mDnk/ACcByDa35gv3HyDqm1kmFLNvM/OcNNmK2FCuIdwKG7QWjmZwIwS3eCudJjDGR3qUTUzZbLpV80eZ0WxYE/CbZdb9gx6lNSAWx+ZaeGTt9M0sD5AfEHSxg2lJFaA5pa0Zaaq4QoultLtfisEnTHKCprjRc9RHuZ0l4kwi2eLtBdMmvR3Guk+wrd/qy6+S2zqn4WMDgE50VE6B6ODXN5nsFGrKfqx4mRD3dic28j1rJ7JVkc8sz8/tI+Mr4onomLZftbAFa5dwdiXtqDbOJlxe4sd4oVDImpocAtk+aIqupqN+Sc0JxCGlNvo5eKdNBZP7u/9UC7eee7Y7lHYRmhzoC7FSzFL1/mGgVxrEljcp8UZ1OD47Aq0XYvJA+5MAElbgWrK+M+EMwOGA85qQES5xtvfyVlnNvked6GQlfEuckM6H5bQCIdGkeuJ/+eWWW0rXNVkYHwA4EdiIaAXya4pO439kZfip/gWFF4mazHKCYOQAKndusFSOvxyWOTY/EbSrI7BYoYwm1WR75q7OozJTYP0V3UO+lQ+0/RgSh2uEqyfqB+EMZlATWBl3QnjxKHm7R0dVPnk9qpsjlVXGgGCCWn1UVHKq8w==\n"]
    26  		cv.So(ok, cv.ShouldBeTrue)
    27  
    28  		cv.So(b.Hostname, cv.ShouldEqual, "10.0.0.201:22")
    29  
    30  	})
    31  }
    32  
    33  func Test302ReadKnownHosts(t *testing.T) {
    34  
    35  	cv.Convey("LoadSshKnownHosts() should read a known hosts file.", t, func() {
    36  
    37  		fmt.Printf("\n when a client connects to a new unknown host with -new or TofuAddIfNotKnown=true, we should record the new host in the known hosts file\n")
    38  
    39  		// start a simple TCP server  that is the target of the forward through the sshd,
    40  		// so we can confirm the client has made the connection.
    41  
    42  		// generate a random payload for the client to send to the server.
    43  		payloadByteCount := 50
    44  		confirmationPayload := RandomString(payloadByteCount)
    45  		confirmationReply := RandomString(payloadByteCount)
    46  
    47  		serverDone := ssh.NewHalter()
    48  
    49  		tcpSrvLsn, tcpSrvPort := GetAvailPort()
    50  
    51  		StartBackgroundTestTcpServer(
    52  			serverDone,
    53  			payloadByteCount,
    54  			confirmationPayload,
    55  			confirmationReply,
    56  			tcpSrvLsn, nil)
    57  
    58  		s := MakeTestSshClientAndServer(true)
    59  		defer TempDirCleanup(s.SrvCfg.Origdir, s.SrvCfg.Tempdir)
    60  
    61  		fmt.Printf("\n tell the server to represent itself as B so we can add its key\n")
    62  		bPubKey, err := s.SrvCfg.HostDb.adoptNewHostKeyFromPath(s.SrvCfg.Tempdir + "/testdata/id_rsa_b")
    63  		panicOn(err)
    64  		sbPubKey := string(ssh.MarshalAuthorizedKey(bPubKey))
    65  		fmt.Printf("\n we had the server adopt public key '%s'\n", sbPubKey)
    66  
    67  		// also have to update the Esshd auth state on the update channel:
    68  		s.SrvCfg.Esshd.updateHostKey <- s.SrvCfg.HostDb.HostSshSigner
    69  
    70  		dest := fmt.Sprintf("127.0.0.1:%v", tcpSrvPort)
    71  
    72  		pp("just prior to manual NewKnownHosts call")
    73  		cliKnownHosts, err := NewKnownHosts(s.CliCfg.ClientKnownHostsPath, KHSsh)
    74  		panicOn(err)
    75  
    76  		cv.So(len(cliKnownHosts.Hosts), cv.ShouldEqual, 3)
    77  
    78  		// verify that read of known hosts lacks B
    79  		_, ok := cliKnownHosts.Hosts["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC9hxNTsXHBIuWdc0SZAwN6Bytwr5vCB2K7rf5yVoC5YX5Hb08c25Xd5sGhehAj8RXooNxCa62mDnk/ACcByDa35gv3HyDqm1kmFLNvM/OcNNmK2FCuIdwKG7QWjmZwIwS3eCudJjDGR3qUTUzZbLpV80eZ0WxYE/CbZdb9gx6lNSAWx+ZaeGTt9M0sD5AfEHSxg2lJFaA5pa0Zaaq4QoultLtfisEnTHKCprjRc9RHuZ0l4kwi2eLtBdMmvR3Guk+wrd/qy6+S2zqn4WMDgE50VE6B6ODXN5nsFGrKfqx4mRD3dic28j1rJ7JVkc8sz8/tI+Mr4onomLZftbAFa5dwdiXtqDbOJlxe4sd4oVDImpocAtk+aIqupqN+Sc0JxCGlNvo5eKdNBZP7u/9UC7eee7Y7lHYRmhzoC7FSzFL1/mGgVxrEljcp8UZ1OD47Aq0XYvJA+5MAElbgWrK+M+EMwOGA85qQES5xtvfyVlnNvked6GQlfEuckM6H5bQCIdGkeuJ/+eWWW0rXNVkYHwA4EdiIaAXya4pO439kZfip/gWFF4mazHKCYOQAKndusFSOvxyWOTY/EbSrI7BYoYwm1WR75q7OozJTYP0V3UO+lQ+0/RgSh2uEqyfqB+EMZlATWBl3QnjxKHm7R0dVPnk9qpsjlVXGgGCCWn1UVHKq8w==\n"]
    80  		cv.So(ok, cv.ShouldBeFalse)
    81  
    82  		// but A should have loaded just fine
    83  		_, ok = cliKnownHosts.Hosts["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDV9+u9lgOMCrRcRa3CR76eQkoJVFauaCUu7P9XasMCpWaWYK/yGqo/WuMEiA3kysAjPyfBSZ9vkOsJIVlnsgKfQqXXmE1yIQeS0qFz+bHx5QaM4zNTLnh5HcXvs5V//831VvHnwqWCapiUj/akyFc8TQaGmUJ0IzQNF5Z1U6brTFv6w5IVO59dJUCUWwr2x08ol+NKTjMIsTtkaqLE2wDZJNUCjKDHzKDGtz1uM+do1we59PrQ3fLK1wVquiNWG9eG9qsylusJaw8IRQu7VtYLq7Y0hv/SXjzv5rULODdnoQhuKkSz/pG3BwyTkZS/Id2aI4gbRLb40pbNDFZx2iY7jyDFyqlaf2mQRFw7lTrjahTfTtpJpTl5VqJMq6+fVV1sx5YkTaCP/uELd8aTk/KdagDOnSv8s+7utz6TW43L1fJl2Ucwmvb8SvByoLZdbphnUhHxhkJ++UaDBRUpqptT2V+tyjP0mCo6GddJbFPiK6nE2DhWqrVhzo3BkkyPeA0L+VTQnF7dTmgInAjat+eU9IooYUFofkrTq+15iJxW7mNY2wp2sUCi94zCzHi9KvkMHv9tVqOU24dJCfUzXEqdYDmTt04DUtDqYB9w3THQFz6a3bdKcB1zbWXH36/6yhdocfu+lPmb9nMbpLChXMRuaSjBSRbpzcVnKxXoTFrCjw==\n"]
    84  		cv.So(ok, cv.ShouldBeTrue)
    85  
    86  		// below over SSH should be equivalent of the following
    87  		// non-encrypted ping/pong.
    88  
    89  		dc := DialConfig{
    90  			ClientKnownHostsPath: s.CliCfg.ClientKnownHostsPath,
    91  			KnownHosts:           cliKnownHosts,
    92  			Mylogin:              s.Mylogin,
    93  			RsaPath:              s.RsaPath,
    94  			TotpUrl:              s.Totp,
    95  			Pw:                   s.Pw,
    96  			Sshdhost:             s.SrvCfg.EmbeddedSSHd.Host,
    97  			Sshdport:             s.SrvCfg.EmbeddedSSHd.Port,
    98  			DownstreamHostPort:   dest,
    99  			TofuAddIfNotKnown:    true,
   100  		}
   101  		ctx := context.Background()
   102  
   103  		// first time we add the server key
   104  		channelToTcpServer, _, _, err := dc.Dial(ctx, nil, false)
   105  		cv.So(err.Error(), cv.ShouldContainSubstring, "Re-run without -new")
   106  
   107  		fmt.Printf("\n now host key B should be known.\n")
   108  		cv.So(len(cliKnownHosts.Hosts), cv.ShouldEqual, 4)
   109  
   110  		// show the hosts:
   111  		i := 0
   112  		for k := range cliKnownHosts.Hosts {
   113  			fmt.Printf("%v: we known about host k = '%v'\n", i, k)
   114  			i++
   115  		}
   116  
   117  		// verify that hostkey for B is now present
   118  		_, ok = cliKnownHosts.Hosts["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC9hxNTsXHBIuWdc0SZAwN6Bytwr5vCB2K7rf5yVoC5YX5Hb08c25Xd5sGhehAj8RXooNxCa62mDnk/ACcByDa35gv3HyDqm1kmFLNvM/OcNNmK2FCuIdwKG7QWjmZwIwS3eCudJjDGR3qUTUzZbLpV80eZ0WxYE/CbZdb9gx6lNSAWx+ZaeGTt9M0sD5AfEHSxg2lJFaA5pa0Zaaq4QoultLtfisEnTHKCprjRc9RHuZ0l4kwi2eLtBdMmvR3Guk+wrd/qy6+S2zqn4WMDgE50VE6B6ODXN5nsFGrKfqx4mRD3dic28j1rJ7JVkc8sz8/tI+Mr4onomLZftbAFa5dwdiXtqDbOJlxe4sd4oVDImpocAtk+aIqupqN+Sc0JxCGlNvo5eKdNBZP7u/9UC7eee7Y7lHYRmhzoC7FSzFL1/mGgVxrEljcp8UZ1OD47Aq0XYvJA+5MAElbgWrK+M+EMwOGA85qQES5xtvfyVlnNvked6GQlfEuckM6H5bQCIdGkeuJ/+eWWW0rXNVkYHwA4EdiIaAXya4pO439kZfip/gWFF4mazHKCYOQAKndusFSOvxyWOTY/EbSrI7BYoYwm1WR75q7OozJTYP0V3UO+lQ+0/RgSh2uEqyfqB+EMZlATWBl3QnjxKHm7R0dVPnk9qpsjlVXGgGCCWn1UVHKq8w==\n"]
   119  		cv.So(ok, cv.ShouldBeTrue)
   120  
   121  		// second time we connect based on that server key
   122  		dc.TofuAddIfNotKnown = false
   123  		channelToTcpServer, _, _, err = dc.Dial(ctx, nil, false)
   124  		cv.So(err, cv.ShouldBeNil)
   125  
   126  		VerifyClientServerExchangeAcrossSshd(channelToTcpServer, confirmationPayload, confirmationReply, payloadByteCount)
   127  		channelToTcpServer.Close()
   128  
   129  		// tcp-server should have exited because it got the expected
   130  		// message and replied with the agreed upon reply and then exited.
   131  		serverDone.RequestStop()
   132  		<-serverDone.DoneChan()
   133  
   134  		// done with testing, cleanup
   135  		s.SrvCfg.Esshd.Stop()
   136  		<-s.SrvCfg.Esshd.Halt.DoneChan()
   137  		cv.So(true, cv.ShouldEqual, true) // we should get here.
   138  
   139  	})
   140  }
   141  
   142  func (s *TestSetup) forTestingUpdateServerHostKey(path string) (sbPubKey string) {
   143  
   144  	bPubKey, err := s.SrvCfg.HostDb.adoptNewHostKeyFromPath(path)
   145  	panicOn(err)
   146  	sbPubKey = string(ssh.MarshalAuthorizedKey(bPubKey))
   147  	fmt.Printf("\n we had the server adopt public key '%s'\n", sbPubKey)
   148  
   149  	// also have to update the Esshd auth state on the update channel:
   150  	s.SrvCfg.Esshd.updateHostKey <- s.SrvCfg.HostDb.HostSshSigner
   151  	return
   152  }
   153  
   154  func Test303DedupKnownHosts(t *testing.T) {
   155  
   156  	cv.Convey("known hosts should be deduplicated and not add to the known hosts file every time the same server as before is contacted", t, func() {
   157  
   158  		payloadByteCount := 50
   159  		confirmationPayload := RandomString(payloadByteCount)
   160  		confirmationReply := RandomString(payloadByteCount)
   161  
   162  		serverDone := ssh.NewHalter()
   163  
   164  		tcpSrvLsn, tcpSrvPort := GetAvailPort()
   165  
   166  		StartBackgroundTestTcpServer(
   167  			serverDone,
   168  			payloadByteCount,
   169  			confirmationPayload,
   170  			confirmationReply,
   171  			tcpSrvLsn, nil)
   172  
   173  		s := MakeTestSshClientAndServer(true)
   174  		defer TempDirCleanup(s.SrvCfg.Origdir, s.SrvCfg.Tempdir)
   175  
   176  		fmt.Printf("\n tell the server to represent itself as B so we can add its key\n")
   177  		s.forTestingUpdateServerHostKey(s.SrvCfg.Tempdir + "/testdata/id_rsa_b")
   178  
   179  		dest := fmt.Sprintf("127.0.0.1:%v", tcpSrvPort)
   180  
   181  		pp("just prior to manual NewKnownHosts call")
   182  		cliKnownHosts, err := NewKnownHosts(s.CliCfg.ClientKnownHostsPath, KHSsh)
   183  		panicOn(err)
   184  
   185  		p("cliKnownHosts.Hosts=(len %v) '%#v'", len(cliKnownHosts.Hosts), cliKnownHosts.Hosts)
   186  		cv.So(len(cliKnownHosts.Hosts), cv.ShouldEqual, 3)
   187  
   188  		// verify that read of known hosts lacks B
   189  		_, ok := cliKnownHosts.Hosts["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC9hxNTsXHBIuWdc0SZAwN6Bytwr5vCB2K7rf5yVoC5YX5Hb08c25Xd5sGhehAj8RXooNxCa62mDnk/ACcByDa35gv3HyDqm1kmFLNvM/OcNNmK2FCuIdwKG7QWjmZwIwS3eCudJjDGR3qUTUzZbLpV80eZ0WxYE/CbZdb9gx6lNSAWx+ZaeGTt9M0sD5AfEHSxg2lJFaA5pa0Zaaq4QoultLtfisEnTHKCprjRc9RHuZ0l4kwi2eLtBdMmvR3Guk+wrd/qy6+S2zqn4WMDgE50VE6B6ODXN5nsFGrKfqx4mRD3dic28j1rJ7JVkc8sz8/tI+Mr4onomLZftbAFa5dwdiXtqDbOJlxe4sd4oVDImpocAtk+aIqupqN+Sc0JxCGlNvo5eKdNBZP7u/9UC7eee7Y7lHYRmhzoC7FSzFL1/mGgVxrEljcp8UZ1OD47Aq0XYvJA+5MAElbgWrK+M+EMwOGA85qQES5xtvfyVlnNvked6GQlfEuckM6H5bQCIdGkeuJ/+eWWW0rXNVkYHwA4EdiIaAXya4pO439kZfip/gWFF4mazHKCYOQAKndusFSOvxyWOTY/EbSrI7BYoYwm1WR75q7OozJTYP0V3UO+lQ+0/RgSh2uEqyfqB+EMZlATWBl3QnjxKHm7R0dVPnk9qpsjlVXGgGCCWn1UVHKq8w==\n"]
   190  		cv.So(ok, cv.ShouldBeFalse)
   191  
   192  		// but A should have loaded just fine
   193  		_, ok = cliKnownHosts.Hosts["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDV9+u9lgOMCrRcRa3CR76eQkoJVFauaCUu7P9XasMCpWaWYK/yGqo/WuMEiA3kysAjPyfBSZ9vkOsJIVlnsgKfQqXXmE1yIQeS0qFz+bHx5QaM4zNTLnh5HcXvs5V//831VvHnwqWCapiUj/akyFc8TQaGmUJ0IzQNF5Z1U6brTFv6w5IVO59dJUCUWwr2x08ol+NKTjMIsTtkaqLE2wDZJNUCjKDHzKDGtz1uM+do1we59PrQ3fLK1wVquiNWG9eG9qsylusJaw8IRQu7VtYLq7Y0hv/SXjzv5rULODdnoQhuKkSz/pG3BwyTkZS/Id2aI4gbRLb40pbNDFZx2iY7jyDFyqlaf2mQRFw7lTrjahTfTtpJpTl5VqJMq6+fVV1sx5YkTaCP/uELd8aTk/KdagDOnSv8s+7utz6TW43L1fJl2Ucwmvb8SvByoLZdbphnUhHxhkJ++UaDBRUpqptT2V+tyjP0mCo6GddJbFPiK6nE2DhWqrVhzo3BkkyPeA0L+VTQnF7dTmgInAjat+eU9IooYUFofkrTq+15iJxW7mNY2wp2sUCi94zCzHi9KvkMHv9tVqOU24dJCfUzXEqdYDmTt04DUtDqYB9w3THQFz6a3bdKcB1zbWXH36/6yhdocfu+lPmb9nMbpLChXMRuaSjBSRbpzcVnKxXoTFrCjw==\n"]
   194  		cv.So(ok, cv.ShouldBeTrue)
   195  
   196  		// below over SSH should be equivalent of the following
   197  		// non-encrypted ping/pong.
   198  
   199  		dc := DialConfig{
   200  			ClientKnownHostsPath: s.CliCfg.ClientKnownHostsPath,
   201  			KnownHosts:           cliKnownHosts,
   202  			Mylogin:              s.Mylogin,
   203  			RsaPath:              s.RsaPath,
   204  			TotpUrl:              s.Totp,
   205  			Pw:                   s.Pw,
   206  			Sshdhost:             s.SrvCfg.EmbeddedSSHd.Host,
   207  			Sshdport:             s.SrvCfg.EmbeddedSSHd.Port,
   208  			DownstreamHostPort:   dest,
   209  			TofuAddIfNotKnown:    true,
   210  		}
   211  		ctx := context.Background()
   212  
   213  		// first time we add the server key
   214  		channelToTcpServer, _, _, err := dc.Dial(ctx, nil, false)
   215  		cv.So(err.Error(), cv.ShouldContainSubstring, "Re-run without -new")
   216  
   217  		fmt.Printf("\n now host key B should be known.\n")
   218  		cv.So(len(cliKnownHosts.Hosts), cv.ShouldEqual, 4)
   219  
   220  		// verify that hostkey for B is now present
   221  		_, ok = cliKnownHosts.Hosts["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC9hxNTsXHBIuWdc0SZAwN6Bytwr5vCB2K7rf5yVoC5YX5Hb08c25Xd5sGhehAj8RXooNxCa62mDnk/ACcByDa35gv3HyDqm1kmFLNvM/OcNNmK2FCuIdwKG7QWjmZwIwS3eCudJjDGR3qUTUzZbLpV80eZ0WxYE/CbZdb9gx6lNSAWx+ZaeGTt9M0sD5AfEHSxg2lJFaA5pa0Zaaq4QoultLtfisEnTHKCprjRc9RHuZ0l4kwi2eLtBdMmvR3Guk+wrd/qy6+S2zqn4WMDgE50VE6B6ODXN5nsFGrKfqx4mRD3dic28j1rJ7JVkc8sz8/tI+Mr4onomLZftbAFa5dwdiXtqDbOJlxe4sd4oVDImpocAtk+aIqupqN+Sc0JxCGlNvo5eKdNBZP7u/9UC7eee7Y7lHYRmhzoC7FSzFL1/mGgVxrEljcp8UZ1OD47Aq0XYvJA+5MAElbgWrK+M+EMwOGA85qQES5xtvfyVlnNvked6GQlfEuckM6H5bQCIdGkeuJ/+eWWW0rXNVkYHwA4EdiIaAXya4pO439kZfip/gWFF4mazHKCYOQAKndusFSOvxyWOTY/EbSrI7BYoYwm1WR75q7OozJTYP0V3UO+lQ+0/RgSh2uEqyfqB+EMZlATWBl3QnjxKHm7R0dVPnk9qpsjlVXGgGCCWn1UVHKq8w==\n"]
   222  		cv.So(ok, cv.ShouldBeTrue)
   223  
   224  		// second time we connect based on that server key
   225  		dc.TofuAddIfNotKnown = false
   226  		channelToTcpServer, _, _, err = dc.Dial(ctx, nil, false)
   227  		cv.So(err, cv.ShouldBeNil)
   228  
   229  		VerifyClientServerExchangeAcrossSshd(channelToTcpServer, confirmationPayload, confirmationReply, payloadByteCount)
   230  		channelToTcpServer.Close()
   231  
   232  		// tcp-server should have exited because it got the expected
   233  		// message and replied with the agreed upon reply and then exited.
   234  		serverDone.RequestStop()
   235  		<-serverDone.DoneChan()
   236  
   237  		// now, the point of 303: connecting to a *2nd* sshd server with
   238  		// a different IP address but the same server key should
   239  		// result in de-duplication.
   240  
   241  		serverDone = ssh.NewHalter()
   242  
   243  		tcpSrvLsn, tcpSrvPort = GetAvailPort()
   244  
   245  		StartBackgroundTestTcpServer(
   246  			serverDone,
   247  			payloadByteCount,
   248  			confirmationPayload,
   249  			confirmationReply,
   250  			tcpSrvLsn, nil)
   251  
   252  		// prior to contacting the 2nd B server, we should have
   253  		// only one SplitHostname
   254  		entry, ok := cliKnownHosts.Hosts["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC9hxNTsXHBIuWdc0SZAwN6Bytwr5vCB2K7rf5yVoC5YX5Hb08c25Xd5sGhehAj8RXooNxCa62mDnk/ACcByDa35gv3HyDqm1kmFLNvM/OcNNmK2FCuIdwKG7QWjmZwIwS3eCudJjDGR3qUTUzZbLpV80eZ0WxYE/CbZdb9gx6lNSAWx+ZaeGTt9M0sD5AfEHSxg2lJFaA5pa0Zaaq4QoultLtfisEnTHKCprjRc9RHuZ0l4kwi2eLtBdMmvR3Guk+wrd/qy6+S2zqn4WMDgE50VE6B6ODXN5nsFGrKfqx4mRD3dic28j1rJ7JVkc8sz8/tI+Mr4onomLZftbAFa5dwdiXtqDbOJlxe4sd4oVDImpocAtk+aIqupqN+Sc0JxCGlNvo5eKdNBZP7u/9UC7eee7Y7lHYRmhzoC7FSzFL1/mGgVxrEljcp8UZ1OD47Aq0XYvJA+5MAElbgWrK+M+EMwOGA85qQES5xtvfyVlnNvked6GQlfEuckM6H5bQCIdGkeuJ/+eWWW0rXNVkYHwA4EdiIaAXya4pO439kZfip/gWFF4mazHKCYOQAKndusFSOvxyWOTY/EbSrI7BYoYwm1WR75q7OozJTYP0V3UO+lQ+0/RgSh2uEqyfqB+EMZlATWBl3QnjxKHm7R0dVPnk9qpsjlVXGgGCCWn1UVHKq8w==\n"]
   255  		cv.So(ok, cv.ShouldBeTrue)
   256  		cv.So(len(entry.SplitHostnames), cv.ShouldEqual, 1)
   257  
   258  		// s2 server will be on a new port, so that is enough to
   259  		// check that dedup happened.
   260  		s2 := MakeTestSshClientAndServer(true)
   261  		defer TempDirCleanup(s2.SrvCfg.Origdir, s2.SrvCfg.Tempdir)
   262  
   263  		fmt.Printf("\n tell the server to represent itself as B so we can add its key\n")
   264  		s2.forTestingUpdateServerHostKey(s2.SrvCfg.Tempdir + "/testdata/id_rsa_b")
   265  
   266  		cv.So(len(cliKnownHosts.Hosts), cv.ShouldEqual, 4)
   267  
   268  		dc2 := DialConfig{
   269  			ClientKnownHostsPath: s2.CliCfg.ClientKnownHostsPath,
   270  			KnownHosts:           cliKnownHosts,
   271  			Mylogin:              s2.Mylogin,
   272  			RsaPath:              s2.RsaPath,
   273  			TotpUrl:              s2.Totp,
   274  			Pw:                   s2.Pw,
   275  			Sshdhost:             s2.SrvCfg.EmbeddedSSHd.Host,
   276  			Sshdport:             s2.SrvCfg.EmbeddedSSHd.Port,
   277  			DownstreamHostPort:   dest,
   278  			TofuAddIfNotKnown:    true,
   279  		}
   280  
   281  		// first time we add the server key
   282  		channelToTcpServer, _, _, err = dc2.Dial(ctx, nil, false)
   283  		pp("dc2.Dial() -> err = %#v", err)
   284  		cv.So(err.Error(), cv.ShouldContainSubstring, "Re-run without -new")
   285  
   286  		fmt.Printf("\n now host key B under a different port should be known and dedupped \n")
   287  		cv.So(len(cliKnownHosts.Hosts), cv.ShouldEqual, 4)
   288  
   289  		// verify that hostkey for B now has two SplitHostnames
   290  		entry, ok = cliKnownHosts.Hosts["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC9hxNTsXHBIuWdc0SZAwN6Bytwr5vCB2K7rf5yVoC5YX5Hb08c25Xd5sGhehAj8RXooNxCa62mDnk/ACcByDa35gv3HyDqm1kmFLNvM/OcNNmK2FCuIdwKG7QWjmZwIwS3eCudJjDGR3qUTUzZbLpV80eZ0WxYE/CbZdb9gx6lNSAWx+ZaeGTt9M0sD5AfEHSxg2lJFaA5pa0Zaaq4QoultLtfisEnTHKCprjRc9RHuZ0l4kwi2eLtBdMmvR3Guk+wrd/qy6+S2zqn4WMDgE50VE6B6ODXN5nsFGrKfqx4mRD3dic28j1rJ7JVkc8sz8/tI+Mr4onomLZftbAFa5dwdiXtqDbOJlxe4sd4oVDImpocAtk+aIqupqN+Sc0JxCGlNvo5eKdNBZP7u/9UC7eee7Y7lHYRmhzoC7FSzFL1/mGgVxrEljcp8UZ1OD47Aq0XYvJA+5MAElbgWrK+M+EMwOGA85qQES5xtvfyVlnNvked6GQlfEuckM6H5bQCIdGkeuJ/+eWWW0rXNVkYHwA4EdiIaAXya4pO439kZfip/gWFF4mazHKCYOQAKndusFSOvxyWOTY/EbSrI7BYoYwm1WR75q7OozJTYP0V3UO+lQ+0/RgSh2uEqyfqB+EMZlATWBl3QnjxKHm7R0dVPnk9qpsjlVXGgGCCWn1UVHKq8w==\n"]
   291  		cv.So(ok, cv.ShouldBeTrue)
   292  		cv.So(len(entry.SplitHostnames), cv.ShouldEqual, 2)
   293  
   294  		for k := range entry.SplitHostnames {
   295  			fmt.Printf("entry.SplitHostnames['%v'] is present\n", k)
   296  		}
   297  
   298  		// done with testing, cleanup
   299  		s.SrvCfg.Esshd.Stop()
   300  		<-s.SrvCfg.Esshd.Halt.DoneChan()
   301  		cv.So(true, cv.ShouldEqual, true) // we should get here.
   302  
   303  	})
   304  }