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