github.com/amanya/packer@v0.12.1-0.20161117214323-902ac5ab2eb6/builder/amazon/common/ssh.go (about)

     1  package common
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  	"os"
     8  	"time"
     9  
    10  	"github.com/aws/aws-sdk-go/service/ec2"
    11  	"github.com/mitchellh/multistep"
    12  	packerssh "github.com/mitchellh/packer/communicator/ssh"
    13  	"golang.org/x/crypto/ssh"
    14  	"golang.org/x/crypto/ssh/agent"
    15  )
    16  
    17  type ec2Describer interface {
    18  	DescribeInstances(*ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error)
    19  }
    20  
    21  var (
    22  	// modified in tests
    23  	sshHostSleepDuration = time.Second
    24  )
    25  
    26  // SSHHost returns a function that can be given to the SSH communicator
    27  // for determining the SSH address based on the instance DNS name.
    28  func SSHHost(e ec2Describer, private bool) func(multistep.StateBag) (string, error) {
    29  	return func(state multistep.StateBag) (string, error) {
    30  		const tries = 2
    31  		// <= with current structure to check result of describing `tries` times
    32  		for j := 0; j <= tries; j++ {
    33  			var host string
    34  			i := state.Get("instance").(*ec2.Instance)
    35  			if i.VpcId != nil && *i.VpcId != "" {
    36  				if i.PublicIpAddress != nil && *i.PublicIpAddress != "" && !private {
    37  					host = *i.PublicIpAddress
    38  				} else if i.PrivateIpAddress != nil && *i.PrivateIpAddress != "" {
    39  					host = *i.PrivateIpAddress
    40  				}
    41  			} else if private && i.PrivateIpAddress != nil && *i.PrivateIpAddress != "" {
    42  				host = *i.PrivateIpAddress
    43  			} else if i.PublicDnsName != nil && *i.PublicDnsName != "" {
    44  				host = *i.PublicDnsName
    45  			}
    46  
    47  			if host != "" {
    48  				return host, nil
    49  			}
    50  
    51  			r, err := e.DescribeInstances(&ec2.DescribeInstancesInput{
    52  				InstanceIds: []*string{i.InstanceId},
    53  			})
    54  			if err != nil {
    55  				return "", err
    56  			}
    57  
    58  			if len(r.Reservations) == 0 || len(r.Reservations[0].Instances) == 0 {
    59  				return "", fmt.Errorf("instance not found: %s", *i.InstanceId)
    60  			}
    61  
    62  			state.Put("instance", r.Reservations[0].Instances[0])
    63  			time.Sleep(sshHostSleepDuration)
    64  		}
    65  
    66  		return "", errors.New("couldn't determine IP address for instance")
    67  	}
    68  }
    69  
    70  // SSHConfig returns a function that can be used for the SSH communicator
    71  // config for connecting to the instance created over SSH using the private key
    72  // or password.
    73  func SSHConfig(useAgent bool, username, password string) func(multistep.StateBag) (*ssh.ClientConfig, error) {
    74  	return func(state multistep.StateBag) (*ssh.ClientConfig, error) {
    75  		if useAgent {
    76  			authSock := os.Getenv("SSH_AUTH_SOCK")
    77  			if authSock == "" {
    78  				return nil, fmt.Errorf("SSH_AUTH_SOCK is not set")
    79  			}
    80  
    81  			sshAgent, err := net.Dial("unix", authSock)
    82  			if err != nil {
    83  				return nil, fmt.Errorf("Cannot connect to SSH Agent socket %q: %s", authSock, err)
    84  			}
    85  
    86  			return &ssh.ClientConfig{
    87  				User: username,
    88  				Auth: []ssh.AuthMethod{
    89  					ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers),
    90  				},
    91  			}, nil
    92  		}
    93  
    94  		privateKey, hasKey := state.GetOk("privateKey")
    95  		if hasKey {
    96  
    97  			signer, err := ssh.ParsePrivateKey([]byte(privateKey.(string)))
    98  			if err != nil {
    99  				return nil, fmt.Errorf("Error setting up SSH config: %s", err)
   100  			}
   101  			return &ssh.ClientConfig{
   102  				User: username,
   103  				Auth: []ssh.AuthMethod{
   104  					ssh.PublicKeys(signer),
   105  				},
   106  			}, nil
   107  
   108  		} else {
   109  			return &ssh.ClientConfig{
   110  				User: username,
   111  				Auth: []ssh.AuthMethod{
   112  					ssh.Password(password),
   113  					ssh.KeyboardInteractive(
   114  						packerssh.PasswordKeyboardInteractive(password)),
   115  				}}, nil
   116  		}
   117  	}
   118  }