github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/communicator/ssh/communicator_test.go (about)

     1  //go:build !race
     2  // +build !race
     3  
     4  package ssh
     5  
     6  import (
     7  	"bufio"
     8  	"bytes"
     9  	"encoding/base64"
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"math/rand"
    14  	"net"
    15  	"os"
    16  	"path/filepath"
    17  	"regexp"
    18  	"strconv"
    19  	"strings"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/muratcelep/terraform/not-internal/communicator/remote"
    24  	"github.com/zclconf/go-cty/cty"
    25  	"golang.org/x/crypto/ssh"
    26  )
    27  
    28  // private key for mock server
    29  const testServerPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
    30  MIIEpAIBAAKCAQEA19lGVsTqIT5iiNYRgnoY1CwkbETW5cq+Rzk5v/kTlf31XpSU
    31  70HVWkbTERECjaYdXM2gGcbb+sxpq6GtXf1M3kVomycqhxwhPv4Cr6Xp4WT/jkFx
    32  9z+FFzpeodGJWjOH6L2H5uX1Cvr9EDdQp9t9/J32/qBFntY8GwoUI/y/1MSTmMiF
    33  tupdMODN064vd3gyMKTwrlQ8tZM6aYuyOPsutLlUY7M5x5FwMDYvnPDSeyT/Iw0z
    34  s3B+NCyqeeMd2T7YzQFnRATj0M7rM5LoSs7DVqVriOEABssFyLj31PboaoLhOKgc
    35  qoM9khkNzr7FHVvi+DhYM2jD0DwvqZLN6NmnLwIDAQABAoIBAQCGVj+kuSFOV1lT
    36  +IclQYA6bM6uY5mroqcSBNegVxCNhWU03BxlW//BE9tA/+kq53vWylMeN9mpGZea
    37  riEMIh25KFGWXqXlOOioH8bkMsqA8S7sBmc7jljyv+0toQ9vCCtJ+sueNPhxQQxH
    38  D2YvUjfzBQ04I9+wn30BByDJ1QA/FoPsunxIOUCcRBE/7jxuLYcpR+JvEF68yYIh
    39  atXRld4W4in7T65YDR8jK1Uj9XAcNeDYNpT/M6oFLx1aPIlkG86aCWRO19S1jLPT
    40  b1ZAKHHxPMCVkSYW0RqvIgLXQOR62D0Zne6/2wtzJkk5UCjkSQ2z7ZzJpMkWgDgN
    41  ifCULFPBAoGBAPoMZ5q1w+zB+knXUD33n1J+niN6TZHJulpf2w5zsW+m2K6Zn62M
    42  MXndXlVAHtk6p02q9kxHdgov34Uo8VpuNjbS1+abGFTI8NZgFo+bsDxJdItemwC4
    43  KJ7L1iz39hRN/ZylMRLz5uTYRGddCkeIHhiG2h7zohH/MaYzUacXEEy3AoGBANz8
    44  e/msleB+iXC0cXKwds26N4hyMdAFE5qAqJXvV3S2W8JZnmU+sS7vPAWMYPlERPk1
    45  D8Q2eXqdPIkAWBhrx4RxD7rNc5qFNcQWEhCIxC9fccluH1y5g2M+4jpMX2CT8Uv+
    46  3z+NoJ5uDTXZTnLCfoZzgZ4nCZVZ+6iU5U1+YXFJAoGBANLPpIV920n/nJmmquMj
    47  orI1R/QXR9Cy56cMC65agezlGOfTYxk5Cfl5Ve+/2IJCfgzwJyjWUsFx7RviEeGw
    48  64o7JoUom1HX+5xxdHPsyZ96OoTJ5RqtKKoApnhRMamau0fWydH1yeOEJd+TRHhc
    49  XStGfhz8QNa1dVFvENczja1vAoGABGWhsd4VPVpHMc7lUvrf4kgKQtTC2PjA4xoc
    50  QJ96hf/642sVE76jl+N6tkGMzGjnVm4P2j+bOy1VvwQavKGoXqJBRd5Apppv727g
    51  /SM7hBXKFc/zH80xKBBgP/i1DR7kdjakCoeu4ngeGywvu2jTS6mQsqzkK+yWbUxJ
    52  I7mYBsECgYB/KNXlTEpXtz/kwWCHFSYA8U74l7zZbVD8ul0e56JDK+lLcJ0tJffk
    53  gqnBycHj6AhEycjda75cs+0zybZvN4x65KZHOGW/O/7OAWEcZP5TPb3zf9ned3Hl
    54  NsZoFj52ponUM6+99A2CmezFCN16c4mbA//luWF+k3VVqR6BpkrhKw==
    55  -----END RSA PRIVATE KEY-----`
    56  
    57  // this cert was signed by the key from testCAPublicKey
    58  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=`
    59  
    60  const testCAPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCrozyZIhdEvalCn+eSzHH94cO9ykiywA13ntWI7mJcHBwYTeCYWG8E9zGXyp2iDOjCGudM0Tdt8o0OofKChk9Z/qiUN0G8y1kmaXBlBM3qA5R9NPpvMYMNkYLfX6ivtZCnqrsbzaoqN2Oc/7H2StHzJWh/XCGu9otQZA6vdv1oSmAsZOjw/xIGaGQqDUaLq21J280PP1qSbdJHf76iSHE+TWe3YpqV946JWM5tCh0DykZ10VznvxYpUjzhr07IN3tVKxOXbPnnU7lX6IaLIWgfzLqwSyheeux05c3JLF9iF4sFu8ou4hwQz1iuUTU1jxgwZP0w/bkXgFFs0949lW81`
    61  
    62  func newMockLineServer(t *testing.T, signer ssh.Signer, pubKey string) string {
    63  	serverConfig := &ssh.ServerConfig{
    64  		PasswordCallback:  acceptUserPass("user", "pass"),
    65  		PublicKeyCallback: acceptPublicKey(pubKey),
    66  	}
    67  
    68  	var err error
    69  	if signer == nil {
    70  		signer, err = ssh.ParsePrivateKey([]byte(testServerPrivateKey))
    71  		if err != nil {
    72  			t.Fatalf("unable to parse private key: %s", err)
    73  		}
    74  	}
    75  	serverConfig.AddHostKey(signer)
    76  
    77  	l, err := net.Listen("tcp", "127.0.0.1:0")
    78  	if err != nil {
    79  		t.Fatalf("Unable to listen for connection: %s", err)
    80  	}
    81  
    82  	go func() {
    83  		defer l.Close()
    84  		c, err := l.Accept()
    85  		if err != nil {
    86  			t.Errorf("Unable to accept incoming connection: %s", err)
    87  		}
    88  		defer c.Close()
    89  		conn, chans, _, err := ssh.NewServerConn(c, serverConfig)
    90  		if err != nil {
    91  			t.Logf("Handshaking error: %v", err)
    92  		}
    93  		t.Log("Accepted SSH connection")
    94  
    95  		for newChannel := range chans {
    96  			channel, requests, err := newChannel.Accept()
    97  			if err != nil {
    98  				t.Errorf("Unable to accept channel.")
    99  			}
   100  			t.Log("Accepted channel")
   101  
   102  			go func(in <-chan *ssh.Request) {
   103  				defer channel.Close()
   104  				for req := range in {
   105  					// since this channel's requests are serviced serially,
   106  					// this will block keepalive probes, and can simulate a
   107  					// hung connection.
   108  					if bytes.Contains(req.Payload, []byte("sleep")) {
   109  						time.Sleep(time.Second)
   110  					}
   111  
   112  					if req.WantReply {
   113  						req.Reply(true, nil)
   114  					}
   115  				}
   116  			}(requests)
   117  		}
   118  		conn.Close()
   119  	}()
   120  
   121  	return l.Addr().String()
   122  }
   123  
   124  func TestNew_Invalid(t *testing.T) {
   125  	address := newMockLineServer(t, nil, testClientPublicKey)
   126  	parts := strings.Split(address, ":")
   127  
   128  	v := cty.ObjectVal(map[string]cty.Value{
   129  		"type":     cty.StringVal("ssh"),
   130  		"user":     cty.StringVal("user"),
   131  		"password": cty.StringVal("i-am-invalid"),
   132  		"host":     cty.StringVal(parts[0]),
   133  		"port":     cty.StringVal(parts[1]),
   134  		"timeout":  cty.StringVal("30s"),
   135  	})
   136  
   137  	c, err := New(v)
   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 TestNew_InvalidHost(t *testing.T) {
   149  	v := cty.ObjectVal(map[string]cty.Value{
   150  		"type":     cty.StringVal("ssh"),
   151  		"user":     cty.StringVal("user"),
   152  		"password": cty.StringVal("i-am-invalid"),
   153  		"port":     cty.StringVal("22"),
   154  		"timeout":  cty.StringVal("30s"),
   155  	})
   156  
   157  	_, err := New(v)
   158  	if err == nil {
   159  		t.Fatal("should have had an error creating communicator")
   160  	}
   161  }
   162  
   163  func TestStart(t *testing.T) {
   164  	address := newMockLineServer(t, nil, testClientPublicKey)
   165  	parts := strings.Split(address, ":")
   166  
   167  	v := cty.ObjectVal(map[string]cty.Value{
   168  		"type":     cty.StringVal("ssh"),
   169  		"user":     cty.StringVal("user"),
   170  		"password": cty.StringVal("pass"),
   171  		"host":     cty.StringVal(parts[0]),
   172  		"port":     cty.StringVal(parts[1]),
   173  		"timeout":  cty.StringVal("30s"),
   174  	})
   175  
   176  	c, err := New(v)
   177  	if err != nil {
   178  		t.Fatalf("error creating communicator: %s", err)
   179  	}
   180  
   181  	var cmd remote.Cmd
   182  	stdout := new(bytes.Buffer)
   183  	cmd.Command = "echo foo"
   184  	cmd.Stdout = stdout
   185  
   186  	err = c.Start(&cmd)
   187  	if err != nil {
   188  		t.Fatalf("error executing remote command: %s", err)
   189  	}
   190  }
   191  
   192  // TestKeepAlives verifies that the keepalive messages don't interfere with
   193  // normal operation of the client.
   194  func TestKeepAlives(t *testing.T) {
   195  	ivl := keepAliveInterval
   196  	keepAliveInterval = 250 * time.Millisecond
   197  	defer func() { keepAliveInterval = ivl }()
   198  
   199  	address := newMockLineServer(t, nil, testClientPublicKey)
   200  	parts := strings.Split(address, ":")
   201  
   202  	v := cty.ObjectVal(map[string]cty.Value{
   203  		"type":     cty.StringVal("ssh"),
   204  		"user":     cty.StringVal("user"),
   205  		"password": cty.StringVal("pass"),
   206  		"host":     cty.StringVal(parts[0]),
   207  		"port":     cty.StringVal(parts[1]),
   208  	})
   209  
   210  	c, err := New(v)
   211  	if err != nil {
   212  		t.Fatalf("error creating communicator: %s", err)
   213  	}
   214  
   215  	if err := c.Connect(nil); err != nil {
   216  		t.Fatal(err)
   217  	}
   218  
   219  	var cmd remote.Cmd
   220  	stdout := new(bytes.Buffer)
   221  	cmd.Command = "sleep"
   222  	cmd.Stdout = stdout
   223  
   224  	// wait a bit before executing the command, so that at least 1 keepalive is sent
   225  	time.Sleep(500 * time.Millisecond)
   226  
   227  	err = c.Start(&cmd)
   228  	if err != nil {
   229  		t.Fatalf("error executing remote command: %s", err)
   230  	}
   231  }
   232  
   233  // TestDeadConnection verifies that failed keepalive messages will eventually
   234  // kill the connection.
   235  func TestFailedKeepAlives(t *testing.T) {
   236  	ivl := keepAliveInterval
   237  	del := maxKeepAliveDelay
   238  	maxKeepAliveDelay = 500 * time.Millisecond
   239  	keepAliveInterval = 250 * time.Millisecond
   240  	defer func() {
   241  		keepAliveInterval = ivl
   242  		maxKeepAliveDelay = del
   243  	}()
   244  
   245  	address := newMockLineServer(t, nil, testClientPublicKey)
   246  	parts := strings.Split(address, ":")
   247  
   248  	v := cty.ObjectVal(map[string]cty.Value{
   249  		"type":     cty.StringVal("ssh"),
   250  		"user":     cty.StringVal("user"),
   251  		"password": cty.StringVal("pass"),
   252  		"host":     cty.StringVal(parts[0]),
   253  		"port":     cty.StringVal(parts[1]),
   254  		"timeout":  cty.StringVal("30s"),
   255  	})
   256  
   257  	c, err := New(v)
   258  	if err != nil {
   259  		t.Fatalf("error creating communicator: %s", err)
   260  	}
   261  
   262  	if err := c.Connect(nil); err != nil {
   263  		t.Fatal(err)
   264  	}
   265  	var cmd remote.Cmd
   266  	stdout := new(bytes.Buffer)
   267  	cmd.Command = "sleep"
   268  	cmd.Stdout = stdout
   269  
   270  	err = c.Start(&cmd)
   271  	if err == nil {
   272  		t.Fatal("expected connection error")
   273  	}
   274  }
   275  
   276  func TestLostConnection(t *testing.T) {
   277  	address := newMockLineServer(t, nil, testClientPublicKey)
   278  	parts := strings.Split(address, ":")
   279  
   280  	v := cty.ObjectVal(map[string]cty.Value{
   281  		"type":     cty.StringVal("ssh"),
   282  		"user":     cty.StringVal("user"),
   283  		"password": cty.StringVal("pass"),
   284  		"host":     cty.StringVal(parts[0]),
   285  		"port":     cty.StringVal(parts[1]),
   286  		"timeout":  cty.StringVal("30s"),
   287  	})
   288  
   289  	c, err := New(v)
   290  	if err != nil {
   291  		t.Fatalf("error creating communicator: %s", err)
   292  	}
   293  
   294  	var cmd remote.Cmd
   295  	stdout := new(bytes.Buffer)
   296  	cmd.Command = "echo foo"
   297  	cmd.Stdout = stdout
   298  
   299  	err = c.Start(&cmd)
   300  	if err != nil {
   301  		t.Fatalf("error executing remote command: %s", err)
   302  	}
   303  
   304  	// The test server can't execute anything, so Wait will block, unless
   305  	// there's an error.  Disconnect the communicator transport, to cause the
   306  	// command to fail.
   307  	go func() {
   308  		time.Sleep(100 * time.Millisecond)
   309  		c.Disconnect()
   310  	}()
   311  
   312  	err = cmd.Wait()
   313  	if err == nil {
   314  		t.Fatal("expected communicator error")
   315  	}
   316  }
   317  
   318  func TestHostKey(t *testing.T) {
   319  	// get the server's public key
   320  	signer, err := ssh.ParsePrivateKey([]byte(testServerPrivateKey))
   321  	if err != nil {
   322  		t.Fatalf("unable to parse private key: %v", err)
   323  	}
   324  	pubKey := fmt.Sprintf("ssh-rsa %s", base64.StdEncoding.EncodeToString(signer.PublicKey().Marshal()))
   325  
   326  	address := newMockLineServer(t, nil, testClientPublicKey)
   327  	host, p, _ := net.SplitHostPort(address)
   328  	port, _ := strconv.Atoi(p)
   329  
   330  	connInfo := &connectionInfo{
   331  		User:     "user",
   332  		Password: "pass",
   333  		Host:     host,
   334  		HostKey:  pubKey,
   335  		Port:     uint16(port),
   336  		Timeout:  "30s",
   337  	}
   338  
   339  	cfg, err := prepareSSHConfig(connInfo)
   340  	if err != nil {
   341  		t.Fatal(err)
   342  	}
   343  
   344  	c := &Communicator{
   345  		connInfo: connInfo,
   346  		config:   cfg,
   347  	}
   348  
   349  	var cmd remote.Cmd
   350  	stdout := new(bytes.Buffer)
   351  	cmd.Command = "echo foo"
   352  	cmd.Stdout = stdout
   353  
   354  	if err := c.Start(&cmd); err != nil {
   355  		t.Fatal(err)
   356  	}
   357  	if err := c.Disconnect(); err != nil {
   358  		t.Fatal(err)
   359  	}
   360  
   361  	// now check with the wrong HostKey
   362  	address = newMockLineServer(t, nil, testClientPublicKey)
   363  	_, p, _ = net.SplitHostPort(address)
   364  	port, _ = strconv.Atoi(p)
   365  
   366  	connInfo.HostKey = testClientPublicKey
   367  	connInfo.Port = uint16(port)
   368  
   369  	cfg, err = prepareSSHConfig(connInfo)
   370  	if err != nil {
   371  		t.Fatal(err)
   372  	}
   373  
   374  	c = &Communicator{
   375  		connInfo: connInfo,
   376  		config:   cfg,
   377  	}
   378  
   379  	err = c.Start(&cmd)
   380  	if err == nil || !strings.Contains(err.Error(), "mismatch") {
   381  		t.Fatalf("expected host key mismatch, got error:%v", err)
   382  	}
   383  }
   384  
   385  func TestHostCert(t *testing.T) {
   386  	pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(testServerHostCert))
   387  	if err != nil {
   388  		t.Fatal(err)
   389  	}
   390  
   391  	signer, err := ssh.ParsePrivateKey([]byte(testServerPrivateKey))
   392  	if err != nil {
   393  		t.Fatal(err)
   394  	}
   395  
   396  	signer, err = ssh.NewCertSigner(pk.(*ssh.Certificate), signer)
   397  	if err != nil {
   398  		t.Fatal(err)
   399  	}
   400  
   401  	address := newMockLineServer(t, signer, testClientPublicKey)
   402  	host, p, _ := net.SplitHostPort(address)
   403  	port, _ := strconv.Atoi(p)
   404  
   405  	connInfo := &connectionInfo{
   406  		User:     "user",
   407  		Password: "pass",
   408  		Host:     host,
   409  		HostKey:  testCAPublicKey,
   410  		Port:     uint16(port),
   411  		Timeout:  "30s",
   412  	}
   413  
   414  	cfg, err := prepareSSHConfig(connInfo)
   415  	if err != nil {
   416  		t.Fatal(err)
   417  	}
   418  
   419  	c := &Communicator{
   420  		connInfo: connInfo,
   421  		config:   cfg,
   422  	}
   423  
   424  	var cmd remote.Cmd
   425  	stdout := new(bytes.Buffer)
   426  	cmd.Command = "echo foo"
   427  	cmd.Stdout = stdout
   428  
   429  	if err := c.Start(&cmd); err != nil {
   430  		t.Fatal(err)
   431  	}
   432  	if err := c.Disconnect(); err != nil {
   433  		t.Fatal(err)
   434  	}
   435  
   436  	// now check with the wrong HostKey
   437  	address = newMockLineServer(t, signer, testClientPublicKey)
   438  	_, p, _ = net.SplitHostPort(address)
   439  	port, _ = strconv.Atoi(p)
   440  
   441  	connInfo.HostKey = testClientPublicKey
   442  	connInfo.Port = uint16(port)
   443  
   444  	cfg, err = prepareSSHConfig(connInfo)
   445  	if err != nil {
   446  		t.Fatal(err)
   447  	}
   448  
   449  	c = &Communicator{
   450  		connInfo: connInfo,
   451  		config:   cfg,
   452  	}
   453  
   454  	err = c.Start(&cmd)
   455  	if err == nil || !strings.Contains(err.Error(), "authorities") {
   456  		t.Fatalf("expected host key mismatch, got error:%v", err)
   457  	}
   458  }
   459  
   460  const SERVER_PEM = `-----BEGIN RSA PRIVATE KEY-----
   461  MIIEpAIBAAKCAQEA8CkDr7uxCFt6lQUVwS8NyPO+fQNxORoGnMnN/XhVJZvpqyKR
   462  Uji9R0d8D66bYxUUsabXjP2y4HTVzbZtnvXFZZshk0cOtJjjekpYJaLK2esPR/iX
   463  wvSltNkrDQDPN/RmgEEMIevW8AgrPsqrnybFHxTpd7rEUHXBOe4nMNRIg3XHykB6
   464  jZk8q5bBPUe3I/f0DK5TJEBpTc6dO3P/j93u55VUqr39/SPRHnld2mCw+c8v6UOh
   465  sssO/DIZFPScD3DYqsk2N+/nz9zXfcOTdWGhawgxuIo1DTokrNQbG3pDrLqcWgqj
   466  13vqJFCmRA0O2CQIwJePd6+Np/XO3Uh/KL6FlQIDAQABAoIBAQCmvQMXNmvCDqk7
   467  30zsVDvw4fHGH+azK3Od1aqTqcEMHISOUbCtckFPxLzIsoSltRQqB1kuRVG07skm
   468  Stsu+xny4lLcSwBVuLRuykEK2EyYIc/5Owo6y9pkhkaSf5ZfFes4bnD6+B/BhRpp
   469  PRMMq0E+xCkX/G6iIi9mhgdlqm0x/vKtjzQeeshw9+gRcRLUpX+UeKFKXMXcDayx
   470  qekr1bAaQKNBhTK+CbZjcqzG4f+BXVGRTZ9nsPAV+yTnWUCU0TghwPmtthHbebqa
   471  9hlkum7qik/bQj/tjJ8/b0vTfHQSVxhtPG/ZV2Tn9ZuL/vrkYqeyMU8XkJ/uaEvH
   472  WPyOcB4BAoGBAP5o5JSEtPog+U3JFrLNSRjz5ofZNVkJzice+0XyqlzJDHhX5tF8
   473  mriYQZLLXYhckBm4IdkhTn/dVbXNQTzyy2WVuO5nU8bkCMvGL9CGpW4YGqwGf7NX
   474  e4H3emtRjLv8VZpUHe/RUUDhmYvMSt1qmXuskfpROuGfLhQBUd6A4J+BAoGBAPGp
   475  UcMKjrxZ5qjYU6DLgS+xeca4Eu70HgdbSQbRo45WubXjyXvTRFij36DrpxJWf1D7
   476  lIsyBifoTra/lAuC1NQXGYWjTCdk2ey8Ll5qOgiXvE6lINHABr+U/Z90/g6LuML2
   477  VzaZbq/QLcT3yVsdyTogKckzCaKsCpusyHE1CXAVAoGAd6kMglKc8N0bhZukgnsN
   478  +5+UeacPcY6sGTh4RWErAjNKGzx1A2lROKvcg9gFaULoQECcIw2IZ5nKW5VsLueg
   479  BWrTrcaJ4A2XmYjhKnp6SvspaGoyHD90hx/Iw7t6r1yzQsB3yDmytwqldtyjBdvC
   480  zynPC2azhDWjraMlR7tka4ECgYAxwvLiHa9sm3qCtCDsUFtmrb3srITBjaUNUL/F
   481  1q8+JR+Sk7gudj9xnTT0VvINNaB71YIt83wPBagHu4VJpYQbtDH+MbUBu6OgOtO1
   482  f1w53rzY2OncJxV8p7pd9mJGLoE6LC2jQY7oRw7Vq0xcJdME1BCmrIrEY3a/vaF8
   483  pjYuTQKBgQCIOH23Xita8KmhH0NdlWxZfcQt1j3AnOcKe6UyN4BsF8hqS7eTA52s
   484  WjG5X2IBl7gs1eMM1qkqR8npS9nwfO/pBmZPwjiZoilypXxWj+c+P3vwre2yija4
   485  bXgFVj4KFBwhr1+8KcobxC0SAPEouMvSkxzjjw+gnebozUtPlud9jA==
   486  -----END RSA PRIVATE KEY-----
   487  `
   488  const CLIENT_CERT_SIGNED_BY_SERVER = `ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgbMDNUn4M2TtzrSH7MOT2QsvLzZWjehJ5TYrBOp9p+lwAAAADAQABAAABAQCyu57E7zIWRyEWuaiOiikOSZKFjbwLkpE9fboFfLLsNUJj4zw+5bZUJtzWK8roPjgL8s1oPncro5wuTtI2Nu4fkpeFK0Hb33o6Eyksuj4Om4+6Uemn1QEcb0bZqK8Zyg9Dg9deP7LeE0v78b5/jZafFgwxv+/sMhM0PRD34NCDYcYmkkHlvQtQWFAdbPXCgghObedZyYdoqZVuhTsiPMWtQS/cc9M4tv6mPOuQlhZt3R/Oh/kwUyu45oGRb5bhO4JicozFS3oeClpU+UMbgslkzApJqxZBWN7+PDFSZhKk2GslyeyP4sH3E30Z00yVi/lQYgmQsB+Hg6ClemNQMNu/AAAAAAAAAAAAAAACAAAABHVzZXIAAAAIAAAABHVzZXIAAAAAWzBjXAAAAAB/POfPAAAAAAAAAAAAAAAAAAABFwAAAAdzc2gtcnNhAAAAAwEAAQAAAQEA8CkDr7uxCFt6lQUVwS8NyPO+fQNxORoGnMnN/XhVJZvpqyKRUji9R0d8D66bYxUUsabXjP2y4HTVzbZtnvXFZZshk0cOtJjjekpYJaLK2esPR/iXwvSltNkrDQDPN/RmgEEMIevW8AgrPsqrnybFHxTpd7rEUHXBOe4nMNRIg3XHykB6jZk8q5bBPUe3I/f0DK5TJEBpTc6dO3P/j93u55VUqr39/SPRHnld2mCw+c8v6UOhsssO/DIZFPScD3DYqsk2N+/nz9zXfcOTdWGhawgxuIo1DTokrNQbG3pDrLqcWgqj13vqJFCmRA0O2CQIwJePd6+Np/XO3Uh/KL6FlQAAAQ8AAAAHc3NoLXJzYQAAAQC6sKEQHyl954BQn2BXuTgOB3NkENBxN7SD8ZaS8PNkDESytLjSIqrzoE6m7xuzprA+G23XRrCY/um3UvM7+7+zbwig2NIBbGbp3QFliQHegQKW6hTZP09jAQZk5jRrrEr/QT/s+gtHPmjxJK7XOQYxhInDKj+aJg62ExcwpQlP/0ATKNOIkdzTzzq916p0UOnnVaaPMKibh5Lv69GafIhKJRZSuuLN9fvs1G1RuUbxn/BNSeoRCr54L++Ztg09fJxunoyELs8mwgzCgB3pdZoUR2Z6ak05W4mvH3lkSz2BKUrlwxI6mterxhJy1GuN1K/zBG0gEMl2UTLajGK3qKM8 itbitloaner@MacBook-Pro-4.fios-router.home`
   489  const CLIENT_PEM = `-----BEGIN RSA PRIVATE KEY-----
   490  MIIEpAIBAAKCAQEAsruexO8yFkchFrmojoopDkmShY28C5KRPX26BXyy7DVCY+M8
   491  PuW2VCbc1ivK6D44C/LNaD53K6OcLk7SNjbuH5KXhStB2996OhMpLLo+DpuPulHp
   492  p9UBHG9G2aivGcoPQ4PXXj+y3hNL+/G+f42WnxYMMb/v7DITND0Q9+DQg2HGJpJB
   493  5b0LUFhQHWz1woIITm3nWcmHaKmVboU7IjzFrUEv3HPTOLb+pjzrkJYWbd0fzof5
   494  MFMruOaBkW+W4TuCYnKMxUt6HgpaVPlDG4LJZMwKSasWQVje/jwxUmYSpNhrJcns
   495  j+LB9xN9GdNMlYv5UGIJkLAfh4OgpXpjUDDbvwIDAQABAoIBAEu2ctFVyk/pnbi0
   496  uRR4rl+hBvKQUeJNGj2ELvL4Ggs5nIAX2IOEZ7JKLC6FqpSrFq7pEd5g57aSvixX
   497  s3DH4CN7w7fj1ShBCNPlHgIWewdRGpeA74vrDWdwNAEsFdDE6aZeCTOhpDGy1vNJ
   498  OrtpzS5i9pN0jTvvEneEjtWSZIHiiVlN+0hsFaiwZ6KXON+sDccZPmnP6Fzwj5Rc
   499  WS0dKSwnxnx0otWgwWFs8nr306nSeMsNmQkHsS9lz4DEVpp9owdzrX1JmbQvNYAV
   500  ohmB3ET4JYFgerqPXJfed9poueGuWCP6MYhsjNeHN35QhofxdO5/0i3JlZfqwZei
   501  tNq/0oECgYEA6SqjRqDiIp3ajwyB7Wf0cIQG/P6JZDyN1jl//htgniliIH5UP1Tm
   502  uAMG5MincV6X9lOyXyh6Yofu5+NR0yt9SqbDZVJ3ZCxKTun7pxJvQFd7wl5bMkiJ
   503  qVfS08k6gQHHDoO+eel+DtpIfWc+e3tvX0aihSU0GZEMqDXYkkphLGECgYEAxDxb
   504  +JwJ3N5UEjjkuvFBpuJnmjIaN9HvQkTv3inlx1gLE4iWBZXXsu4aWF8MCUeAAZyP
   505  42hQDSkCYX/A22tYCEn/jfrU6A+6rkWBTjdUlYLvlSkhosSnO+117WEItb5cUE95
   506  hF4UY7LNs1AsDkV4WE87f/EjpxSwUAjB2Lfd/B8CgYAJ/JiHsuZcozQ0Qk3iVDyF
   507  ATKnbWOHFozgqw/PW27U92LLj32eRM2o/gAylmGNmoaZt1YBe2NaiwXxiqv7hnZU
   508  VzYxRcn1UWxRWvY7Xq/DKrwTRCVVzwOObEOMbKcD1YaoGX50DEso6bKHJH/pnAzW
   509  INlfKIvFuI+5OK0w/tyQoQKBgQCf/jpaOxaLfrV62eobRQJrByLDBGB97GsvU7di
   510  IjTWz8DQH0d5rE7d8uWF8ZCFrEcAiV6DYZQK9smbJqbd/uoacAKtBro5rkFdPwwK
   511  8m/DKqsdqRhkdgOHh7bjYH7Sdy8ax4Fi27WyB6FQtmgFBrz0+zyetsODwQlzZ4Bs
   512  qpSRrwKBgQC0vWHrY5aGIdF+b8EpP0/SSLLALpMySHyWhDyxYcPqdhszYbjDcavv
   513  xrrLXNUD2duBHKPVYE+7uVoDkpZXLUQ4x8argo/IwQM6Kh2ma1y83TYMT6XhL1+B
   514  5UPcl6RXZBCkiU7nFIG6/0XKFqVWc3fU8e09X+iJwXIJ5Jatywtg+g==
   515  -----END RSA PRIVATE KEY-----
   516  `
   517  
   518  func TestCertificateBasedAuth(t *testing.T) {
   519  	signer, err := ssh.ParsePrivateKey([]byte(SERVER_PEM))
   520  	if err != nil {
   521  		t.Fatalf("unable to parse private key: %v", err)
   522  	}
   523  	address := newMockLineServer(t, signer, CLIENT_CERT_SIGNED_BY_SERVER)
   524  	host, p, _ := net.SplitHostPort(address)
   525  	port, _ := strconv.Atoi(p)
   526  
   527  	connInfo := &connectionInfo{
   528  		User:        "user",
   529  		Host:        host,
   530  		PrivateKey:  CLIENT_PEM,
   531  		Certificate: CLIENT_CERT_SIGNED_BY_SERVER,
   532  		Port:        uint16(port),
   533  		Timeout:     "30s",
   534  	}
   535  
   536  	cfg, err := prepareSSHConfig(connInfo)
   537  	if err != nil {
   538  		t.Fatal(err)
   539  	}
   540  
   541  	c := &Communicator{
   542  		connInfo: connInfo,
   543  		config:   cfg,
   544  	}
   545  
   546  	var cmd remote.Cmd
   547  	stdout := new(bytes.Buffer)
   548  	cmd.Command = "echo foo"
   549  	cmd.Stdout = stdout
   550  
   551  	if err := c.Start(&cmd); err != nil {
   552  		t.Fatal(err)
   553  	}
   554  	if err := c.Disconnect(); err != nil {
   555  		t.Fatal(err)
   556  	}
   557  }
   558  
   559  func TestAccUploadFile(t *testing.T) {
   560  	// use the local ssh server and scp binary to check uploads
   561  	if ok := os.Getenv("SSH_UPLOAD_TEST"); ok == "" {
   562  		t.Log("Skipping Upload Acceptance without SSH_UPLOAD_TEST set")
   563  		t.Skip()
   564  	}
   565  
   566  	v := cty.ObjectVal(map[string]cty.Value{
   567  		"type":    cty.StringVal("ssh"),
   568  		"user":    cty.StringVal(os.Getenv("USER")),
   569  		"host":    cty.StringVal("127.0.0.1"),
   570  		"port":    cty.StringVal("22"),
   571  		"timeout": cty.StringVal("30s"),
   572  	})
   573  
   574  	c, err := New(v)
   575  	if err != nil {
   576  		t.Fatalf("error creating communicator: %s", err)
   577  	}
   578  
   579  	tmpDir, err := ioutil.TempDir("", "communicator")
   580  	if err != nil {
   581  		t.Fatal(err)
   582  	}
   583  	defer os.RemoveAll(tmpDir)
   584  
   585  	content := []byte("this is the file content")
   586  	source := bytes.NewReader(content)
   587  	tmpFile := filepath.Join(tmpDir, "tempFile.out")
   588  	err = c.Upload(tmpFile, source)
   589  	if err != nil {
   590  		t.Fatalf("error uploading file: %s", err)
   591  	}
   592  
   593  	data, err := ioutil.ReadFile(tmpFile)
   594  	if err != nil {
   595  		t.Fatal(err)
   596  	}
   597  
   598  	if !bytes.Equal(data, content) {
   599  		t.Fatalf("bad: %s", data)
   600  	}
   601  }
   602  
   603  func TestAccHugeUploadFile(t *testing.T) {
   604  	// use the local ssh server and scp binary to check uploads
   605  	if ok := os.Getenv("SSH_UPLOAD_TEST"); ok == "" {
   606  		t.Log("Skipping Upload Acceptance without SSH_UPLOAD_TEST set")
   607  		t.Skip()
   608  	}
   609  
   610  	v := cty.ObjectVal(map[string]cty.Value{
   611  		"type":    cty.StringVal("ssh"),
   612  		"host":    cty.StringVal("127.0.0.1"),
   613  		"user":    cty.StringVal(os.Getenv("USER")),
   614  		"port":    cty.StringVal("22"),
   615  		"timeout": cty.StringVal("30s"),
   616  	})
   617  
   618  	c, err := New(v)
   619  	if err != nil {
   620  		t.Fatalf("error creating communicator: %s", err)
   621  	}
   622  
   623  	// copy 4GB of data, random to prevent compression.
   624  	size := int64(1 << 32)
   625  	source := io.LimitReader(rand.New(rand.NewSource(0)), size)
   626  
   627  	dest, err := ioutil.TempFile("", "communicator")
   628  	if err != nil {
   629  		t.Fatal(err)
   630  	}
   631  	destName := dest.Name()
   632  	dest.Close()
   633  	defer os.Remove(destName)
   634  
   635  	t.Log("Uploading to", destName)
   636  
   637  	// bypass the Upload method so we can directly supply the file size
   638  	// preventing the extra copy of the huge file.
   639  	targetDir := filepath.Dir(destName)
   640  	targetFile := filepath.Base(destName)
   641  
   642  	scpFunc := func(w io.Writer, stdoutR *bufio.Reader) error {
   643  		return scpUploadFile(targetFile, source, w, stdoutR, size)
   644  	}
   645  
   646  	cmd, err := quoteShell([]string{"scp", "-vt", targetDir}, c.connInfo.TargetPlatform)
   647  	if err != nil {
   648  		t.Fatal(err)
   649  	}
   650  	err = c.scpSession(cmd, scpFunc)
   651  	if err != nil {
   652  		t.Fatal(err)
   653  	}
   654  
   655  	// check the final file size
   656  	fs, err := os.Stat(destName)
   657  	if err != nil {
   658  		t.Fatal(err)
   659  	}
   660  
   661  	if fs.Size() != size {
   662  		t.Fatalf("expected file size of %d, got %d", size, fs.Size())
   663  	}
   664  }
   665  
   666  func TestScriptPath(t *testing.T) {
   667  	cases := []struct {
   668  		Input   string
   669  		Pattern string
   670  	}{
   671  		{
   672  			"/tmp/script.sh",
   673  			`^/tmp/script\.sh$`,
   674  		},
   675  		{
   676  			"/tmp/script_%RAND%.sh",
   677  			`^/tmp/script_(\d+)\.sh$`,
   678  		},
   679  	}
   680  
   681  	for _, tc := range cases {
   682  		v := cty.ObjectVal(map[string]cty.Value{
   683  			"type":        cty.StringVal("ssh"),
   684  			"host":        cty.StringVal("127.0.0.1"),
   685  			"script_path": cty.StringVal(tc.Input),
   686  		})
   687  
   688  		comm, err := New(v)
   689  		if err != nil {
   690  			t.Fatalf("err: %s", err)
   691  		}
   692  		output := comm.ScriptPath()
   693  
   694  		match, err := regexp.Match(tc.Pattern, []byte(output))
   695  		if err != nil {
   696  			t.Fatalf("bad: %s\n\nerr: %s", tc.Input, err)
   697  		}
   698  		if !match {
   699  			t.Fatalf("bad: %s\n\n%s", tc.Input, output)
   700  		}
   701  	}
   702  }
   703  
   704  func TestScriptPath_randSeed(t *testing.T) {
   705  	// Pre GH-4186 fix, this value was the deterministic start the pseudorandom
   706  	// chain of unseeded math/rand values for Int31().
   707  	staticSeedPath := "/tmp/terraform_1298498081.sh"
   708  	c, err := New(cty.ObjectVal(map[string]cty.Value{
   709  		"type": cty.StringVal("ssh"),
   710  		"host": cty.StringVal("127.0.0.1"),
   711  	}))
   712  	if err != nil {
   713  		t.Fatalf("err: %s", err)
   714  	}
   715  	path := c.ScriptPath()
   716  	if path == staticSeedPath {
   717  		t.Fatalf("rand not seeded! got: %s", path)
   718  	}
   719  }
   720  
   721  var testClientPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDE6A1c4n+OtEPEFlNKTZf2i03L3NylSYmvmJ8OLmzLuPZmJBJt4G3VZ/60s1aKzwLKrTq20S+ONG4zvnK5zIPoauoNNdUJKbg944hB4OE+HDbrBhk7SH+YWCsCILBoSXwAVdUEic6FWf/SeqBSmTBySHvpuNOw16J+SK6Ardx8k64F2tRkZuC6AmOZijgKa/sQKjWAIVPk34ECM6OLfPc3kKUEfkdpYLvuMfuRMfSTlxn5lFC0b0SovK9aWfNMBH9iXLQkieQ5rXoyzUC7mwgnASgl8cqw1UrToiUuhvneduXBhbQfmC/Upv+tL6dSSk+0DlgVKEHuJmc8s8+/qpdL`
   722  
   723  func acceptUserPass(goodUser, goodPass string) func(ssh.ConnMetadata, []byte) (*ssh.Permissions, error) {
   724  	return func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
   725  		if c.User() == goodUser && string(pass) == goodPass {
   726  			return nil, nil
   727  		}
   728  		return nil, fmt.Errorf("password rejected for %q", c.User())
   729  	}
   730  }
   731  
   732  func acceptPublicKey(keystr string) func(ssh.ConnMetadata, ssh.PublicKey) (*ssh.Permissions, error) {
   733  	return func(_ ssh.ConnMetadata, inkey ssh.PublicKey) (*ssh.Permissions, error) {
   734  		goodkey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(keystr))
   735  		if err != nil {
   736  			return nil, fmt.Errorf("error parsing key: %v", err)
   737  		}
   738  
   739  		if bytes.Equal(inkey.Marshal(), goodkey.Marshal()) {
   740  			return nil, nil
   741  		}
   742  
   743  		return nil, fmt.Errorf("public key rejected")
   744  	}
   745  }