github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/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  	packerssh "github.com/hashicorp/packer/communicator/ssh"
    12  	"github.com/hashicorp/packer/helper/multistep"
    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, sshInterface string) 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 sshInterface != "" {
    36  				switch sshInterface {
    37  				case "public_ip":
    38  					if i.PublicIpAddress != nil {
    39  						host = *i.PublicIpAddress
    40  					}
    41  				case "private_ip":
    42  					if i.PrivateIpAddress != nil {
    43  						host = *i.PrivateIpAddress
    44  					}
    45  				case "public_dns":
    46  					if i.PublicDnsName != nil {
    47  						host = *i.PublicDnsName
    48  					}
    49  				case "private_dns":
    50  					if i.PrivateDnsName != nil {
    51  						host = *i.PrivateDnsName
    52  					}
    53  				default:
    54  					panic(fmt.Sprintf("Unknown interface type: %s", sshInterface))
    55  				}
    56  			} else if i.VpcId != nil && *i.VpcId != "" {
    57  				if i.PublicIpAddress != nil && *i.PublicIpAddress != "" {
    58  					host = *i.PublicIpAddress
    59  				} else if i.PrivateIpAddress != nil && *i.PrivateIpAddress != "" {
    60  					host = *i.PrivateIpAddress
    61  				}
    62  			} else if i.PublicDnsName != nil && *i.PublicDnsName != "" {
    63  				host = *i.PublicDnsName
    64  			}
    65  
    66  			if host != "" {
    67  				return host, nil
    68  			}
    69  
    70  			r, err := e.DescribeInstances(&ec2.DescribeInstancesInput{
    71  				InstanceIds: []*string{i.InstanceId},
    72  			})
    73  			if err != nil {
    74  				return "", err
    75  			}
    76  
    77  			if len(r.Reservations) == 0 || len(r.Reservations[0].Instances) == 0 {
    78  				return "", fmt.Errorf("instance not found: %s", *i.InstanceId)
    79  			}
    80  
    81  			state.Put("instance", r.Reservations[0].Instances[0])
    82  			time.Sleep(sshHostSleepDuration)
    83  		}
    84  
    85  		return "", errors.New("couldn't determine address for instance")
    86  	}
    87  }
    88  
    89  // SSHConfig returns a function that can be used for the SSH communicator
    90  // config for connecting to the instance created over SSH using the private key
    91  // or password.
    92  func SSHConfig(useAgent bool, username, password string) func(multistep.StateBag) (*ssh.ClientConfig, error) {
    93  	return func(state multistep.StateBag) (*ssh.ClientConfig, error) {
    94  		if useAgent {
    95  			authSock := os.Getenv("SSH_AUTH_SOCK")
    96  			if authSock == "" {
    97  				return nil, fmt.Errorf("SSH_AUTH_SOCK is not set")
    98  			}
    99  
   100  			sshAgent, err := net.Dial("unix", authSock)
   101  			if err != nil {
   102  				return nil, fmt.Errorf("Cannot connect to SSH Agent socket %q: %s", authSock, err)
   103  			}
   104  
   105  			return &ssh.ClientConfig{
   106  				User: username,
   107  				Auth: []ssh.AuthMethod{
   108  					ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers),
   109  				},
   110  				HostKeyCallback: ssh.InsecureIgnoreHostKey(),
   111  			}, nil
   112  		}
   113  
   114  		privateKey, hasKey := state.GetOk("privateKey")
   115  		if hasKey {
   116  
   117  			signer, err := ssh.ParsePrivateKey([]byte(privateKey.(string)))
   118  			if err != nil {
   119  				return nil, fmt.Errorf("Error setting up SSH config: %s", err)
   120  			}
   121  			return &ssh.ClientConfig{
   122  				User: username,
   123  				Auth: []ssh.AuthMethod{
   124  					ssh.PublicKeys(signer),
   125  				},
   126  				HostKeyCallback: ssh.InsecureIgnoreHostKey(),
   127  			}, nil
   128  
   129  		} else {
   130  			return &ssh.ClientConfig{
   131  				User:            username,
   132  				HostKeyCallback: ssh.InsecureIgnoreHostKey(),
   133  				Auth: []ssh.AuthMethod{
   134  					ssh.Password(password),
   135  					ssh.KeyboardInteractive(
   136  						packerssh.PasswordKeyboardInteractive(password)),
   137  				}}, nil
   138  		}
   139  	}
   140  }