github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/communicator/ssh/communicator_test.go (about) 1 //go:build !race 2 // +build !race 3 4 package ssh 5 6 import ( 7 "bufio" 8 "bytes" 9 "encoding/base64" 10 "fmt" 11 "io" 12 "io/ioutil" 13 "math/rand" 14 "net" 15 "os" 16 "path/filepath" 17 "regexp" 18 "strconv" 19 "strings" 20 "testing" 21 "time" 22 23 "github.com/muratcelep/terraform/not-internal/communicator/remote" 24 "github.com/zclconf/go-cty/cty" 25 "golang.org/x/crypto/ssh" 26 ) 27 28 // private key for mock server 29 const testServerPrivateKey = `-----BEGIN RSA PRIVATE KEY----- 30 MIIEpAIBAAKCAQEA19lGVsTqIT5iiNYRgnoY1CwkbETW5cq+Rzk5v/kTlf31XpSU 31 70HVWkbTERECjaYdXM2gGcbb+sxpq6GtXf1M3kVomycqhxwhPv4Cr6Xp4WT/jkFx 32 9z+FFzpeodGJWjOH6L2H5uX1Cvr9EDdQp9t9/J32/qBFntY8GwoUI/y/1MSTmMiF 33 tupdMODN064vd3gyMKTwrlQ8tZM6aYuyOPsutLlUY7M5x5FwMDYvnPDSeyT/Iw0z 34 s3B+NCyqeeMd2T7YzQFnRATj0M7rM5LoSs7DVqVriOEABssFyLj31PboaoLhOKgc 35 qoM9khkNzr7FHVvi+DhYM2jD0DwvqZLN6NmnLwIDAQABAoIBAQCGVj+kuSFOV1lT 36 +IclQYA6bM6uY5mroqcSBNegVxCNhWU03BxlW//BE9tA/+kq53vWylMeN9mpGZea 37 riEMIh25KFGWXqXlOOioH8bkMsqA8S7sBmc7jljyv+0toQ9vCCtJ+sueNPhxQQxH 38 D2YvUjfzBQ04I9+wn30BByDJ1QA/FoPsunxIOUCcRBE/7jxuLYcpR+JvEF68yYIh 39 atXRld4W4in7T65YDR8jK1Uj9XAcNeDYNpT/M6oFLx1aPIlkG86aCWRO19S1jLPT 40 b1ZAKHHxPMCVkSYW0RqvIgLXQOR62D0Zne6/2wtzJkk5UCjkSQ2z7ZzJpMkWgDgN 41 ifCULFPBAoGBAPoMZ5q1w+zB+knXUD33n1J+niN6TZHJulpf2w5zsW+m2K6Zn62M 42 MXndXlVAHtk6p02q9kxHdgov34Uo8VpuNjbS1+abGFTI8NZgFo+bsDxJdItemwC4 43 KJ7L1iz39hRN/ZylMRLz5uTYRGddCkeIHhiG2h7zohH/MaYzUacXEEy3AoGBANz8 44 e/msleB+iXC0cXKwds26N4hyMdAFE5qAqJXvV3S2W8JZnmU+sS7vPAWMYPlERPk1 45 D8Q2eXqdPIkAWBhrx4RxD7rNc5qFNcQWEhCIxC9fccluH1y5g2M+4jpMX2CT8Uv+ 46 3z+NoJ5uDTXZTnLCfoZzgZ4nCZVZ+6iU5U1+YXFJAoGBANLPpIV920n/nJmmquMj 47 orI1R/QXR9Cy56cMC65agezlGOfTYxk5Cfl5Ve+/2IJCfgzwJyjWUsFx7RviEeGw 48 64o7JoUom1HX+5xxdHPsyZ96OoTJ5RqtKKoApnhRMamau0fWydH1yeOEJd+TRHhc 49 XStGfhz8QNa1dVFvENczja1vAoGABGWhsd4VPVpHMc7lUvrf4kgKQtTC2PjA4xoc 50 QJ96hf/642sVE76jl+N6tkGMzGjnVm4P2j+bOy1VvwQavKGoXqJBRd5Apppv727g 51 /SM7hBXKFc/zH80xKBBgP/i1DR7kdjakCoeu4ngeGywvu2jTS6mQsqzkK+yWbUxJ 52 I7mYBsECgYB/KNXlTEpXtz/kwWCHFSYA8U74l7zZbVD8ul0e56JDK+lLcJ0tJffk 53 gqnBycHj6AhEycjda75cs+0zybZvN4x65KZHOGW/O/7OAWEcZP5TPb3zf9ned3Hl 54 NsZoFj52ponUM6+99A2CmezFCN16c4mbA//luWF+k3VVqR6BpkrhKw== 55 -----END RSA PRIVATE KEY-----` 56 57 // this cert was signed by the key from testCAPublicKey 58 const testServerHostCert = `ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgvQ3Bs1ex7277b9q6I0fNaWsVEC16f+LcT8RLPSVMEVMAAAADAQABAAABAQDX2UZWxOohPmKI1hGCehjULCRsRNblyr5HOTm/+ROV/fVelJTvQdVaRtMREQKNph1czaAZxtv6zGmroa1d/UzeRWibJyqHHCE+/gKvpenhZP+OQXH3P4UXOl6h0YlaM4fovYfm5fUK+v0QN1Cn2338nfb+oEWe1jwbChQj/L/UxJOYyIW26l0w4M3Tri93eDIwpPCuVDy1kzppi7I4+y60uVRjsznHkXAwNi+c8NJ7JP8jDTOzcH40LKp54x3ZPtjNAWdEBOPQzuszkuhKzsNWpWuI4QAGywXIuPfU9uhqguE4qByqgz2SGQ3OvsUdW+L4OFgzaMPQPC+pks3o2acvAAAAAAAAAAAAAAACAAAAB2NhLXRlc3QAAAANAAAACTEyNy4wLjAuMQAAAABag0jkAAAAAHDcHtAAAAAAAAAAAAAAAAAAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQCrozyZIhdEvalCn+eSzHH94cO9ykiywA13ntWI7mJcHBwYTeCYWG8E9zGXyp2iDOjCGudM0Tdt8o0OofKChk9Z/qiUN0G8y1kmaXBlBM3qA5R9NPpvMYMNkYLfX6ivtZCnqrsbzaoqN2Oc/7H2StHzJWh/XCGu9otQZA6vdv1oSmAsZOjw/xIGaGQqDUaLq21J280PP1qSbdJHf76iSHE+TWe3YpqV946JWM5tCh0DykZ10VznvxYpUjzhr07IN3tVKxOXbPnnU7lX6IaLIWgfzLqwSyheeux05c3JLF9iF4sFu8ou4hwQz1iuUTU1jxgwZP0w/bkXgFFs0949lW81AAABDwAAAAdzc2gtcnNhAAABAEyoiVkZ5z79nh3WSU5mU2U7e2BItnnEqsJIm9EN+35uG0yORSXmQoaa9mtli7G3r79tyqEJd/C95EdNvU/9TjaoDcbH8OHP+Ue9XSfUzBuQ6bGSXe6mlZlO7QJ1cIyWphFP3MkrweDSiJ+SpeXzLzZkiJ7zKv5czhBEyG/MujFgvikotL+eUNG42y2cgsesXSjENSBS3l11q55a+RM2QKt3W32im8CsSxrH6Mz6p4JXQNgsVvZRknLxNlWXULFB2HLTunPKzJNMTf6xZf66oivSBAXVIdNKhlVpAQ3dT/dW5K6J4aQF/hjWByyLprFwZ16cPDqvtalnTCpbRYelNbw=` 59 60 const testCAPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCrozyZIhdEvalCn+eSzHH94cO9ykiywA13ntWI7mJcHBwYTeCYWG8E9zGXyp2iDOjCGudM0Tdt8o0OofKChk9Z/qiUN0G8y1kmaXBlBM3qA5R9NPpvMYMNkYLfX6ivtZCnqrsbzaoqN2Oc/7H2StHzJWh/XCGu9otQZA6vdv1oSmAsZOjw/xIGaGQqDUaLq21J280PP1qSbdJHf76iSHE+TWe3YpqV946JWM5tCh0DykZ10VznvxYpUjzhr07IN3tVKxOXbPnnU7lX6IaLIWgfzLqwSyheeux05c3JLF9iF4sFu8ou4hwQz1iuUTU1jxgwZP0w/bkXgFFs0949lW81` 61 62 func newMockLineServer(t *testing.T, signer ssh.Signer, pubKey string) string { 63 serverConfig := &ssh.ServerConfig{ 64 PasswordCallback: acceptUserPass("user", "pass"), 65 PublicKeyCallback: acceptPublicKey(pubKey), 66 } 67 68 var err error 69 if signer == nil { 70 signer, err = ssh.ParsePrivateKey([]byte(testServerPrivateKey)) 71 if err != nil { 72 t.Fatalf("unable to parse private key: %s", err) 73 } 74 } 75 serverConfig.AddHostKey(signer) 76 77 l, err := net.Listen("tcp", "127.0.0.1:0") 78 if err != nil { 79 t.Fatalf("Unable to listen for connection: %s", err) 80 } 81 82 go func() { 83 defer l.Close() 84 c, err := l.Accept() 85 if err != nil { 86 t.Errorf("Unable to accept incoming connection: %s", err) 87 } 88 defer c.Close() 89 conn, chans, _, err := ssh.NewServerConn(c, serverConfig) 90 if err != nil { 91 t.Logf("Handshaking error: %v", err) 92 } 93 t.Log("Accepted SSH connection") 94 95 for newChannel := range chans { 96 channel, requests, err := newChannel.Accept() 97 if err != nil { 98 t.Errorf("Unable to accept channel.") 99 } 100 t.Log("Accepted channel") 101 102 go func(in <-chan *ssh.Request) { 103 defer channel.Close() 104 for req := range in { 105 // since this channel's requests are serviced serially, 106 // this will block keepalive probes, and can simulate a 107 // hung connection. 108 if bytes.Contains(req.Payload, []byte("sleep")) { 109 time.Sleep(time.Second) 110 } 111 112 if req.WantReply { 113 req.Reply(true, nil) 114 } 115 } 116 }(requests) 117 } 118 conn.Close() 119 }() 120 121 return l.Addr().String() 122 } 123 124 func TestNew_Invalid(t *testing.T) { 125 address := newMockLineServer(t, nil, testClientPublicKey) 126 parts := strings.Split(address, ":") 127 128 v := cty.ObjectVal(map[string]cty.Value{ 129 "type": cty.StringVal("ssh"), 130 "user": cty.StringVal("user"), 131 "password": cty.StringVal("i-am-invalid"), 132 "host": cty.StringVal(parts[0]), 133 "port": cty.StringVal(parts[1]), 134 "timeout": cty.StringVal("30s"), 135 }) 136 137 c, err := New(v) 138 if err != nil { 139 t.Fatalf("error creating communicator: %s", err) 140 } 141 142 err = c.Connect(nil) 143 if err == nil { 144 t.Fatal("should have had an error connecting") 145 } 146 } 147 148 func TestNew_InvalidHost(t *testing.T) { 149 v := cty.ObjectVal(map[string]cty.Value{ 150 "type": cty.StringVal("ssh"), 151 "user": cty.StringVal("user"), 152 "password": cty.StringVal("i-am-invalid"), 153 "port": cty.StringVal("22"), 154 "timeout": cty.StringVal("30s"), 155 }) 156 157 _, err := New(v) 158 if err == nil { 159 t.Fatal("should have had an error creating communicator") 160 } 161 } 162 163 func TestStart(t *testing.T) { 164 address := newMockLineServer(t, nil, testClientPublicKey) 165 parts := strings.Split(address, ":") 166 167 v := cty.ObjectVal(map[string]cty.Value{ 168 "type": cty.StringVal("ssh"), 169 "user": cty.StringVal("user"), 170 "password": cty.StringVal("pass"), 171 "host": cty.StringVal(parts[0]), 172 "port": cty.StringVal(parts[1]), 173 "timeout": cty.StringVal("30s"), 174 }) 175 176 c, err := New(v) 177 if err != nil { 178 t.Fatalf("error creating communicator: %s", err) 179 } 180 181 var cmd remote.Cmd 182 stdout := new(bytes.Buffer) 183 cmd.Command = "echo foo" 184 cmd.Stdout = stdout 185 186 err = c.Start(&cmd) 187 if err != nil { 188 t.Fatalf("error executing remote command: %s", err) 189 } 190 } 191 192 // TestKeepAlives verifies that the keepalive messages don't interfere with 193 // normal operation of the client. 194 func TestKeepAlives(t *testing.T) { 195 ivl := keepAliveInterval 196 keepAliveInterval = 250 * time.Millisecond 197 defer func() { keepAliveInterval = ivl }() 198 199 address := newMockLineServer(t, nil, testClientPublicKey) 200 parts := strings.Split(address, ":") 201 202 v := cty.ObjectVal(map[string]cty.Value{ 203 "type": cty.StringVal("ssh"), 204 "user": cty.StringVal("user"), 205 "password": cty.StringVal("pass"), 206 "host": cty.StringVal(parts[0]), 207 "port": cty.StringVal(parts[1]), 208 }) 209 210 c, err := New(v) 211 if err != nil { 212 t.Fatalf("error creating communicator: %s", err) 213 } 214 215 if err := c.Connect(nil); err != nil { 216 t.Fatal(err) 217 } 218 219 var cmd remote.Cmd 220 stdout := new(bytes.Buffer) 221 cmd.Command = "sleep" 222 cmd.Stdout = stdout 223 224 // wait a bit before executing the command, so that at least 1 keepalive is sent 225 time.Sleep(500 * time.Millisecond) 226 227 err = c.Start(&cmd) 228 if err != nil { 229 t.Fatalf("error executing remote command: %s", err) 230 } 231 } 232 233 // TestDeadConnection verifies that failed keepalive messages will eventually 234 // kill the connection. 235 func TestFailedKeepAlives(t *testing.T) { 236 ivl := keepAliveInterval 237 del := maxKeepAliveDelay 238 maxKeepAliveDelay = 500 * time.Millisecond 239 keepAliveInterval = 250 * time.Millisecond 240 defer func() { 241 keepAliveInterval = ivl 242 maxKeepAliveDelay = del 243 }() 244 245 address := newMockLineServer(t, nil, testClientPublicKey) 246 parts := strings.Split(address, ":") 247 248 v := cty.ObjectVal(map[string]cty.Value{ 249 "type": cty.StringVal("ssh"), 250 "user": cty.StringVal("user"), 251 "password": cty.StringVal("pass"), 252 "host": cty.StringVal(parts[0]), 253 "port": cty.StringVal(parts[1]), 254 "timeout": cty.StringVal("30s"), 255 }) 256 257 c, err := New(v) 258 if err != nil { 259 t.Fatalf("error creating communicator: %s", err) 260 } 261 262 if err := c.Connect(nil); err != nil { 263 t.Fatal(err) 264 } 265 var cmd remote.Cmd 266 stdout := new(bytes.Buffer) 267 cmd.Command = "sleep" 268 cmd.Stdout = stdout 269 270 err = c.Start(&cmd) 271 if err == nil { 272 t.Fatal("expected connection error") 273 } 274 } 275 276 func TestLostConnection(t *testing.T) { 277 address := newMockLineServer(t, nil, testClientPublicKey) 278 parts := strings.Split(address, ":") 279 280 v := cty.ObjectVal(map[string]cty.Value{ 281 "type": cty.StringVal("ssh"), 282 "user": cty.StringVal("user"), 283 "password": cty.StringVal("pass"), 284 "host": cty.StringVal(parts[0]), 285 "port": cty.StringVal(parts[1]), 286 "timeout": cty.StringVal("30s"), 287 }) 288 289 c, err := New(v) 290 if err != nil { 291 t.Fatalf("error creating communicator: %s", err) 292 } 293 294 var cmd remote.Cmd 295 stdout := new(bytes.Buffer) 296 cmd.Command = "echo foo" 297 cmd.Stdout = stdout 298 299 err = c.Start(&cmd) 300 if err != nil { 301 t.Fatalf("error executing remote command: %s", err) 302 } 303 304 // The test server can't execute anything, so Wait will block, unless 305 // there's an error. Disconnect the communicator transport, to cause the 306 // command to fail. 307 go func() { 308 time.Sleep(100 * time.Millisecond) 309 c.Disconnect() 310 }() 311 312 err = cmd.Wait() 313 if err == nil { 314 t.Fatal("expected communicator error") 315 } 316 } 317 318 func TestHostKey(t *testing.T) { 319 // get the server's public key 320 signer, err := ssh.ParsePrivateKey([]byte(testServerPrivateKey)) 321 if err != nil { 322 t.Fatalf("unable to parse private key: %v", err) 323 } 324 pubKey := fmt.Sprintf("ssh-rsa %s", base64.StdEncoding.EncodeToString(signer.PublicKey().Marshal())) 325 326 address := newMockLineServer(t, nil, testClientPublicKey) 327 host, p, _ := net.SplitHostPort(address) 328 port, _ := strconv.Atoi(p) 329 330 connInfo := &connectionInfo{ 331 User: "user", 332 Password: "pass", 333 Host: host, 334 HostKey: pubKey, 335 Port: uint16(port), 336 Timeout: "30s", 337 } 338 339 cfg, err := prepareSSHConfig(connInfo) 340 if err != nil { 341 t.Fatal(err) 342 } 343 344 c := &Communicator{ 345 connInfo: connInfo, 346 config: cfg, 347 } 348 349 var cmd remote.Cmd 350 stdout := new(bytes.Buffer) 351 cmd.Command = "echo foo" 352 cmd.Stdout = stdout 353 354 if err := c.Start(&cmd); err != nil { 355 t.Fatal(err) 356 } 357 if err := c.Disconnect(); err != nil { 358 t.Fatal(err) 359 } 360 361 // now check with the wrong HostKey 362 address = newMockLineServer(t, nil, testClientPublicKey) 363 _, p, _ = net.SplitHostPort(address) 364 port, _ = strconv.Atoi(p) 365 366 connInfo.HostKey = testClientPublicKey 367 connInfo.Port = uint16(port) 368 369 cfg, err = prepareSSHConfig(connInfo) 370 if err != nil { 371 t.Fatal(err) 372 } 373 374 c = &Communicator{ 375 connInfo: connInfo, 376 config: cfg, 377 } 378 379 err = c.Start(&cmd) 380 if err == nil || !strings.Contains(err.Error(), "mismatch") { 381 t.Fatalf("expected host key mismatch, got error:%v", err) 382 } 383 } 384 385 func TestHostCert(t *testing.T) { 386 pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(testServerHostCert)) 387 if err != nil { 388 t.Fatal(err) 389 } 390 391 signer, err := ssh.ParsePrivateKey([]byte(testServerPrivateKey)) 392 if err != nil { 393 t.Fatal(err) 394 } 395 396 signer, err = ssh.NewCertSigner(pk.(*ssh.Certificate), signer) 397 if err != nil { 398 t.Fatal(err) 399 } 400 401 address := newMockLineServer(t, signer, testClientPublicKey) 402 host, p, _ := net.SplitHostPort(address) 403 port, _ := strconv.Atoi(p) 404 405 connInfo := &connectionInfo{ 406 User: "user", 407 Password: "pass", 408 Host: host, 409 HostKey: testCAPublicKey, 410 Port: uint16(port), 411 Timeout: "30s", 412 } 413 414 cfg, err := prepareSSHConfig(connInfo) 415 if err != nil { 416 t.Fatal(err) 417 } 418 419 c := &Communicator{ 420 connInfo: connInfo, 421 config: cfg, 422 } 423 424 var cmd remote.Cmd 425 stdout := new(bytes.Buffer) 426 cmd.Command = "echo foo" 427 cmd.Stdout = stdout 428 429 if err := c.Start(&cmd); err != nil { 430 t.Fatal(err) 431 } 432 if err := c.Disconnect(); err != nil { 433 t.Fatal(err) 434 } 435 436 // now check with the wrong HostKey 437 address = newMockLineServer(t, signer, testClientPublicKey) 438 _, p, _ = net.SplitHostPort(address) 439 port, _ = strconv.Atoi(p) 440 441 connInfo.HostKey = testClientPublicKey 442 connInfo.Port = uint16(port) 443 444 cfg, err = prepareSSHConfig(connInfo) 445 if err != nil { 446 t.Fatal(err) 447 } 448 449 c = &Communicator{ 450 connInfo: connInfo, 451 config: cfg, 452 } 453 454 err = c.Start(&cmd) 455 if err == nil || !strings.Contains(err.Error(), "authorities") { 456 t.Fatalf("expected host key mismatch, got error:%v", err) 457 } 458 } 459 460 const SERVER_PEM = `-----BEGIN RSA PRIVATE KEY----- 461 MIIEpAIBAAKCAQEA8CkDr7uxCFt6lQUVwS8NyPO+fQNxORoGnMnN/XhVJZvpqyKR 462 Uji9R0d8D66bYxUUsabXjP2y4HTVzbZtnvXFZZshk0cOtJjjekpYJaLK2esPR/iX 463 wvSltNkrDQDPN/RmgEEMIevW8AgrPsqrnybFHxTpd7rEUHXBOe4nMNRIg3XHykB6 464 jZk8q5bBPUe3I/f0DK5TJEBpTc6dO3P/j93u55VUqr39/SPRHnld2mCw+c8v6UOh 465 sssO/DIZFPScD3DYqsk2N+/nz9zXfcOTdWGhawgxuIo1DTokrNQbG3pDrLqcWgqj 466 13vqJFCmRA0O2CQIwJePd6+Np/XO3Uh/KL6FlQIDAQABAoIBAQCmvQMXNmvCDqk7 467 30zsVDvw4fHGH+azK3Od1aqTqcEMHISOUbCtckFPxLzIsoSltRQqB1kuRVG07skm 468 Stsu+xny4lLcSwBVuLRuykEK2EyYIc/5Owo6y9pkhkaSf5ZfFes4bnD6+B/BhRpp 469 PRMMq0E+xCkX/G6iIi9mhgdlqm0x/vKtjzQeeshw9+gRcRLUpX+UeKFKXMXcDayx 470 qekr1bAaQKNBhTK+CbZjcqzG4f+BXVGRTZ9nsPAV+yTnWUCU0TghwPmtthHbebqa 471 9hlkum7qik/bQj/tjJ8/b0vTfHQSVxhtPG/ZV2Tn9ZuL/vrkYqeyMU8XkJ/uaEvH 472 WPyOcB4BAoGBAP5o5JSEtPog+U3JFrLNSRjz5ofZNVkJzice+0XyqlzJDHhX5tF8 473 mriYQZLLXYhckBm4IdkhTn/dVbXNQTzyy2WVuO5nU8bkCMvGL9CGpW4YGqwGf7NX 474 e4H3emtRjLv8VZpUHe/RUUDhmYvMSt1qmXuskfpROuGfLhQBUd6A4J+BAoGBAPGp 475 UcMKjrxZ5qjYU6DLgS+xeca4Eu70HgdbSQbRo45WubXjyXvTRFij36DrpxJWf1D7 476 lIsyBifoTra/lAuC1NQXGYWjTCdk2ey8Ll5qOgiXvE6lINHABr+U/Z90/g6LuML2 477 VzaZbq/QLcT3yVsdyTogKckzCaKsCpusyHE1CXAVAoGAd6kMglKc8N0bhZukgnsN 478 +5+UeacPcY6sGTh4RWErAjNKGzx1A2lROKvcg9gFaULoQECcIw2IZ5nKW5VsLueg 479 BWrTrcaJ4A2XmYjhKnp6SvspaGoyHD90hx/Iw7t6r1yzQsB3yDmytwqldtyjBdvC 480 zynPC2azhDWjraMlR7tka4ECgYAxwvLiHa9sm3qCtCDsUFtmrb3srITBjaUNUL/F 481 1q8+JR+Sk7gudj9xnTT0VvINNaB71YIt83wPBagHu4VJpYQbtDH+MbUBu6OgOtO1 482 f1w53rzY2OncJxV8p7pd9mJGLoE6LC2jQY7oRw7Vq0xcJdME1BCmrIrEY3a/vaF8 483 pjYuTQKBgQCIOH23Xita8KmhH0NdlWxZfcQt1j3AnOcKe6UyN4BsF8hqS7eTA52s 484 WjG5X2IBl7gs1eMM1qkqR8npS9nwfO/pBmZPwjiZoilypXxWj+c+P3vwre2yija4 485 bXgFVj4KFBwhr1+8KcobxC0SAPEouMvSkxzjjw+gnebozUtPlud9jA== 486 -----END RSA PRIVATE KEY----- 487 ` 488 const CLIENT_CERT_SIGNED_BY_SERVER = `ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgbMDNUn4M2TtzrSH7MOT2QsvLzZWjehJ5TYrBOp9p+lwAAAADAQABAAABAQCyu57E7zIWRyEWuaiOiikOSZKFjbwLkpE9fboFfLLsNUJj4zw+5bZUJtzWK8roPjgL8s1oPncro5wuTtI2Nu4fkpeFK0Hb33o6Eyksuj4Om4+6Uemn1QEcb0bZqK8Zyg9Dg9deP7LeE0v78b5/jZafFgwxv+/sMhM0PRD34NCDYcYmkkHlvQtQWFAdbPXCgghObedZyYdoqZVuhTsiPMWtQS/cc9M4tv6mPOuQlhZt3R/Oh/kwUyu45oGRb5bhO4JicozFS3oeClpU+UMbgslkzApJqxZBWN7+PDFSZhKk2GslyeyP4sH3E30Z00yVi/lQYgmQsB+Hg6ClemNQMNu/AAAAAAAAAAAAAAACAAAABHVzZXIAAAAIAAAABHVzZXIAAAAAWzBjXAAAAAB/POfPAAAAAAAAAAAAAAAAAAABFwAAAAdzc2gtcnNhAAAAAwEAAQAAAQEA8CkDr7uxCFt6lQUVwS8NyPO+fQNxORoGnMnN/XhVJZvpqyKRUji9R0d8D66bYxUUsabXjP2y4HTVzbZtnvXFZZshk0cOtJjjekpYJaLK2esPR/iXwvSltNkrDQDPN/RmgEEMIevW8AgrPsqrnybFHxTpd7rEUHXBOe4nMNRIg3XHykB6jZk8q5bBPUe3I/f0DK5TJEBpTc6dO3P/j93u55VUqr39/SPRHnld2mCw+c8v6UOhsssO/DIZFPScD3DYqsk2N+/nz9zXfcOTdWGhawgxuIo1DTokrNQbG3pDrLqcWgqj13vqJFCmRA0O2CQIwJePd6+Np/XO3Uh/KL6FlQAAAQ8AAAAHc3NoLXJzYQAAAQC6sKEQHyl954BQn2BXuTgOB3NkENBxN7SD8ZaS8PNkDESytLjSIqrzoE6m7xuzprA+G23XRrCY/um3UvM7+7+zbwig2NIBbGbp3QFliQHegQKW6hTZP09jAQZk5jRrrEr/QT/s+gtHPmjxJK7XOQYxhInDKj+aJg62ExcwpQlP/0ATKNOIkdzTzzq916p0UOnnVaaPMKibh5Lv69GafIhKJRZSuuLN9fvs1G1RuUbxn/BNSeoRCr54L++Ztg09fJxunoyELs8mwgzCgB3pdZoUR2Z6ak05W4mvH3lkSz2BKUrlwxI6mterxhJy1GuN1K/zBG0gEMl2UTLajGK3qKM8 itbitloaner@MacBook-Pro-4.fios-router.home` 489 const CLIENT_PEM = `-----BEGIN RSA PRIVATE KEY----- 490 MIIEpAIBAAKCAQEAsruexO8yFkchFrmojoopDkmShY28C5KRPX26BXyy7DVCY+M8 491 PuW2VCbc1ivK6D44C/LNaD53K6OcLk7SNjbuH5KXhStB2996OhMpLLo+DpuPulHp 492 p9UBHG9G2aivGcoPQ4PXXj+y3hNL+/G+f42WnxYMMb/v7DITND0Q9+DQg2HGJpJB 493 5b0LUFhQHWz1woIITm3nWcmHaKmVboU7IjzFrUEv3HPTOLb+pjzrkJYWbd0fzof5 494 MFMruOaBkW+W4TuCYnKMxUt6HgpaVPlDG4LJZMwKSasWQVje/jwxUmYSpNhrJcns 495 j+LB9xN9GdNMlYv5UGIJkLAfh4OgpXpjUDDbvwIDAQABAoIBAEu2ctFVyk/pnbi0 496 uRR4rl+hBvKQUeJNGj2ELvL4Ggs5nIAX2IOEZ7JKLC6FqpSrFq7pEd5g57aSvixX 497 s3DH4CN7w7fj1ShBCNPlHgIWewdRGpeA74vrDWdwNAEsFdDE6aZeCTOhpDGy1vNJ 498 OrtpzS5i9pN0jTvvEneEjtWSZIHiiVlN+0hsFaiwZ6KXON+sDccZPmnP6Fzwj5Rc 499 WS0dKSwnxnx0otWgwWFs8nr306nSeMsNmQkHsS9lz4DEVpp9owdzrX1JmbQvNYAV 500 ohmB3ET4JYFgerqPXJfed9poueGuWCP6MYhsjNeHN35QhofxdO5/0i3JlZfqwZei 501 tNq/0oECgYEA6SqjRqDiIp3ajwyB7Wf0cIQG/P6JZDyN1jl//htgniliIH5UP1Tm 502 uAMG5MincV6X9lOyXyh6Yofu5+NR0yt9SqbDZVJ3ZCxKTun7pxJvQFd7wl5bMkiJ 503 qVfS08k6gQHHDoO+eel+DtpIfWc+e3tvX0aihSU0GZEMqDXYkkphLGECgYEAxDxb 504 +JwJ3N5UEjjkuvFBpuJnmjIaN9HvQkTv3inlx1gLE4iWBZXXsu4aWF8MCUeAAZyP 505 42hQDSkCYX/A22tYCEn/jfrU6A+6rkWBTjdUlYLvlSkhosSnO+117WEItb5cUE95 506 hF4UY7LNs1AsDkV4WE87f/EjpxSwUAjB2Lfd/B8CgYAJ/JiHsuZcozQ0Qk3iVDyF 507 ATKnbWOHFozgqw/PW27U92LLj32eRM2o/gAylmGNmoaZt1YBe2NaiwXxiqv7hnZU 508 VzYxRcn1UWxRWvY7Xq/DKrwTRCVVzwOObEOMbKcD1YaoGX50DEso6bKHJH/pnAzW 509 INlfKIvFuI+5OK0w/tyQoQKBgQCf/jpaOxaLfrV62eobRQJrByLDBGB97GsvU7di 510 IjTWz8DQH0d5rE7d8uWF8ZCFrEcAiV6DYZQK9smbJqbd/uoacAKtBro5rkFdPwwK 511 8m/DKqsdqRhkdgOHh7bjYH7Sdy8ax4Fi27WyB6FQtmgFBrz0+zyetsODwQlzZ4Bs 512 qpSRrwKBgQC0vWHrY5aGIdF+b8EpP0/SSLLALpMySHyWhDyxYcPqdhszYbjDcavv 513 xrrLXNUD2duBHKPVYE+7uVoDkpZXLUQ4x8argo/IwQM6Kh2ma1y83TYMT6XhL1+B 514 5UPcl6RXZBCkiU7nFIG6/0XKFqVWc3fU8e09X+iJwXIJ5Jatywtg+g== 515 -----END RSA PRIVATE KEY----- 516 ` 517 518 func TestCertificateBasedAuth(t *testing.T) { 519 signer, err := ssh.ParsePrivateKey([]byte(SERVER_PEM)) 520 if err != nil { 521 t.Fatalf("unable to parse private key: %v", err) 522 } 523 address := newMockLineServer(t, signer, CLIENT_CERT_SIGNED_BY_SERVER) 524 host, p, _ := net.SplitHostPort(address) 525 port, _ := strconv.Atoi(p) 526 527 connInfo := &connectionInfo{ 528 User: "user", 529 Host: host, 530 PrivateKey: CLIENT_PEM, 531 Certificate: CLIENT_CERT_SIGNED_BY_SERVER, 532 Port: uint16(port), 533 Timeout: "30s", 534 } 535 536 cfg, err := prepareSSHConfig(connInfo) 537 if err != nil { 538 t.Fatal(err) 539 } 540 541 c := &Communicator{ 542 connInfo: connInfo, 543 config: cfg, 544 } 545 546 var cmd remote.Cmd 547 stdout := new(bytes.Buffer) 548 cmd.Command = "echo foo" 549 cmd.Stdout = stdout 550 551 if err := c.Start(&cmd); err != nil { 552 t.Fatal(err) 553 } 554 if err := c.Disconnect(); err != nil { 555 t.Fatal(err) 556 } 557 } 558 559 func TestAccUploadFile(t *testing.T) { 560 // use the local ssh server and scp binary to check uploads 561 if ok := os.Getenv("SSH_UPLOAD_TEST"); ok == "" { 562 t.Log("Skipping Upload Acceptance without SSH_UPLOAD_TEST set") 563 t.Skip() 564 } 565 566 v := cty.ObjectVal(map[string]cty.Value{ 567 "type": cty.StringVal("ssh"), 568 "user": cty.StringVal(os.Getenv("USER")), 569 "host": cty.StringVal("127.0.0.1"), 570 "port": cty.StringVal("22"), 571 "timeout": cty.StringVal("30s"), 572 }) 573 574 c, err := New(v) 575 if err != nil { 576 t.Fatalf("error creating communicator: %s", err) 577 } 578 579 tmpDir, err := ioutil.TempDir("", "communicator") 580 if err != nil { 581 t.Fatal(err) 582 } 583 defer os.RemoveAll(tmpDir) 584 585 content := []byte("this is the file content") 586 source := bytes.NewReader(content) 587 tmpFile := filepath.Join(tmpDir, "tempFile.out") 588 err = c.Upload(tmpFile, source) 589 if err != nil { 590 t.Fatalf("error uploading file: %s", err) 591 } 592 593 data, err := ioutil.ReadFile(tmpFile) 594 if err != nil { 595 t.Fatal(err) 596 } 597 598 if !bytes.Equal(data, content) { 599 t.Fatalf("bad: %s", data) 600 } 601 } 602 603 func TestAccHugeUploadFile(t *testing.T) { 604 // use the local ssh server and scp binary to check uploads 605 if ok := os.Getenv("SSH_UPLOAD_TEST"); ok == "" { 606 t.Log("Skipping Upload Acceptance without SSH_UPLOAD_TEST set") 607 t.Skip() 608 } 609 610 v := cty.ObjectVal(map[string]cty.Value{ 611 "type": cty.StringVal("ssh"), 612 "host": cty.StringVal("127.0.0.1"), 613 "user": cty.StringVal(os.Getenv("USER")), 614 "port": cty.StringVal("22"), 615 "timeout": cty.StringVal("30s"), 616 }) 617 618 c, err := New(v) 619 if err != nil { 620 t.Fatalf("error creating communicator: %s", err) 621 } 622 623 // copy 4GB of data, random to prevent compression. 624 size := int64(1 << 32) 625 source := io.LimitReader(rand.New(rand.NewSource(0)), size) 626 627 dest, err := ioutil.TempFile("", "communicator") 628 if err != nil { 629 t.Fatal(err) 630 } 631 destName := dest.Name() 632 dest.Close() 633 defer os.Remove(destName) 634 635 t.Log("Uploading to", destName) 636 637 // bypass the Upload method so we can directly supply the file size 638 // preventing the extra copy of the huge file. 639 targetDir := filepath.Dir(destName) 640 targetFile := filepath.Base(destName) 641 642 scpFunc := func(w io.Writer, stdoutR *bufio.Reader) error { 643 return scpUploadFile(targetFile, source, w, stdoutR, size) 644 } 645 646 cmd, err := quoteShell([]string{"scp", "-vt", targetDir}, c.connInfo.TargetPlatform) 647 if err != nil { 648 t.Fatal(err) 649 } 650 err = c.scpSession(cmd, scpFunc) 651 if err != nil { 652 t.Fatal(err) 653 } 654 655 // check the final file size 656 fs, err := os.Stat(destName) 657 if err != nil { 658 t.Fatal(err) 659 } 660 661 if fs.Size() != size { 662 t.Fatalf("expected file size of %d, got %d", size, fs.Size()) 663 } 664 } 665 666 func TestScriptPath(t *testing.T) { 667 cases := []struct { 668 Input string 669 Pattern string 670 }{ 671 { 672 "/tmp/script.sh", 673 `^/tmp/script\.sh$`, 674 }, 675 { 676 "/tmp/script_%RAND%.sh", 677 `^/tmp/script_(\d+)\.sh$`, 678 }, 679 } 680 681 for _, tc := range cases { 682 v := cty.ObjectVal(map[string]cty.Value{ 683 "type": cty.StringVal("ssh"), 684 "host": cty.StringVal("127.0.0.1"), 685 "script_path": cty.StringVal(tc.Input), 686 }) 687 688 comm, err := New(v) 689 if err != nil { 690 t.Fatalf("err: %s", err) 691 } 692 output := comm.ScriptPath() 693 694 match, err := regexp.Match(tc.Pattern, []byte(output)) 695 if err != nil { 696 t.Fatalf("bad: %s\n\nerr: %s", tc.Input, err) 697 } 698 if !match { 699 t.Fatalf("bad: %s\n\n%s", tc.Input, output) 700 } 701 } 702 } 703 704 func TestScriptPath_randSeed(t *testing.T) { 705 // Pre GH-4186 fix, this value was the deterministic start the pseudorandom 706 // chain of unseeded math/rand values for Int31(). 707 staticSeedPath := "/tmp/terraform_1298498081.sh" 708 c, err := New(cty.ObjectVal(map[string]cty.Value{ 709 "type": cty.StringVal("ssh"), 710 "host": cty.StringVal("127.0.0.1"), 711 })) 712 if err != nil { 713 t.Fatalf("err: %s", err) 714 } 715 path := c.ScriptPath() 716 if path == staticSeedPath { 717 t.Fatalf("rand not seeded! got: %s", path) 718 } 719 } 720 721 var testClientPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDE6A1c4n+OtEPEFlNKTZf2i03L3NylSYmvmJ8OLmzLuPZmJBJt4G3VZ/60s1aKzwLKrTq20S+ONG4zvnK5zIPoauoNNdUJKbg944hB4OE+HDbrBhk7SH+YWCsCILBoSXwAVdUEic6FWf/SeqBSmTBySHvpuNOw16J+SK6Ardx8k64F2tRkZuC6AmOZijgKa/sQKjWAIVPk34ECM6OLfPc3kKUEfkdpYLvuMfuRMfSTlxn5lFC0b0SovK9aWfNMBH9iXLQkieQ5rXoyzUC7mwgnASgl8cqw1UrToiUuhvneduXBhbQfmC/Upv+tL6dSSk+0DlgVKEHuJmc8s8+/qpdL` 722 723 func acceptUserPass(goodUser, goodPass string) func(ssh.ConnMetadata, []byte) (*ssh.Permissions, error) { 724 return func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { 725 if c.User() == goodUser && string(pass) == goodPass { 726 return nil, nil 727 } 728 return nil, fmt.Errorf("password rejected for %q", c.User()) 729 } 730 } 731 732 func acceptPublicKey(keystr string) func(ssh.ConnMetadata, ssh.PublicKey) (*ssh.Permissions, error) { 733 return func(_ ssh.ConnMetadata, inkey ssh.PublicKey) (*ssh.Permissions, error) { 734 goodkey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(keystr)) 735 if err != nil { 736 return nil, fmt.Errorf("error parsing key: %v", err) 737 } 738 739 if bytes.Equal(inkey.Marshal(), goodkey.Marshal()) { 740 return nil, nil 741 } 742 743 return nil, fmt.Errorf("public key rejected") 744 } 745 }