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 }