github.com/HashDataInc/packer@v1.3.2/communicator/ssh/communicator_test.go (about) 1 // +build !race 2 3 package ssh 4 5 import ( 6 "bytes" 7 "fmt" 8 "net" 9 "testing" 10 "time" 11 12 "github.com/hashicorp/packer/packer" 13 "golang.org/x/crypto/ssh" 14 ) 15 16 // private key for mock server 17 const testServerPrivateKey = `-----BEGIN RSA PRIVATE KEY----- 18 MIIEpAIBAAKCAQEA19lGVsTqIT5iiNYRgnoY1CwkbETW5cq+Rzk5v/kTlf31XpSU 19 70HVWkbTERECjaYdXM2gGcbb+sxpq6GtXf1M3kVomycqhxwhPv4Cr6Xp4WT/jkFx 20 9z+FFzpeodGJWjOH6L2H5uX1Cvr9EDdQp9t9/J32/qBFntY8GwoUI/y/1MSTmMiF 21 tupdMODN064vd3gyMKTwrlQ8tZM6aYuyOPsutLlUY7M5x5FwMDYvnPDSeyT/Iw0z 22 s3B+NCyqeeMd2T7YzQFnRATj0M7rM5LoSs7DVqVriOEABssFyLj31PboaoLhOKgc 23 qoM9khkNzr7FHVvi+DhYM2jD0DwvqZLN6NmnLwIDAQABAoIBAQCGVj+kuSFOV1lT 24 +IclQYA6bM6uY5mroqcSBNegVxCNhWU03BxlW//BE9tA/+kq53vWylMeN9mpGZea 25 riEMIh25KFGWXqXlOOioH8bkMsqA8S7sBmc7jljyv+0toQ9vCCtJ+sueNPhxQQxH 26 D2YvUjfzBQ04I9+wn30BByDJ1QA/FoPsunxIOUCcRBE/7jxuLYcpR+JvEF68yYIh 27 atXRld4W4in7T65YDR8jK1Uj9XAcNeDYNpT/M6oFLx1aPIlkG86aCWRO19S1jLPT 28 b1ZAKHHxPMCVkSYW0RqvIgLXQOR62D0Zne6/2wtzJkk5UCjkSQ2z7ZzJpMkWgDgN 29 ifCULFPBAoGBAPoMZ5q1w+zB+knXUD33n1J+niN6TZHJulpf2w5zsW+m2K6Zn62M 30 MXndXlVAHtk6p02q9kxHdgov34Uo8VpuNjbS1+abGFTI8NZgFo+bsDxJdItemwC4 31 KJ7L1iz39hRN/ZylMRLz5uTYRGddCkeIHhiG2h7zohH/MaYzUacXEEy3AoGBANz8 32 e/msleB+iXC0cXKwds26N4hyMdAFE5qAqJXvV3S2W8JZnmU+sS7vPAWMYPlERPk1 33 D8Q2eXqdPIkAWBhrx4RxD7rNc5qFNcQWEhCIxC9fccluH1y5g2M+4jpMX2CT8Uv+ 34 3z+NoJ5uDTXZTnLCfoZzgZ4nCZVZ+6iU5U1+YXFJAoGBANLPpIV920n/nJmmquMj 35 orI1R/QXR9Cy56cMC65agezlGOfTYxk5Cfl5Ve+/2IJCfgzwJyjWUsFx7RviEeGw 36 64o7JoUom1HX+5xxdHPsyZ96OoTJ5RqtKKoApnhRMamau0fWydH1yeOEJd+TRHhc 37 XStGfhz8QNa1dVFvENczja1vAoGABGWhsd4VPVpHMc7lUvrf4kgKQtTC2PjA4xoc 38 QJ96hf/642sVE76jl+N6tkGMzGjnVm4P2j+bOy1VvwQavKGoXqJBRd5Apppv727g 39 /SM7hBXKFc/zH80xKBBgP/i1DR7kdjakCoeu4ngeGywvu2jTS6mQsqzkK+yWbUxJ 40 I7mYBsECgYB/KNXlTEpXtz/kwWCHFSYA8U74l7zZbVD8ul0e56JDK+lLcJ0tJffk 41 gqnBycHj6AhEycjda75cs+0zybZvN4x65KZHOGW/O/7OAWEcZP5TPb3zf9ned3Hl 42 NsZoFj52ponUM6+99A2CmezFCN16c4mbA//luWF+k3VVqR6BpkrhKw== 43 -----END RSA PRIVATE KEY-----` 44 45 var serverConfig = &ssh.ServerConfig{ 46 PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { 47 if c.User() == "user" && string(pass) == "pass" { 48 return nil, nil 49 } 50 return nil, fmt.Errorf("password rejected for %q", c.User()) 51 }, 52 } 53 54 func init() { 55 // Parse and set the private key of the server, required to accept connections 56 signer, err := ssh.ParsePrivateKey([]byte(testServerPrivateKey)) 57 if err != nil { 58 panic("unable to parse private key: " + err.Error()) 59 } 60 serverConfig.AddHostKey(signer) 61 } 62 63 func newMockLineServer(t *testing.T) string { 64 l, err := net.Listen("tcp", "127.0.0.1:0") 65 if err != nil { 66 t.Fatalf("Unable to listen for connection: %s", err) 67 } 68 69 go func() { 70 defer l.Close() 71 c, err := l.Accept() 72 if err != nil { 73 t.Errorf("Unable to accept incoming connection: %s", err) 74 } 75 defer c.Close() 76 conn, chans, _, err := ssh.NewServerConn(c, serverConfig) 77 if err != nil { 78 t.Logf("Handshaking error: %v", err) 79 } 80 t.Log("Accepted SSH connection") 81 for newChannel := range chans { 82 channel, _, err := newChannel.Accept() 83 if err != nil { 84 t.Errorf("Unable to accept channel.") 85 } 86 t.Log("Accepted channel") 87 88 go func(channelType string) { 89 defer channel.Close() 90 conn.OpenChannel(channelType, nil) 91 }(newChannel.ChannelType()) 92 } 93 conn.Close() 94 }() 95 96 return l.Addr().String() 97 } 98 99 func newMockBrokenServer(t *testing.T) string { 100 l, err := net.Listen("tcp", "127.0.0.1:0") 101 if err != nil { 102 t.Fatalf("Unable tp listen for connection: %s", err) 103 } 104 105 go func() { 106 defer l.Close() 107 c, err := l.Accept() 108 if err != nil { 109 t.Errorf("Unable to accept incoming connection: %s", err) 110 } 111 defer c.Close() 112 // This should block for a period of time longer than our timeout in 113 // the test case. That way we invoke a failure scenario. 114 t.Log("Block on handshaking for SSH connection") 115 time.Sleep(5 * time.Second) 116 }() 117 118 return l.Addr().String() 119 } 120 121 func TestCommIsCommunicator(t *testing.T) { 122 var raw interface{} 123 raw = &comm{} 124 if _, ok := raw.(packer.Communicator); !ok { 125 t.Fatalf("comm must be a communicator") 126 } 127 } 128 129 func TestNew_Invalid(t *testing.T) { 130 clientConfig := &ssh.ClientConfig{ 131 User: "user", 132 Auth: []ssh.AuthMethod{ 133 ssh.Password("i-am-invalid"), 134 }, 135 HostKeyCallback: ssh.InsecureIgnoreHostKey(), 136 } 137 138 address := newMockLineServer(t) 139 conn := func() (net.Conn, error) { 140 conn, err := net.Dial("tcp", address) 141 if err != nil { 142 t.Errorf("Unable to accept incoming connection: %v", err) 143 } 144 return conn, err 145 } 146 147 config := &Config{ 148 Connection: conn, 149 SSHConfig: clientConfig, 150 } 151 152 _, err := New(address, config) 153 if err == nil { 154 t.Fatal("should have had an error connecting") 155 } 156 } 157 158 func TestStart(t *testing.T) { 159 clientConfig := &ssh.ClientConfig{ 160 User: "user", 161 Auth: []ssh.AuthMethod{ 162 ssh.Password("pass"), 163 }, 164 HostKeyCallback: ssh.InsecureIgnoreHostKey(), 165 } 166 167 address := newMockLineServer(t) 168 conn := func() (net.Conn, error) { 169 conn, err := net.Dial("tcp", address) 170 if err != nil { 171 t.Fatalf("unable to dial to remote side: %s", err) 172 } 173 return conn, err 174 } 175 176 config := &Config{ 177 Connection: conn, 178 SSHConfig: clientConfig, 179 } 180 181 client, err := New(address, config) 182 if err != nil { 183 t.Fatalf("error connecting to SSH: %s", err) 184 } 185 186 cmd := &packer.RemoteCmd{ 187 Command: "echo foo", 188 Stdout: new(bytes.Buffer), 189 } 190 191 client.Start(cmd) 192 } 193 194 func TestHandshakeTimeout(t *testing.T) { 195 clientConfig := &ssh.ClientConfig{ 196 User: "user", 197 Auth: []ssh.AuthMethod{ 198 ssh.Password("pass"), 199 }, 200 HostKeyCallback: ssh.InsecureIgnoreHostKey(), 201 } 202 203 address := newMockBrokenServer(t) 204 conn := func() (net.Conn, error) { 205 conn, err := net.Dial("tcp", address) 206 if err != nil { 207 t.Fatalf("unable to dial to remote side: %s", err) 208 } 209 return conn, err 210 } 211 212 config := &Config{ 213 Connection: conn, 214 SSHConfig: clientConfig, 215 HandshakeTimeout: 50 * time.Millisecond, 216 } 217 218 _, err := New(address, config) 219 if err != ErrHandshakeTimeout { 220 // Note: there's another error that can come back from this call: 221 // ssh: handshake failed: EOF 222 // This should appear in cases where the handshake fails because of 223 // malformed (or no) data sent back by the server, but should not happen 224 // in a timeout scenario. 225 t.Fatalf("Expected handshake timeout, got: %s", err) 226 } 227 }