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  }