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  }