github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/utils/ssh/ssh_gocrypto_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package ssh_test 5 6 import ( 7 "encoding/binary" 8 "errors" 9 "net" 10 "sync" 11 12 cryptossh "code.google.com/p/go.crypto/ssh" 13 gc "launchpad.net/gocheck" 14 15 jc "launchpad.net/juju-core/testing/checkers" 16 "launchpad.net/juju-core/testing/testbase" 17 "launchpad.net/juju-core/utils/ssh" 18 ) 19 20 var ( 21 testCommand = []string{"echo", "$abc"} 22 testCommandFlat = `echo "\$abc"` 23 ) 24 25 type sshServer struct { 26 cfg *cryptossh.ServerConfig 27 *cryptossh.Listener 28 } 29 30 func newServer(c *gc.C) *sshServer { 31 private, _, err := ssh.GenerateKey("test-server") 32 c.Assert(err, gc.IsNil) 33 key, err := cryptossh.ParsePrivateKey([]byte(private)) 34 c.Assert(err, gc.IsNil) 35 server := &sshServer{ 36 cfg: &cryptossh.ServerConfig{}, 37 } 38 server.cfg.AddHostKey(key) 39 server.Listener, err = cryptossh.Listen("tcp", "127.0.0.1:0", server.cfg) 40 c.Assert(err, gc.IsNil) 41 return server 42 } 43 44 func (s *sshServer) run(c *gc.C) { 45 conn, err := s.Accept() 46 c.Assert(err, gc.IsNil) 47 defer func() { 48 err = conn.Close() 49 c.Assert(err, gc.IsNil) 50 }() 51 err = conn.Handshake() 52 c.Assert(err, gc.IsNil) 53 var wg sync.WaitGroup 54 defer wg.Wait() 55 for { 56 channel, err := conn.Accept() 57 c.Assert(err, gc.IsNil) 58 c.Assert(channel.ChannelType(), gc.Equals, "session") 59 channel.Accept() 60 wg.Add(1) 61 go func() { 62 defer wg.Done() 63 defer channel.Close() 64 _, err := channel.Read(nil) 65 c.Assert(err, gc.FitsTypeOf, cryptossh.ChannelRequest{}) 66 req := err.(cryptossh.ChannelRequest) 67 c.Assert(req.Request, gc.Equals, "exec") 68 c.Assert(req.WantReply, jc.IsTrue) 69 n := binary.BigEndian.Uint32(req.Payload[:4]) 70 command := string(req.Payload[4 : n+4]) 71 c.Assert(command, gc.Equals, testCommandFlat) 72 // TODO(axw) when gosshnew is ready, send reply to client. 73 }() 74 } 75 } 76 77 type SSHGoCryptoCommandSuite struct { 78 testbase.LoggingSuite 79 client ssh.Client 80 } 81 82 var _ = gc.Suite(&SSHGoCryptoCommandSuite{}) 83 84 func (s *SSHGoCryptoCommandSuite) SetUpTest(c *gc.C) { 85 s.LoggingSuite.SetUpTest(c) 86 client, err := ssh.NewGoCryptoClient() 87 c.Assert(err, gc.IsNil) 88 s.client = client 89 } 90 91 func (s *SSHGoCryptoCommandSuite) TestNewGoCryptoClient(c *gc.C) { 92 _, err := ssh.NewGoCryptoClient() 93 c.Assert(err, gc.IsNil) 94 private, _, err := ssh.GenerateKey("test-client") 95 c.Assert(err, gc.IsNil) 96 key, err := cryptossh.ParsePrivateKey([]byte(private)) 97 c.Assert(err, gc.IsNil) 98 _, err = ssh.NewGoCryptoClient(key) 99 c.Assert(err, gc.IsNil) 100 } 101 102 func (s *SSHGoCryptoCommandSuite) TestClientNoKeys(c *gc.C) { 103 client, err := ssh.NewGoCryptoClient() 104 c.Assert(err, gc.IsNil) 105 cmd := client.Command("0.1.2.3", []string{"echo", "123"}, nil) 106 _, err = cmd.Output() 107 c.Assert(err, gc.ErrorMatches, "no private keys available") 108 defer ssh.ClearClientKeys() 109 err = ssh.LoadClientKeys(c.MkDir()) 110 c.Assert(err, gc.IsNil) 111 112 s.PatchValue(ssh.SSHDial, func(network, address string, cfg *cryptossh.ClientConfig) (*cryptossh.ClientConn, error) { 113 return nil, errors.New("ssh.Dial failed") 114 }) 115 cmd = client.Command("0.1.2.3", []string{"echo", "123"}, nil) 116 _, err = cmd.Output() 117 // error message differs based on whether using cgo or not 118 c.Assert(err, gc.ErrorMatches, "ssh.Dial failed") 119 } 120 121 func (s *SSHGoCryptoCommandSuite) TestCommand(c *gc.C) { 122 private, _, err := ssh.GenerateKey("test-server") 123 c.Assert(err, gc.IsNil) 124 key, err := cryptossh.ParsePrivateKey([]byte(private)) 125 client, err := ssh.NewGoCryptoClient(key) 126 c.Assert(err, gc.IsNil) 127 server := newServer(c) 128 var opts ssh.Options 129 opts.SetPort(server.Addr().(*net.TCPAddr).Port) 130 cmd := client.Command("127.0.0.1", testCommand, &opts) 131 checkedKey := false 132 server.cfg.PublicKeyCallback = func(conn *cryptossh.ServerConn, user, algo string, pubkey []byte) bool { 133 c.Check(pubkey, gc.DeepEquals, cryptossh.MarshalPublicKey(key.PublicKey())) 134 checkedKey = true 135 return true 136 } 137 go server.run(c) 138 out, err := cmd.Output() 139 c.Assert(err, gc.ErrorMatches, "ssh: could not execute command.*") 140 // TODO(axw) when gosshnew is ready, expect reply from server. 141 c.Assert(out, gc.IsNil) 142 c.Assert(checkedKey, jc.IsTrue) 143 } 144 145 func (s *SSHGoCryptoCommandSuite) TestCopy(c *gc.C) { 146 client, err := ssh.NewGoCryptoClient() 147 c.Assert(err, gc.IsNil) 148 err = client.Copy([]string{"0.1.2.3:b", c.MkDir()}, nil, nil) 149 c.Assert(err, gc.ErrorMatches, `scp command is not implemented \(OpenSSH scp not available in PATH\)`) 150 }