github.com/prasannakumarik25/packer@v1.3.2/helper/communicator/config.go (about) 1 package communicator 2 3 import ( 4 "errors" 5 "fmt" 6 "io/ioutil" 7 "net" 8 "os" 9 "time" 10 11 packerssh "github.com/hashicorp/packer/communicator/ssh" 12 "github.com/hashicorp/packer/helper/multistep" 13 helperssh "github.com/hashicorp/packer/helper/ssh" 14 "github.com/hashicorp/packer/template/interpolate" 15 "github.com/masterzen/winrm" 16 "golang.org/x/crypto/ssh" 17 "golang.org/x/crypto/ssh/agent" 18 ) 19 20 // Config is the common configuration that communicators allow within 21 // a builder. 22 type Config struct { 23 Type string `mapstructure:"communicator"` 24 25 // SSH 26 SSHHost string `mapstructure:"ssh_host"` 27 SSHPort int `mapstructure:"ssh_port"` 28 SSHUsername string `mapstructure:"ssh_username"` 29 SSHPassword string `mapstructure:"ssh_password"` 30 SSHPublicKey []byte `mapstructure:"ssh_public_key"` 31 SSHPrivateKey []byte `mapstructure:"ssh_private_key"` 32 SSHKeyPairName string `mapstructure:"ssh_keypair_name"` 33 SSHTemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"` 34 SSHClearAuthorizedKeys bool `mapstructure:"ssh_clear_authorized_keys"` 35 SSHPrivateKeyFile string `mapstructure:"ssh_private_key_file"` 36 SSHInterface string `mapstructure:"ssh_interface"` 37 SSHIPVersion string `mapstructure:"ssh_ip_version"` 38 SSHPty bool `mapstructure:"ssh_pty"` 39 SSHTimeout time.Duration `mapstructure:"ssh_timeout"` 40 SSHAgentAuth bool `mapstructure:"ssh_agent_auth"` 41 SSHDisableAgentForwarding bool `mapstructure:"ssh_disable_agent_forwarding"` 42 SSHHandshakeAttempts int `mapstructure:"ssh_handshake_attempts"` 43 SSHBastionHost string `mapstructure:"ssh_bastion_host"` 44 SSHBastionPort int `mapstructure:"ssh_bastion_port"` 45 SSHBastionAgentAuth bool `mapstructure:"ssh_bastion_agent_auth"` 46 SSHBastionUsername string `mapstructure:"ssh_bastion_username"` 47 SSHBastionPassword string `mapstructure:"ssh_bastion_password"` 48 SSHBastionPrivateKeyFile string `mapstructure:"ssh_bastion_private_key_file"` 49 SSHFileTransferMethod string `mapstructure:"ssh_file_transfer_method"` 50 SSHProxyHost string `mapstructure:"ssh_proxy_host"` 51 SSHProxyPort int `mapstructure:"ssh_proxy_port"` 52 SSHProxyUsername string `mapstructure:"ssh_proxy_username"` 53 SSHProxyPassword string `mapstructure:"ssh_proxy_password"` 54 SSHKeepAliveInterval time.Duration `mapstructure:"ssh_keep_alive_interval"` 55 SSHReadWriteTimeout time.Duration `mapstructure:"ssh_read_write_timeout"` 56 57 // WinRM 58 WinRMUser string `mapstructure:"winrm_username"` 59 WinRMPassword string `mapstructure:"winrm_password"` 60 WinRMHost string `mapstructure:"winrm_host"` 61 WinRMPort int `mapstructure:"winrm_port"` 62 WinRMTimeout time.Duration `mapstructure:"winrm_timeout"` 63 WinRMUseSSL bool `mapstructure:"winrm_use_ssl"` 64 WinRMInsecure bool `mapstructure:"winrm_insecure"` 65 WinRMUseNTLM bool `mapstructure:"winrm_use_ntlm"` 66 WinRMTransportDecorator func() winrm.Transporter 67 } 68 69 // SSHConfigFunc returns a function that can be used for the SSH communicator 70 // config for connecting to the instance created over SSH using the private key 71 // or password. 72 func (c *Config) SSHConfigFunc() func(multistep.StateBag) (*ssh.ClientConfig, error) { 73 return func(state multistep.StateBag) (*ssh.ClientConfig, error) { 74 sshConfig := &ssh.ClientConfig{ 75 User: c.SSHUsername, 76 HostKeyCallback: ssh.InsecureIgnoreHostKey(), 77 } 78 79 if c.SSHAgentAuth { 80 authSock := os.Getenv("SSH_AUTH_SOCK") 81 if authSock == "" { 82 return nil, fmt.Errorf("SSH_AUTH_SOCK is not set") 83 } 84 85 sshAgent, err := net.Dial("unix", authSock) 86 if err != nil { 87 return nil, fmt.Errorf("Cannot connect to SSH Agent socket %q: %s", authSock, err) 88 } 89 90 sshConfig.Auth = append(sshConfig.Auth, ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers)) 91 } 92 93 var privateKeys [][]byte 94 if c.SSHPrivateKeyFile != "" { 95 // key based auth 96 bytes, err := ioutil.ReadFile(c.SSHPrivateKeyFile) 97 if err != nil { 98 return nil, fmt.Errorf("Error setting up SSH config: %s", err) 99 } 100 privateKeys = append(privateKeys, bytes) 101 } 102 103 // aws,alicloud,cloudstack,digitalOcean,oneAndOne,openstack,oracle & profitbricks key 104 if iKey, hasKey := state.GetOk("privateKey"); hasKey { 105 privateKeys = append(privateKeys, []byte(iKey.(string))) 106 } 107 108 if len(c.SSHPrivateKey) != 0 { 109 privateKeys = append(privateKeys, c.SSHPrivateKey) 110 } 111 112 for _, key := range privateKeys { 113 signer, err := ssh.ParsePrivateKey(key) 114 if err != nil { 115 return nil, fmt.Errorf("Error setting up SSH config: %s", err) 116 } 117 sshConfig.Auth = append(sshConfig.Auth, ssh.PublicKeys(signer)) 118 } 119 120 if c.SSHPassword != "" { 121 sshConfig.Auth = append(sshConfig.Auth, 122 ssh.Password(c.SSHPassword), 123 ssh.KeyboardInteractive(packerssh.PasswordKeyboardInteractive(c.SSHPassword)), 124 ) 125 } 126 return sshConfig, nil 127 } 128 } 129 130 // Port returns the port that will be used for access based on config. 131 func (c *Config) Port() int { 132 switch c.Type { 133 case "ssh": 134 return c.SSHPort 135 case "winrm": 136 return c.WinRMPort 137 default: 138 return 0 139 } 140 } 141 142 // Host returns the port that will be used for access based on config. 143 func (c *Config) Host() string { 144 switch c.Type { 145 case "ssh": 146 return c.SSHHost 147 case "winrm": 148 return c.WinRMHost 149 default: 150 return "" 151 } 152 } 153 154 // User returns the port that will be used for access based on config. 155 func (c *Config) User() string { 156 switch c.Type { 157 case "ssh": 158 return c.SSHUsername 159 case "winrm": 160 return c.WinRMUser 161 default: 162 return "" 163 } 164 } 165 166 // Password returns the port that will be used for access based on config. 167 func (c *Config) Password() string { 168 switch c.Type { 169 case "ssh": 170 return c.SSHPassword 171 case "winrm": 172 return c.WinRMPassword 173 default: 174 return "" 175 } 176 } 177 178 func (c *Config) Prepare(ctx *interpolate.Context) []error { 179 if c.Type == "" { 180 c.Type = "ssh" 181 } 182 183 var errs []error 184 switch c.Type { 185 case "ssh": 186 if es := c.prepareSSH(ctx); len(es) > 0 { 187 errs = append(errs, es...) 188 } 189 case "winrm": 190 if es := c.prepareWinRM(ctx); len(es) > 0 { 191 errs = append(errs, es...) 192 } 193 case "docker", "none": 194 break 195 default: 196 return []error{fmt.Errorf("Communicator type %s is invalid", c.Type)} 197 } 198 199 return errs 200 } 201 202 func (c *Config) prepareSSH(ctx *interpolate.Context) []error { 203 if c.SSHPort == 0 { 204 c.SSHPort = 22 205 } 206 207 if c.SSHTimeout == 0 { 208 c.SSHTimeout = 5 * time.Minute 209 } 210 211 if c.SSHKeepAliveInterval == 0 { 212 c.SSHKeepAliveInterval = 5 * time.Second 213 } 214 215 if c.SSHHandshakeAttempts == 0 { 216 c.SSHHandshakeAttempts = 10 217 } 218 219 if c.SSHBastionHost != "" { 220 if c.SSHBastionPort == 0 { 221 c.SSHBastionPort = 22 222 } 223 224 if c.SSHBastionPrivateKeyFile == "" && c.SSHPrivateKeyFile != "" { 225 c.SSHBastionPrivateKeyFile = c.SSHPrivateKeyFile 226 } 227 } 228 229 if c.SSHProxyHost != "" { 230 if c.SSHProxyPort == 0 { 231 c.SSHProxyPort = 1080 232 } 233 } 234 235 if c.SSHFileTransferMethod == "" { 236 c.SSHFileTransferMethod = "scp" 237 } 238 239 // Validation 240 var errs []error 241 if c.SSHUsername == "" { 242 errs = append(errs, errors.New("An ssh_username must be specified\n Note: some builders used to default ssh_username to \"root\".")) 243 } 244 245 if c.SSHPrivateKeyFile != "" { 246 if _, err := os.Stat(c.SSHPrivateKeyFile); err != nil { 247 errs = append(errs, fmt.Errorf( 248 "ssh_private_key_file is invalid: %s", err)) 249 } else if _, err := helperssh.FileSigner(c.SSHPrivateKeyFile); err != nil { 250 errs = append(errs, fmt.Errorf( 251 "ssh_private_key_file is invalid: %s", err)) 252 } 253 } 254 255 if c.SSHBastionHost != "" && !c.SSHBastionAgentAuth { 256 if c.SSHBastionPassword == "" && c.SSHBastionPrivateKeyFile == "" { 257 errs = append(errs, errors.New( 258 "ssh_bastion_password or ssh_bastion_private_key_file must be specified")) 259 } 260 } 261 262 if c.SSHFileTransferMethod != "scp" && c.SSHFileTransferMethod != "sftp" { 263 errs = append(errs, fmt.Errorf( 264 "ssh_file_transfer_method ('%s') is invalid, valid methods: sftp, scp", 265 c.SSHFileTransferMethod)) 266 } 267 268 if c.SSHBastionHost != "" && c.SSHProxyHost != "" { 269 errs = append(errs, errors.New("please specify either ssh_bastion_host or ssh_proxy_host, not both")) 270 } 271 272 return errs 273 } 274 275 func (c *Config) prepareWinRM(ctx *interpolate.Context) []error { 276 if c.WinRMPort == 0 && c.WinRMUseSSL { 277 c.WinRMPort = 5986 278 } else if c.WinRMPort == 0 { 279 c.WinRMPort = 5985 280 } 281 282 if c.WinRMTimeout == 0 { 283 c.WinRMTimeout = 30 * time.Minute 284 } 285 286 if c.WinRMUseNTLM == true { 287 c.WinRMTransportDecorator = func() winrm.Transporter { return &winrm.ClientNTLM{} } 288 } 289 290 var errs []error 291 if c.WinRMUser == "" { 292 errs = append(errs, errors.New("winrm_username must be specified.")) 293 } 294 295 return errs 296 }