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 }