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