github.com/gavinw2006/hashicorp-terraform@v0.11.12-beta1/communicator/ssh/communicator_test.go (about) 1 // +build !race 2 3 package ssh 4 5 import ( 6 "bufio" 7 "bytes" 8 "encoding/base64" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "math/rand" 13 "net" 14 "os" 15 "path/filepath" 16 "regexp" 17 "strconv" 18 "strings" 19 "testing" 20 "time" 21 22 "github.com/hashicorp/terraform/communicator/remote" 23 "github.com/hashicorp/terraform/terraform" 24 "golang.org/x/crypto/ssh" 25 ) 26 27 // private key for mock server 28 const testServerPrivateKey = `-----BEGIN RSA PRIVATE KEY----- 29 MIIEpAIBAAKCAQEA19lGVsTqIT5iiNYRgnoY1CwkbETW5cq+Rzk5v/kTlf31XpSU 30 70HVWkbTERECjaYdXM2gGcbb+sxpq6GtXf1M3kVomycqhxwhPv4Cr6Xp4WT/jkFx 31 9z+FFzpeodGJWjOH6L2H5uX1Cvr9EDdQp9t9/J32/qBFntY8GwoUI/y/1MSTmMiF 32 tupdMODN064vd3gyMKTwrlQ8tZM6aYuyOPsutLlUY7M5x5FwMDYvnPDSeyT/Iw0z 33 s3B+NCyqeeMd2T7YzQFnRATj0M7rM5LoSs7DVqVriOEABssFyLj31PboaoLhOKgc 34 qoM9khkNzr7FHVvi+DhYM2jD0DwvqZLN6NmnLwIDAQABAoIBAQCGVj+kuSFOV1lT 35 +IclQYA6bM6uY5mroqcSBNegVxCNhWU03BxlW//BE9tA/+kq53vWylMeN9mpGZea 36 riEMIh25KFGWXqXlOOioH8bkMsqA8S7sBmc7jljyv+0toQ9vCCtJ+sueNPhxQQxH 37 D2YvUjfzBQ04I9+wn30BByDJ1QA/FoPsunxIOUCcRBE/7jxuLYcpR+JvEF68yYIh 38 atXRld4W4in7T65YDR8jK1Uj9XAcNeDYNpT/M6oFLx1aPIlkG86aCWRO19S1jLPT 39 b1ZAKHHxPMCVkSYW0RqvIgLXQOR62D0Zne6/2wtzJkk5UCjkSQ2z7ZzJpMkWgDgN 40 ifCULFPBAoGBAPoMZ5q1w+zB+knXUD33n1J+niN6TZHJulpf2w5zsW+m2K6Zn62M 41 MXndXlVAHtk6p02q9kxHdgov34Uo8VpuNjbS1+abGFTI8NZgFo+bsDxJdItemwC4 42 KJ7L1iz39hRN/ZylMRLz5uTYRGddCkeIHhiG2h7zohH/MaYzUacXEEy3AoGBANz8 43 e/msleB+iXC0cXKwds26N4hyMdAFE5qAqJXvV3S2W8JZnmU+sS7vPAWMYPlERPk1 44 D8Q2eXqdPIkAWBhrx4RxD7rNc5qFNcQWEhCIxC9fccluH1y5g2M+4jpMX2CT8Uv+ 45 3z+NoJ5uDTXZTnLCfoZzgZ4nCZVZ+6iU5U1+YXFJAoGBANLPpIV920n/nJmmquMj 46 orI1R/QXR9Cy56cMC65agezlGOfTYxk5Cfl5Ve+/2IJCfgzwJyjWUsFx7RviEeGw 47 64o7JoUom1HX+5xxdHPsyZ96OoTJ5RqtKKoApnhRMamau0fWydH1yeOEJd+TRHhc 48 XStGfhz8QNa1dVFvENczja1vAoGABGWhsd4VPVpHMc7lUvrf4kgKQtTC2PjA4xoc 49 QJ96hf/642sVE76jl+N6tkGMzGjnVm4P2j+bOy1VvwQavKGoXqJBRd5Apppv727g 50 /SM7hBXKFc/zH80xKBBgP/i1DR7kdjakCoeu4ngeGywvu2jTS6mQsqzkK+yWbUxJ 51 I7mYBsECgYB/KNXlTEpXtz/kwWCHFSYA8U74l7zZbVD8ul0e56JDK+lLcJ0tJffk 52 gqnBycHj6AhEycjda75cs+0zybZvN4x65KZHOGW/O/7OAWEcZP5TPb3zf9ned3Hl 53 NsZoFj52ponUM6+99A2CmezFCN16c4mbA//luWF+k3VVqR6BpkrhKw== 54 -----END RSA PRIVATE KEY-----` 55 56 // this cert was signed by the key from testCAPublicKey 57 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=` 58 59 const testCAPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCrozyZIhdEvalCn+eSzHH94cO9ykiywA13ntWI7mJcHBwYTeCYWG8E9zGXyp2iDOjCGudM0Tdt8o0OofKChk9Z/qiUN0G8y1kmaXBlBM3qA5R9NPpvMYMNkYLfX6ivtZCnqrsbzaoqN2Oc/7H2StHzJWh/XCGu9otQZA6vdv1oSmAsZOjw/xIGaGQqDUaLq21J280PP1qSbdJHf76iSHE+TWe3YpqV946JWM5tCh0DykZ10VznvxYpUjzhr07IN3tVKxOXbPnnU7lX6IaLIWgfzLqwSyheeux05c3JLF9iF4sFu8ou4hwQz1iuUTU1jxgwZP0w/bkXgFFs0949lW81` 60 61 func newMockLineServer(t *testing.T, signer ssh.Signer) string { 62 serverConfig := &ssh.ServerConfig{ 63 PasswordCallback: acceptUserPass("user", "pass"), 64 PublicKeyCallback: acceptPublicKey(testClientPublicKey), 65 } 66 67 var err error 68 if signer == nil { 69 signer, err = ssh.ParsePrivateKey([]byte(testServerPrivateKey)) 70 if err != nil { 71 t.Fatalf("unable to parse private key: %s", err) 72 } 73 } 74 serverConfig.AddHostKey(signer) 75 76 l, err := net.Listen("tcp", "127.0.0.1:0") 77 if err != nil { 78 t.Fatalf("Unable to listen for connection: %s", err) 79 } 80 81 go func() { 82 defer l.Close() 83 c, err := l.Accept() 84 if err != nil { 85 t.Errorf("Unable to accept incoming connection: %s", err) 86 } 87 defer c.Close() 88 conn, chans, _, err := ssh.NewServerConn(c, serverConfig) 89 if err != nil { 90 t.Logf("Handshaking error: %v", err) 91 } 92 t.Log("Accepted SSH connection") 93 94 for newChannel := range chans { 95 channel, requests, err := newChannel.Accept() 96 if err != nil { 97 t.Errorf("Unable to accept channel.") 98 } 99 t.Log("Accepted channel") 100 101 go func(in <-chan *ssh.Request) { 102 for req := range in { 103 if req.WantReply { 104 req.Reply(true, nil) 105 } 106 } 107 }(requests) 108 109 go func(newChannel ssh.NewChannel) { 110 conn.OpenChannel(newChannel.ChannelType(), nil) 111 }(newChannel) 112 113 defer channel.Close() 114 } 115 conn.Close() 116 }() 117 118 return l.Addr().String() 119 } 120 121 func TestNew_Invalid(t *testing.T) { 122 address := newMockLineServer(t, nil) 123 parts := strings.Split(address, ":") 124 125 r := &terraform.InstanceState{ 126 Ephemeral: terraform.EphemeralState{ 127 ConnInfo: map[string]string{ 128 "type": "ssh", 129 "user": "user", 130 "password": "i-am-invalid", 131 "host": parts[0], 132 "port": parts[1], 133 "timeout": "30s", 134 }, 135 }, 136 } 137 138 c, err := New(r) 139 if err != nil { 140 t.Fatalf("error creating communicator: %s", err) 141 } 142 143 err = c.Connect(nil) 144 if err == nil { 145 t.Fatal("should have had an error connecting") 146 } 147 } 148 149 func TestStart(t *testing.T) { 150 address := newMockLineServer(t, nil) 151 parts := strings.Split(address, ":") 152 153 r := &terraform.InstanceState{ 154 Ephemeral: terraform.EphemeralState{ 155 ConnInfo: map[string]string{ 156 "type": "ssh", 157 "user": "user", 158 "password": "pass", 159 "host": parts[0], 160 "port": parts[1], 161 "timeout": "30s", 162 }, 163 }, 164 } 165 166 c, err := New(r) 167 if err != nil { 168 t.Fatalf("error creating communicator: %s", err) 169 } 170 171 var cmd remote.Cmd 172 stdout := new(bytes.Buffer) 173 cmd.Command = "echo foo" 174 cmd.Stdout = stdout 175 176 err = c.Start(&cmd) 177 if err != nil { 178 t.Fatalf("error executing remote command: %s", err) 179 } 180 } 181 182 func TestLostConnection(t *testing.T) { 183 address := newMockLineServer(t, nil) 184 parts := strings.Split(address, ":") 185 186 r := &terraform.InstanceState{ 187 Ephemeral: terraform.EphemeralState{ 188 ConnInfo: map[string]string{ 189 "type": "ssh", 190 "user": "user", 191 "password": "pass", 192 "host": parts[0], 193 "port": parts[1], 194 "timeout": "30s", 195 }, 196 }, 197 } 198 199 c, err := New(r) 200 if err != nil { 201 t.Fatalf("error creating communicator: %s", err) 202 } 203 204 var cmd remote.Cmd 205 stdout := new(bytes.Buffer) 206 cmd.Command = "echo foo" 207 cmd.Stdout = stdout 208 209 err = c.Start(&cmd) 210 if err != nil { 211 t.Fatalf("error executing remote command: %s", err) 212 } 213 214 // The test server can't execute anything, so Wait will block, unless 215 // there's an error. Disconnect the communicator transport, to cause the 216 // command to fail. 217 go func() { 218 time.Sleep(100 * time.Millisecond) 219 c.Disconnect() 220 }() 221 222 err = cmd.Wait() 223 if err == nil { 224 t.Fatal("expected communicator error") 225 } 226 } 227 228 func TestHostKey(t *testing.T) { 229 // get the server's public key 230 signer, err := ssh.ParsePrivateKey([]byte(testServerPrivateKey)) 231 if err != nil { 232 panic("unable to parse private key: " + err.Error()) 233 } 234 pubKey := fmt.Sprintf("ssh-rsa %s", base64.StdEncoding.EncodeToString(signer.PublicKey().Marshal())) 235 236 address := newMockLineServer(t, nil) 237 host, p, _ := net.SplitHostPort(address) 238 port, _ := strconv.Atoi(p) 239 240 connInfo := &connectionInfo{ 241 User: "user", 242 Password: "pass", 243 Host: host, 244 HostKey: pubKey, 245 Port: port, 246 Timeout: "30s", 247 } 248 249 cfg, err := prepareSSHConfig(connInfo) 250 if err != nil { 251 t.Fatal(err) 252 } 253 254 c := &Communicator{ 255 connInfo: connInfo, 256 config: cfg, 257 } 258 259 var cmd remote.Cmd 260 stdout := new(bytes.Buffer) 261 cmd.Command = "echo foo" 262 cmd.Stdout = stdout 263 264 if err := c.Start(&cmd); err != nil { 265 t.Fatal(err) 266 } 267 if err := c.Disconnect(); err != nil { 268 t.Fatal(err) 269 } 270 271 // now check with the wrong HostKey 272 address = newMockLineServer(t, nil) 273 _, p, _ = net.SplitHostPort(address) 274 port, _ = strconv.Atoi(p) 275 276 connInfo.HostKey = testClientPublicKey 277 connInfo.Port = port 278 279 cfg, err = prepareSSHConfig(connInfo) 280 if err != nil { 281 t.Fatal(err) 282 } 283 284 c = &Communicator{ 285 connInfo: connInfo, 286 config: cfg, 287 } 288 289 err = c.Start(&cmd) 290 if err == nil || !strings.Contains(err.Error(), "mismatch") { 291 t.Fatalf("expected host key mismatch, got error:%v", err) 292 } 293 } 294 295 func TestHostCert(t *testing.T) { 296 pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(testServerHostCert)) 297 if err != nil { 298 t.Fatal(err) 299 } 300 301 signer, err := ssh.ParsePrivateKey([]byte(testServerPrivateKey)) 302 if err != nil { 303 t.Fatal(err) 304 } 305 306 signer, err = ssh.NewCertSigner(pk.(*ssh.Certificate), signer) 307 if err != nil { 308 t.Fatal(err) 309 } 310 311 address := newMockLineServer(t, signer) 312 host, p, _ := net.SplitHostPort(address) 313 port, _ := strconv.Atoi(p) 314 315 connInfo := &connectionInfo{ 316 User: "user", 317 Password: "pass", 318 Host: host, 319 HostKey: testCAPublicKey, 320 Port: port, 321 Timeout: "30s", 322 } 323 324 cfg, err := prepareSSHConfig(connInfo) 325 if err != nil { 326 t.Fatal(err) 327 } 328 329 c := &Communicator{ 330 connInfo: connInfo, 331 config: cfg, 332 } 333 334 var cmd remote.Cmd 335 stdout := new(bytes.Buffer) 336 cmd.Command = "echo foo" 337 cmd.Stdout = stdout 338 339 if err := c.Start(&cmd); err != nil { 340 t.Fatal(err) 341 } 342 if err := c.Disconnect(); err != nil { 343 t.Fatal(err) 344 } 345 346 // now check with the wrong HostKey 347 address = newMockLineServer(t, signer) 348 _, p, _ = net.SplitHostPort(address) 349 port, _ = strconv.Atoi(p) 350 351 connInfo.HostKey = testClientPublicKey 352 connInfo.Port = port 353 354 cfg, err = prepareSSHConfig(connInfo) 355 if err != nil { 356 t.Fatal(err) 357 } 358 359 c = &Communicator{ 360 connInfo: connInfo, 361 config: cfg, 362 } 363 364 err = c.Start(&cmd) 365 if err == nil || !strings.Contains(err.Error(), "authorities") { 366 t.Fatalf("expected host key mismatch, got error:%v", err) 367 } 368 } 369 370 func TestAccUploadFile(t *testing.T) { 371 // use the local ssh server and scp binary to check uploads 372 if ok := os.Getenv("SSH_UPLOAD_TEST"); ok == "" { 373 t.Log("Skipping Upload Acceptance without SSH_UPLOAD_TEST set") 374 t.Skip() 375 } 376 377 r := &terraform.InstanceState{ 378 Ephemeral: terraform.EphemeralState{ 379 ConnInfo: map[string]string{ 380 "type": "ssh", 381 "user": os.Getenv("USER"), 382 "host": "127.0.0.1", 383 "port": "22", 384 "timeout": "30s", 385 }, 386 }, 387 } 388 389 c, err := New(r) 390 if err != nil { 391 t.Fatalf("error creating communicator: %s", err) 392 } 393 394 tmpDir, err := ioutil.TempDir("", "communicator") 395 if err != nil { 396 t.Fatal(err) 397 } 398 defer os.RemoveAll(tmpDir) 399 400 content := []byte("this is the file content") 401 source := bytes.NewReader(content) 402 tmpFile := filepath.Join(tmpDir, "tempFile.out") 403 err = c.Upload(tmpFile, source) 404 if err != nil { 405 t.Fatalf("error uploading file: %s", err) 406 } 407 408 data, err := ioutil.ReadFile(tmpFile) 409 if err != nil { 410 t.Fatal(err) 411 } 412 413 if !bytes.Equal(data, content) { 414 t.Fatalf("bad: %s", data) 415 } 416 } 417 418 func TestAccHugeUploadFile(t *testing.T) { 419 // use the local ssh server and scp binary to check uploads 420 if ok := os.Getenv("SSH_UPLOAD_TEST"); ok == "" { 421 t.Log("Skipping Upload Acceptance without SSH_UPLOAD_TEST set") 422 t.Skip() 423 } 424 425 r := &terraform.InstanceState{ 426 Ephemeral: terraform.EphemeralState{ 427 ConnInfo: map[string]string{ 428 "type": "ssh", 429 "user": os.Getenv("USER"), 430 "host": "127.0.0.1", 431 "port": "22", 432 "timeout": "30s", 433 }, 434 }, 435 } 436 437 c, err := New(r) 438 if err != nil { 439 t.Fatalf("error creating communicator: %s", err) 440 } 441 442 // copy 4GB of data, random to prevent compression. 443 size := int64(1 << 32) 444 source := io.LimitReader(rand.New(rand.NewSource(0)), size) 445 446 dest, err := ioutil.TempFile("", "communicator") 447 if err != nil { 448 t.Fatal(err) 449 } 450 destName := dest.Name() 451 dest.Close() 452 defer os.Remove(destName) 453 454 t.Log("Uploading to", destName) 455 456 // bypass the Upload method so we can directly supply the file size 457 // preventing the extra copy of the huge file. 458 targetDir := filepath.Dir(destName) 459 targetFile := filepath.Base(destName) 460 461 scpFunc := func(w io.Writer, stdoutR *bufio.Reader) error { 462 return scpUploadFile(targetFile, source, w, stdoutR, size) 463 } 464 465 err = c.scpSession("scp -vt "+targetDir, scpFunc) 466 if err != nil { 467 t.Fatal(err) 468 } 469 470 // check the final file size 471 fs, err := os.Stat(destName) 472 if err != nil { 473 t.Fatal(err) 474 } 475 476 if fs.Size() != size { 477 t.Fatalf("expected file size of %d, got %d", size, fs.Size()) 478 } 479 } 480 481 func TestScriptPath(t *testing.T) { 482 cases := []struct { 483 Input string 484 Pattern string 485 }{ 486 { 487 "/tmp/script.sh", 488 `^/tmp/script\.sh$`, 489 }, 490 { 491 "/tmp/script_%RAND%.sh", 492 `^/tmp/script_(\d+)\.sh$`, 493 }, 494 } 495 496 for _, tc := range cases { 497 r := &terraform.InstanceState{ 498 Ephemeral: terraform.EphemeralState{ 499 ConnInfo: map[string]string{ 500 "type": "ssh", 501 "script_path": tc.Input, 502 }, 503 }, 504 } 505 comm, err := New(r) 506 if err != nil { 507 t.Fatalf("err: %s", err) 508 } 509 output := comm.ScriptPath() 510 511 match, err := regexp.Match(tc.Pattern, []byte(output)) 512 if err != nil { 513 t.Fatalf("bad: %s\n\nerr: %s", tc.Input, err) 514 } 515 if !match { 516 t.Fatalf("bad: %s\n\n%s", tc.Input, output) 517 } 518 } 519 } 520 521 func TestScriptPath_randSeed(t *testing.T) { 522 // Pre GH-4186 fix, this value was the deterministic start the pseudorandom 523 // chain of unseeded math/rand values for Int31(). 524 staticSeedPath := "/tmp/terraform_1298498081.sh" 525 c, err := New(&terraform.InstanceState{}) 526 if err != nil { 527 t.Fatalf("err: %s", err) 528 } 529 path := c.ScriptPath() 530 if path == staticSeedPath { 531 t.Fatalf("rand not seeded! got: %s", path) 532 } 533 } 534 535 const testClientPrivateKey = `-----BEGIN RSA PRIVATE KEY----- 536 MIIEpQIBAAKCAQEAxOgNXOJ/jrRDxBZTSk2X9otNy9zcpUmJr5ifDi5sy7j2ZiQS 537 beBt1Wf+tLNWis8Cyq06ttEvjjRuM75yucyD6GrqDTXVCSm4PeOIQeDhPhw26wYZ 538 O0h/mFgrAiCwaEl8AFXVBInOhVn/0nqgUpkwckh76bjTsNeifkiugK3cfJOuBdrU 539 ZGbgugJjmYo4Cmv7ECo1gCFT5N+BAjOji3z3N5ClBH5HaWC77jH7kTH0k5cZ+ZRQ 540 tG9EqLyvWlnzTAR/Yly0JInkOa16Ms1Au5sIJwEoJfHKsNVK06IlLob53nblwYW0 541 H5gv1Kb/rS+nUkpPtA5YFShB7iZnPLPPv6qXSwIDAQABAoIBAC0UY1rMkB9/rbQK 542 2G6+bPgI1HrDydAdkeQdsOxyPH43jlG8GGwHYZ3l/S4pkLqewijcmACay6Rm5IP8 543 Kg/XfquLLqJvnKJIZuHkYaGTdn3dv8T21Hf6FRwvs0j9auW1TSpWfDpZwmpNPIBX 544 irTeVXUUmynbIrvt4km/IhRbuYrbbb964CLYD1DCl3XssXxoRNvPpc5EtOuyDorA 545 5g1hvZR1FqbOAmOuNQMYJociMuWB8mCaHb+o1Sg4A65OLXxoKs0cuwInJ/n/R4Z3 546 +GrV+x5ypBMxXgjjQtKMLEOujkvxs1cp34hkbhKMHHXxbMu5jl74YtGGsLLk90rq 547 ieZGIgECgYEA49OM9mMCrDoFUTZdJaSARA/MOXkdQgrqVTv9kUHee7oeMZZ6lS0i 548 bPU7g+Bq+UAN0qcw9x992eAElKjBA71Q5UbZYWh29BDMZd8bRJmwz4P6aSMoYLWI 549 Sr31caJU9LdmPFatarNeehjSJtlTuoZD9+NElnnUwNaTeOOo5UdhTQsCgYEA3UGm 550 QWoDUttFwK9oL2KL8M54Bx6EzNhnyk03WrqBbR7PJcPKnsF0R/0soQ+y0FW0r8RJ 551 TqG6ze5fUJII72B4GlMTQdP+BIvaKQttwWQTNIjbbv4NksF445gdVOO1xi9SvQ7k 552 uvMVxOb+1jL3HAFa3furWu2tJRDs6dhuaILLxsECgYEAhnhlKUBDYZhVbxvhWsh/ 553 lKymY/3ikQqUSX7BKa1xPiIalDY3YDllql4MpMgfG8L85asdMZ96ztB0o7H/Ss/B 554 IbLxt5bLLz+DBVXsaE82lyVU9h10RbCgI01/w3SHJHHjfBXFAcehKfvgfmGkE+IP 555 2A5ie1aphrCgFqh5FetNuQUCgYEAibL42I804FUtFR1VduAa/dRRqQSaW6528dWa 556 lLGsKRBalUNEEAeP6dmr89UEUVp1qEo94V0QGGe5FDi+rNPaC3AWdQqNdaDgNlkx 557 hoFU3oYqIuqj4ejc5rBd2N4a2+vJz3W8bokozDGC+iYf2mMRfUPKwj1XW9Er0OFs 558 3UhBsEECgYEAto/iJB7ZlCM7EyV9JW0tsEt83rbKMQ/Ex0ShbBIejej0Xx7bwx60 559 tVgay+bzJnNkXu6J4XVI98A/WsdI2kW4hL0STYdHV5HVA1l87V4ZbvTF2Bx8a8RJ 560 OF3UjpMTWKqOprw9nAu5VuwNRVzORF8ER8rgGeaR2/gsSvIYFy9VXq8= 561 -----END RSA PRIVATE KEY-----` 562 563 var testClientPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDE6A1c4n+OtEPEFlNKTZf2i03L3NylSYmvmJ8OLmzLuPZmJBJt4G3VZ/60s1aKzwLKrTq20S+ONG4zvnK5zIPoauoNNdUJKbg944hB4OE+HDbrBhk7SH+YWCsCILBoSXwAVdUEic6FWf/SeqBSmTBySHvpuNOw16J+SK6Ardx8k64F2tRkZuC6AmOZijgKa/sQKjWAIVPk34ECM6OLfPc3kKUEfkdpYLvuMfuRMfSTlxn5lFC0b0SovK9aWfNMBH9iXLQkieQ5rXoyzUC7mwgnASgl8cqw1UrToiUuhvneduXBhbQfmC/Upv+tL6dSSk+0DlgVKEHuJmc8s8+/qpdL` 564 565 func acceptUserPass(goodUser, goodPass string) func(ssh.ConnMetadata, []byte) (*ssh.Permissions, error) { 566 return func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { 567 if c.User() == goodUser && string(pass) == goodPass { 568 return nil, nil 569 } 570 return nil, fmt.Errorf("password rejected for %q", c.User()) 571 } 572 } 573 574 func acceptPublicKey(keystr string) func(ssh.ConnMetadata, ssh.PublicKey) (*ssh.Permissions, error) { 575 goodkey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(keystr)) 576 if err != nil { 577 panic(fmt.Errorf("error parsing key: %s", err)) 578 } 579 return func(_ ssh.ConnMetadata, inkey ssh.PublicKey) (*ssh.Permissions, error) { 580 if bytes.Equal(inkey.Marshal(), goodkey.Marshal()) { 581 return nil, nil 582 } 583 584 return nil, fmt.Errorf("public key rejected") 585 } 586 }