github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/communicator/ssh/communicator_test.go (about)

     1  // +build !race
     2  
     3  package ssh
     4  
     5  import (
     6  	"bytes"
     7  	"fmt"
     8  	"net"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/hashicorp/packer/packer"
    13  	"golang.org/x/crypto/ssh"
    14  )
    15  
    16  // private key for mock server
    17  const testServerPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
    18  MIIEpAIBAAKCAQEA19lGVsTqIT5iiNYRgnoY1CwkbETW5cq+Rzk5v/kTlf31XpSU
    19  70HVWkbTERECjaYdXM2gGcbb+sxpq6GtXf1M3kVomycqhxwhPv4Cr6Xp4WT/jkFx
    20  9z+FFzpeodGJWjOH6L2H5uX1Cvr9EDdQp9t9/J32/qBFntY8GwoUI/y/1MSTmMiF
    21  tupdMODN064vd3gyMKTwrlQ8tZM6aYuyOPsutLlUY7M5x5FwMDYvnPDSeyT/Iw0z
    22  s3B+NCyqeeMd2T7YzQFnRATj0M7rM5LoSs7DVqVriOEABssFyLj31PboaoLhOKgc
    23  qoM9khkNzr7FHVvi+DhYM2jD0DwvqZLN6NmnLwIDAQABAoIBAQCGVj+kuSFOV1lT
    24  +IclQYA6bM6uY5mroqcSBNegVxCNhWU03BxlW//BE9tA/+kq53vWylMeN9mpGZea
    25  riEMIh25KFGWXqXlOOioH8bkMsqA8S7sBmc7jljyv+0toQ9vCCtJ+sueNPhxQQxH
    26  D2YvUjfzBQ04I9+wn30BByDJ1QA/FoPsunxIOUCcRBE/7jxuLYcpR+JvEF68yYIh
    27  atXRld4W4in7T65YDR8jK1Uj9XAcNeDYNpT/M6oFLx1aPIlkG86aCWRO19S1jLPT
    28  b1ZAKHHxPMCVkSYW0RqvIgLXQOR62D0Zne6/2wtzJkk5UCjkSQ2z7ZzJpMkWgDgN
    29  ifCULFPBAoGBAPoMZ5q1w+zB+knXUD33n1J+niN6TZHJulpf2w5zsW+m2K6Zn62M
    30  MXndXlVAHtk6p02q9kxHdgov34Uo8VpuNjbS1+abGFTI8NZgFo+bsDxJdItemwC4
    31  KJ7L1iz39hRN/ZylMRLz5uTYRGddCkeIHhiG2h7zohH/MaYzUacXEEy3AoGBANz8
    32  e/msleB+iXC0cXKwds26N4hyMdAFE5qAqJXvV3S2W8JZnmU+sS7vPAWMYPlERPk1
    33  D8Q2eXqdPIkAWBhrx4RxD7rNc5qFNcQWEhCIxC9fccluH1y5g2M+4jpMX2CT8Uv+
    34  3z+NoJ5uDTXZTnLCfoZzgZ4nCZVZ+6iU5U1+YXFJAoGBANLPpIV920n/nJmmquMj
    35  orI1R/QXR9Cy56cMC65agezlGOfTYxk5Cfl5Ve+/2IJCfgzwJyjWUsFx7RviEeGw
    36  64o7JoUom1HX+5xxdHPsyZ96OoTJ5RqtKKoApnhRMamau0fWydH1yeOEJd+TRHhc
    37  XStGfhz8QNa1dVFvENczja1vAoGABGWhsd4VPVpHMc7lUvrf4kgKQtTC2PjA4xoc
    38  QJ96hf/642sVE76jl+N6tkGMzGjnVm4P2j+bOy1VvwQavKGoXqJBRd5Apppv727g
    39  /SM7hBXKFc/zH80xKBBgP/i1DR7kdjakCoeu4ngeGywvu2jTS6mQsqzkK+yWbUxJ
    40  I7mYBsECgYB/KNXlTEpXtz/kwWCHFSYA8U74l7zZbVD8ul0e56JDK+lLcJ0tJffk
    41  gqnBycHj6AhEycjda75cs+0zybZvN4x65KZHOGW/O/7OAWEcZP5TPb3zf9ned3Hl
    42  NsZoFj52ponUM6+99A2CmezFCN16c4mbA//luWF+k3VVqR6BpkrhKw==
    43  -----END RSA PRIVATE KEY-----`
    44  
    45  var serverConfig = &ssh.ServerConfig{
    46  	PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
    47  		if c.User() == "user" && string(pass) == "pass" {
    48  			return nil, nil
    49  		}
    50  		return nil, fmt.Errorf("password rejected for %q", c.User())
    51  	},
    52  }
    53  
    54  func init() {
    55  	// Parse and set the private key of the server, required to accept connections
    56  	signer, err := ssh.ParsePrivateKey([]byte(testServerPrivateKey))
    57  	if err != nil {
    58  		panic("unable to parse private key: " + err.Error())
    59  	}
    60  	serverConfig.AddHostKey(signer)
    61  }
    62  
    63  func newMockLineServer(t *testing.T) string {
    64  	l, err := net.Listen("tcp", "127.0.0.1:0")
    65  	if err != nil {
    66  		t.Fatalf("Unable to listen for connection: %s", err)
    67  	}
    68  
    69  	go func() {
    70  		defer l.Close()
    71  		c, err := l.Accept()
    72  		if err != nil {
    73  			t.Errorf("Unable to accept incoming connection: %s", err)
    74  		}
    75  		defer c.Close()
    76  		conn, chans, _, err := ssh.NewServerConn(c, serverConfig)
    77  		if err != nil {
    78  			t.Logf("Handshaking error: %v", err)
    79  		}
    80  		t.Log("Accepted SSH connection")
    81  		for newChannel := range chans {
    82  			channel, _, err := newChannel.Accept()
    83  			if err != nil {
    84  				t.Errorf("Unable to accept channel.")
    85  			}
    86  			t.Log("Accepted channel")
    87  
    88  			go func(channelType string) {
    89  				defer channel.Close()
    90  				conn.OpenChannel(channelType, nil)
    91  			}(newChannel.ChannelType())
    92  		}
    93  		conn.Close()
    94  	}()
    95  
    96  	return l.Addr().String()
    97  }
    98  
    99  func newMockBrokenServer(t *testing.T) string {
   100  	l, err := net.Listen("tcp", "127.0.0.1:0")
   101  	if err != nil {
   102  		t.Fatalf("Unable tp listen for connection: %s", err)
   103  	}
   104  
   105  	go func() {
   106  		defer l.Close()
   107  		c, err := l.Accept()
   108  		if err != nil {
   109  			t.Errorf("Unable to accept incoming connection: %s", err)
   110  		}
   111  		defer c.Close()
   112  		// This should block for a period of time longer than our timeout in
   113  		// the test case. That way we invoke a failure scenario.
   114  		t.Log("Block on handshaking for SSH connection")
   115  		time.Sleep(5 * time.Second)
   116  	}()
   117  
   118  	return l.Addr().String()
   119  }
   120  
   121  func TestCommIsCommunicator(t *testing.T) {
   122  	var raw interface{}
   123  	raw = &comm{}
   124  	if _, ok := raw.(packer.Communicator); !ok {
   125  		t.Fatalf("comm must be a communicator")
   126  	}
   127  }
   128  
   129  func TestNew_Invalid(t *testing.T) {
   130  	clientConfig := &ssh.ClientConfig{
   131  		User: "user",
   132  		Auth: []ssh.AuthMethod{
   133  			ssh.Password("i-am-invalid"),
   134  		},
   135  		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
   136  	}
   137  
   138  	address := newMockLineServer(t)
   139  	conn := func() (net.Conn, error) {
   140  		conn, err := net.Dial("tcp", address)
   141  		if err != nil {
   142  			t.Errorf("Unable to accept incoming connection: %v", err)
   143  		}
   144  		return conn, err
   145  	}
   146  
   147  	config := &Config{
   148  		Connection: conn,
   149  		SSHConfig:  clientConfig,
   150  	}
   151  
   152  	_, err := New(address, config)
   153  	if err == nil {
   154  		t.Fatal("should have had an error connecting")
   155  	}
   156  }
   157  
   158  func TestStart(t *testing.T) {
   159  	clientConfig := &ssh.ClientConfig{
   160  		User: "user",
   161  		Auth: []ssh.AuthMethod{
   162  			ssh.Password("pass"),
   163  		},
   164  		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
   165  	}
   166  
   167  	address := newMockLineServer(t)
   168  	conn := func() (net.Conn, error) {
   169  		conn, err := net.Dial("tcp", address)
   170  		if err != nil {
   171  			t.Fatalf("unable to dial to remote side: %s", err)
   172  		}
   173  		return conn, err
   174  	}
   175  
   176  	config := &Config{
   177  		Connection: conn,
   178  		SSHConfig:  clientConfig,
   179  	}
   180  
   181  	client, err := New(address, config)
   182  	if err != nil {
   183  		t.Fatalf("error connecting to SSH: %s", err)
   184  	}
   185  
   186  	cmd := &packer.RemoteCmd{
   187  		Command: "echo foo",
   188  		Stdout:  new(bytes.Buffer),
   189  	}
   190  
   191  	client.Start(cmd)
   192  }
   193  
   194  func TestHandshakeTimeout(t *testing.T) {
   195  	clientConfig := &ssh.ClientConfig{
   196  		User: "user",
   197  		Auth: []ssh.AuthMethod{
   198  			ssh.Password("pass"),
   199  		},
   200  		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
   201  	}
   202  
   203  	address := newMockBrokenServer(t)
   204  	conn := func() (net.Conn, error) {
   205  		conn, err := net.Dial("tcp", address)
   206  		if err != nil {
   207  			t.Fatalf("unable to dial to remote side: %s", err)
   208  		}
   209  		return conn, err
   210  	}
   211  
   212  	config := &Config{
   213  		Connection:       conn,
   214  		SSHConfig:        clientConfig,
   215  		HandshakeTimeout: 50 * time.Millisecond,
   216  	}
   217  
   218  	_, err := New(address, config)
   219  	if err != ErrHandshakeTimeout {
   220  		// Note: there's another error that can come back from this call:
   221  		//   ssh: handshake failed: EOF
   222  		// This should appear in cases where the handshake fails because of
   223  		// malformed (or no) data sent back by the server, but should not happen
   224  		// in a timeout scenario.
   225  		t.Fatalf("Expected handshake timeout, got: %s", err)
   226  	}
   227  }