github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/builder/openstack/ssh.go (about) 1 package openstack 2 3 import ( 4 "errors" 5 "fmt" 6 "log" 7 "net" 8 "os" 9 "time" 10 11 "github.com/gophercloud/gophercloud" 12 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" 13 "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" 14 packerssh "github.com/hashicorp/packer/communicator/ssh" 15 "github.com/hashicorp/packer/helper/multistep" 16 "golang.org/x/crypto/ssh" 17 "golang.org/x/crypto/ssh/agent" 18 ) 19 20 // CommHost looks up the host for the communicator. 21 func CommHost( 22 client *gophercloud.ServiceClient, 23 sshinterface string, 24 sshipversion string) func(multistep.StateBag) (string, error) { 25 return func(state multistep.StateBag) (string, error) { 26 s := state.Get("server").(*servers.Server) 27 28 // If we have a specific interface, try that 29 if sshinterface != "" { 30 if addr := sshAddrFromPool(s, sshinterface, sshipversion); addr != "" { 31 log.Printf("[DEBUG] Using IP address %s from specified interface %s to connect", addr, sshinterface) 32 return addr, nil 33 } 34 } 35 36 // If we have a floating IP, use that 37 ip := state.Get("access_ip").(*floatingips.FloatingIP) 38 if ip != nil && ip.IP != "" { 39 log.Printf("[DEBUG] Using floating IP %s to connect", ip.IP) 40 return ip.IP, nil 41 } 42 43 if s.AccessIPv4 != "" { 44 log.Printf("[DEBUG] Using AccessIPv4 %s to connect", s.AccessIPv4) 45 return s.AccessIPv4, nil 46 } 47 48 // Try to get it from the requested interface 49 if addr := sshAddrFromPool(s, sshinterface, sshipversion); addr != "" { 50 log.Printf("[DEBUG] Using IP address %s to connect", addr) 51 return addr, nil 52 } 53 54 s, err := servers.Get(client, s.ID).Extract() 55 if err != nil { 56 return "", err 57 } 58 59 state.Put("server", s) 60 time.Sleep(1 * time.Second) 61 62 return "", errors.New("couldn't determine IP address for server") 63 } 64 } 65 66 // SSHConfig returns a function that can be used for the SSH communicator 67 // config for connecting to the instance created over SSH using a private key 68 // or a password. 69 func SSHConfig(useAgent bool, username, password string) func(multistep.StateBag) (*ssh.ClientConfig, error) { 70 return func(state multistep.StateBag) (*ssh.ClientConfig, error) { 71 if useAgent { 72 authSock := os.Getenv("SSH_AUTH_SOCK") 73 if authSock == "" { 74 return nil, fmt.Errorf("SSH_AUTH_SOCK is not set") 75 } 76 77 sshAgent, err := net.Dial("unix", authSock) 78 if err != nil { 79 return nil, fmt.Errorf("Cannot connect to SSH Agent socket %q: %s", authSock, err) 80 } 81 82 return &ssh.ClientConfig{ 83 User: username, 84 Auth: []ssh.AuthMethod{ 85 ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers), 86 }, 87 HostKeyCallback: ssh.InsecureIgnoreHostKey(), 88 }, nil 89 } 90 91 privateKey, hasKey := state.GetOk("privateKey") 92 93 if hasKey { 94 95 signer, err := ssh.ParsePrivateKey([]byte(privateKey.(string))) 96 if err != nil { 97 return nil, fmt.Errorf("Error setting up SSH config: %s", err) 98 } 99 100 return &ssh.ClientConfig{ 101 User: username, 102 Auth: []ssh.AuthMethod{ 103 ssh.PublicKeys(signer), 104 }, 105 HostKeyCallback: ssh.InsecureIgnoreHostKey(), 106 }, nil 107 108 } else { 109 110 return &ssh.ClientConfig{ 111 User: username, 112 HostKeyCallback: ssh.InsecureIgnoreHostKey(), 113 Auth: []ssh.AuthMethod{ 114 ssh.Password(password), 115 ssh.KeyboardInteractive( 116 packerssh.PasswordKeyboardInteractive(password)), 117 }}, nil 118 } 119 } 120 } 121 122 func sshAddrFromPool(s *servers.Server, desired string, sshIPVersion string) string { 123 // Get all the addresses associated with this server. This 124 // was taken directly from Terraform. 125 for pool, networkAddresses := range s.Addresses { 126 // If we have an SSH interface specified, skip it if no match 127 if desired != "" && pool != desired { 128 log.Printf( 129 "[INFO] Skipping pool %s, doesn't match requested %s", 130 pool, desired) 131 continue 132 } 133 134 elements, ok := networkAddresses.([]interface{}) 135 if !ok { 136 log.Printf( 137 "[ERROR] Unknown return type for address field: %#v", 138 networkAddresses) 139 continue 140 } 141 142 for _, element := range elements { 143 var addr string 144 address := element.(map[string]interface{}) 145 if address["OS-EXT-IPS:type"] == "floating" { 146 addr = address["addr"].(string) 147 } else if sshIPVersion == "4" { 148 if address["version"].(float64) == 4 { 149 addr = address["addr"].(string) 150 } 151 } else if sshIPVersion == "6" { 152 if address["version"].(float64) == 6 { 153 addr = fmt.Sprintf("[%s]", address["addr"].(string)) 154 } 155 } else { 156 if address["version"].(float64) == 6 { 157 addr = fmt.Sprintf("[%s]", address["addr"].(string)) 158 } else { 159 addr = address["addr"].(string) 160 } 161 } 162 163 if addr != "" { 164 log.Printf("[DEBUG] Detected address: %s", addr) 165 return addr 166 } 167 } 168 } 169 170 return "" 171 }