From da0bad844142f9e3f1b4a684496e463d5947da5c Mon Sep 17 00:00:00 2001 From: Mikhail Ushanov Date: Tue, 6 Nov 2018 21:59:40 +0300 Subject: [PATCH 1/7] communicator/ssh: add private key file read helper Signed-off-by: Mikhail Ushanov --- helper/communicator/config.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/helper/communicator/config.go b/helper/communicator/config.go index 44738d61b..b7041eb40 100644 --- a/helper/communicator/config.go +++ b/helper/communicator/config.go @@ -66,6 +66,23 @@ type Config struct { WinRMTransportDecorator func() winrm.Transporter } +// ReadSSHPrivateKeyFile returns the SSH private key bytes +func (c *Config) ReadSSHPrivateKeyFile() ([]byte, error) { + var privateKey []byte + + if c.SSHPrivateKeyFile != "" { + keyPath, err := homedir.Expand(c.SSHPrivateKeyFile) + if err != nil { + return privateKey, fmt.Errorf("Error expanding path for SSH private key: %s", err) + } + privateKey, err = ioutil.ReadFile(keyPath) + if err != nil { + return privateKey, fmt.Errorf("Error on reading SSH private key: %s", err) + } + } + return privateKey, nil +} + // SSHConfigFunc returns a function that can be used for the SSH communicator // config for connecting to the instance created over SSH using the private key // or password. From 6d2a0ab0dfcbdc90284848da30bdfee5d5286005 Mon Sep 17 00:00:00 2001 From: Mikhail Ushanov Date: Thu, 1 Nov 2018 00:47:50 +0300 Subject: [PATCH 2/7] communicator/ssh: expand user path for private key Signed-off-by: Mikhail Ushanov --- helper/communicator/config.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/helper/communicator/config.go b/helper/communicator/config.go index b7041eb40..bf481a636 100644 --- a/helper/communicator/config.go +++ b/helper/communicator/config.go @@ -13,6 +13,7 @@ import ( helperssh "github.com/hashicorp/packer/helper/ssh" "github.com/hashicorp/packer/template/interpolate" "github.com/masterzen/winrm" + "github.com/mitchellh/go-homedir" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" ) @@ -109,12 +110,11 @@ func (c *Config) SSHConfigFunc() func(multistep.StateBag) (*ssh.ClientConfig, er var privateKeys [][]byte if c.SSHPrivateKeyFile != "" { - // key based auth - bytes, err := ioutil.ReadFile(c.SSHPrivateKeyFile) + privateKey, err := c.ReadSSHPrivateKeyFile() if err != nil { - return nil, fmt.Errorf("Error setting up SSH config: %s", err) + return nil, err } - privateKeys = append(privateKeys, bytes) + privateKeys = append(privateKeys, privateKey) } // aws,alicloud,cloudstack,digitalOcean,oneAndOne,openstack,oracle & profitbricks key @@ -260,10 +260,14 @@ func (c *Config) prepareSSH(ctx *interpolate.Context) []error { } if c.SSHPrivateKeyFile != "" { - if _, err := os.Stat(c.SSHPrivateKeyFile); err != nil { + path, err := homedir.Expand(c.SSHPrivateKeyFile) + if err != nil { + errs = append(errs, fmt.Errorf( + "ssh_private_key_file is invalid: %s", err)) + } else if _, err := os.Stat(path); err != nil { errs = append(errs, fmt.Errorf( "ssh_private_key_file is invalid: %s", err)) - } else if _, err := helperssh.FileSigner(c.SSHPrivateKeyFile); err != nil { + } else if _, err := helperssh.FileSigner(path); err != nil { errs = append(errs, fmt.Errorf( "ssh_private_key_file is invalid: %s", err)) } From 1c503b86d9bd85b09b977722b8b212728ff7a9a6 Mon Sep 17 00:00:00 2001 From: Mikhail Ushanov Date: Thu, 1 Nov 2018 01:26:18 +0300 Subject: [PATCH 3/7] communicator/ssh: expand user path for bastion private key Signed-off-by: Mikhail Ushanov --- helper/communicator/config.go | 12 ++++++++++++ helper/communicator/step_connect_ssh.go | 8 +++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/helper/communicator/config.go b/helper/communicator/config.go index bf481a636..61424a2a5 100644 --- a/helper/communicator/config.go +++ b/helper/communicator/config.go @@ -277,6 +277,18 @@ func (c *Config) prepareSSH(ctx *interpolate.Context) []error { if c.SSHBastionPassword == "" && c.SSHBastionPrivateKeyFile == "" { errs = append(errs, errors.New( "ssh_bastion_password or ssh_bastion_private_key_file must be specified")) + } else if c.SSHBastionPrivateKeyFile != "" { + path, err := homedir.Expand(c.SSHBastionPrivateKeyFile) + if err != nil { + errs = append(errs, fmt.Errorf( + "ssh_bastion_private_key_file is invalid: %s", err)) + } else if _, err := os.Stat(path); err != nil { + errs = append(errs, fmt.Errorf( + "ssh_bastion_private_key_file is invalid: %s", err)) + } else if _, err := helperssh.FileSigner(path); err != nil { + errs = append(errs, fmt.Errorf( + "ssh_bastion_private_key_file is invalid: %s", err)) + } } } diff --git a/helper/communicator/step_connect_ssh.go b/helper/communicator/step_connect_ssh.go index 6bdbcffae..6517a04a7 100644 --- a/helper/communicator/step_connect_ssh.go +++ b/helper/communicator/step_connect_ssh.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/packer/helper/multistep" helperssh "github.com/hashicorp/packer/helper/ssh" "github.com/hashicorp/packer/packer" + "github.com/mitchellh/go-homedir" gossh "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" "golang.org/x/net/proxy" @@ -226,7 +227,12 @@ func sshBastionConfig(config *Config) (*gossh.ClientConfig, error) { } if config.SSHBastionPrivateKeyFile != "" { - signer, err := helperssh.FileSigner(config.SSHBastionPrivateKeyFile) + path, err := homedir.Expand(config.SSHBastionPrivateKeyFile) + if err != nil { + return nil, fmt.Errorf( + "Error expanding path for SSH bastion private key: %s", err) + } + signer, err := helperssh.FileSigner(path) if err != nil { return nil, err } From 45925657fc47b7a4da4cd298055dea7e2eef71f2 Mon Sep 17 00:00:00 2001 From: Mikhail Ushanov Date: Tue, 6 Nov 2018 22:01:41 +0300 Subject: [PATCH 4/7] communicator/ssh: make ssh keys payload internal Signed-off-by: Mikhail Ushanov --- helper/communicator/config.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/helper/communicator/config.go b/helper/communicator/config.go index 61424a2a5..71e40daac 100644 --- a/helper/communicator/config.go +++ b/helper/communicator/config.go @@ -28,8 +28,6 @@ type Config struct { SSHPort int `mapstructure:"ssh_port"` SSHUsername string `mapstructure:"ssh_username"` SSHPassword string `mapstructure:"ssh_password"` - SSHPublicKey []byte `mapstructure:"ssh_public_key"` - SSHPrivateKey []byte `mapstructure:"ssh_private_key"` SSHKeyPairName string `mapstructure:"ssh_keypair_name"` SSHTemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"` SSHClearAuthorizedKeys bool `mapstructure:"ssh_clear_authorized_keys"` @@ -54,6 +52,9 @@ type Config struct { SSHProxyPassword string `mapstructure:"ssh_proxy_password"` SSHKeepAliveInterval time.Duration `mapstructure:"ssh_keep_alive_interval"` SSHReadWriteTimeout time.Duration `mapstructure:"ssh_read_write_timeout"` + // SSH Internals + SSHPublicKey []byte + SSHPrivateKey []byte // WinRM WinRMUser string `mapstructure:"winrm_username"` From ee0bff6451b3189f80f315c17e6fa626adb512b9 Mon Sep 17 00:00:00 2001 From: Mikhail Ushanov Date: Tue, 6 Nov 2018 22:02:51 +0300 Subject: [PATCH 5/7] communicator/ssh: proper error message Signed-off-by: Mikhail Ushanov --- helper/communicator/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helper/communicator/config.go b/helper/communicator/config.go index 71e40daac..fa4e053c3 100644 --- a/helper/communicator/config.go +++ b/helper/communicator/config.go @@ -130,7 +130,7 @@ func (c *Config) SSHConfigFunc() func(multistep.StateBag) (*ssh.ClientConfig, er for _, key := range privateKeys { signer, err := ssh.ParsePrivateKey(key) if err != nil { - return nil, fmt.Errorf("Error setting up SSH config: %s", err) + return nil, fmt.Errorf("Error on parsing SSH private key: %s", err) } sshConfig.Auth = append(sshConfig.Auth, ssh.PublicKeys(signer)) } From 536252683e5d0a227a98fd6cade47db4fa95149f Mon Sep 17 00:00:00 2001 From: Mikhail Ushanov Date: Tue, 6 Nov 2018 22:03:33 +0300 Subject: [PATCH 6/7] builders: reuse private key file reading function Signed-off-by: Mikhail Ushanov --- builder/alicloud/ecs/step_config_key_pair.go | 6 ++---- builder/amazon/common/step_key_pair.go | 6 ++---- builder/azure/arm/config.go | 2 +- builder/cloudstack/step_keypair.go | 7 +++---- builder/googlecompute/step_create_ssh_key.go | 6 ++---- builder/openstack/step_key_pair.go | 6 +++--- builder/oracle/common/step_ssh_key_pair.go | 5 ++--- builder/profitbricks/step_create_ssh_key.go | 5 ++--- builder/scaleway/step_create_ssh_key.go | 6 ++---- 9 files changed, 19 insertions(+), 30 deletions(-) diff --git a/builder/alicloud/ecs/step_config_key_pair.go b/builder/alicloud/ecs/step_config_key_pair.go index 9d502ea68..c4307378e 100644 --- a/builder/alicloud/ecs/step_config_key_pair.go +++ b/builder/alicloud/ecs/step_config_key_pair.go @@ -3,7 +3,6 @@ package ecs import ( "context" "fmt" - "io/ioutil" "os" "runtime" @@ -28,10 +27,9 @@ func (s *stepConfigAlicloudKeyPair) Run(_ context.Context, state multistep.State if s.Comm.SSHPrivateKeyFile != "" { ui.Say("Using existing SSH private key") - privateKeyBytes, err := ioutil.ReadFile(s.Comm.SSHPrivateKeyFile) + privateKeyBytes, err := s.Comm.ReadSSHPrivateKeyFile() if err != nil { - state.Put("error", fmt.Errorf( - "Error loading configured private key file: %s", err)) + state.Put("error", err) return multistep.ActionHalt } diff --git a/builder/amazon/common/step_key_pair.go b/builder/amazon/common/step_key_pair.go index 9b34b64af..ba72b5668 100644 --- a/builder/amazon/common/step_key_pair.go +++ b/builder/amazon/common/step_key_pair.go @@ -3,7 +3,6 @@ package common import ( "context" "fmt" - "io/ioutil" "os" "runtime" @@ -26,10 +25,9 @@ func (s *StepKeyPair) Run(_ context.Context, state multistep.StateBag) multistep if s.Comm.SSHPrivateKeyFile != "" { ui.Say("Using existing SSH private key") - privateKeyBytes, err := ioutil.ReadFile(s.Comm.SSHPrivateKeyFile) + privateKeyBytes, err := s.Comm.ReadSSHPrivateKeyFile() if err != nil { - state.Put("error", fmt.Errorf( - "Error loading configured private key file: %s", err)) + state.Put("error", err) return multistep.ActionHalt } diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index d72d28550..77daa67c0 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -326,7 +326,7 @@ func setSshValues(c *Config) error { } if c.Comm.SSHPrivateKeyFile != "" { - privateKeyBytes, err := ioutil.ReadFile(c.Comm.SSHPrivateKeyFile) + privateKeyBytes, err := c.Comm.ReadSSHPrivateKeyFile() if err != nil { return err } diff --git a/builder/cloudstack/step_keypair.go b/builder/cloudstack/step_keypair.go index f08bd706d..bb40534fe 100644 --- a/builder/cloudstack/step_keypair.go +++ b/builder/cloudstack/step_keypair.go @@ -3,7 +3,6 @@ package cloudstack import ( "context" "fmt" - "io/ioutil" "os" "runtime" @@ -23,10 +22,10 @@ func (s *stepKeypair) Run(_ context.Context, state multistep.StateBag) multistep ui := state.Get("ui").(packer.Ui) if s.Comm.SSHPrivateKeyFile != "" { - privateKeyBytes, err := ioutil.ReadFile(s.Comm.SSHPrivateKeyFile) + ui.Say("Using existing SSH private key") + privateKeyBytes, err := s.Comm.ReadSSHPrivateKeyFile() if err != nil { - state.Put("error", fmt.Errorf( - "Error loading configured private key file: %s", err)) + state.Put("error", err) return multistep.ActionHalt } diff --git a/builder/googlecompute/step_create_ssh_key.go b/builder/googlecompute/step_create_ssh_key.go index 889cf1b76..897e0a6a3 100644 --- a/builder/googlecompute/step_create_ssh_key.go +++ b/builder/googlecompute/step_create_ssh_key.go @@ -7,7 +7,6 @@ import ( "crypto/x509" "encoding/pem" "fmt" - "io/ioutil" "os" "github.com/hashicorp/packer/helper/multistep" @@ -29,10 +28,9 @@ func (s *StepCreateSSHKey) Run(_ context.Context, state multistep.StateBag) mult if config.Comm.SSHPrivateKeyFile != "" { ui.Say("Using existing SSH private key") - privateKeyBytes, err := ioutil.ReadFile(config.Comm.SSHPrivateKeyFile) + privateKeyBytes, err := config.Comm.ReadSSHPrivateKeyFile() if err != nil { - state.Put("error", fmt.Errorf( - "Error loading configured private key file: %s", err)) + state.Put("error", err) return multistep.ActionHalt } diff --git a/builder/openstack/step_key_pair.go b/builder/openstack/step_key_pair.go index ccf1eaecf..a850b8056 100644 --- a/builder/openstack/step_key_pair.go +++ b/builder/openstack/step_key_pair.go @@ -28,10 +28,10 @@ func (s *StepKeyPair) Run(_ context.Context, state multistep.StateBag) multistep ui := state.Get("ui").(packer.Ui) if s.Comm.SSHPrivateKeyFile != "" { - privateKeyBytes, err := ioutil.ReadFile(s.Comm.SSHPrivateKeyFile) + ui.Say("Using existing SSH private key") + privateKeyBytes, err := s.Comm.ReadSSHPrivateKeyFile() if err != nil { - state.Put("error", fmt.Errorf( - "Error loading configured private key file: %s", err)) + state.Put("error", err) return multistep.ActionHalt } diff --git a/builder/oracle/common/step_ssh_key_pair.go b/builder/oracle/common/step_ssh_key_pair.go index 04e82b986..49a0fd01b 100644 --- a/builder/oracle/common/step_ssh_key_pair.go +++ b/builder/oracle/common/step_ssh_key_pair.go @@ -7,7 +7,6 @@ import ( "crypto/x509" "encoding/pem" "fmt" - "io/ioutil" "os" "runtime" @@ -27,9 +26,9 @@ func (s *StepKeyPair) Run(_ context.Context, state multistep.StateBag) multistep ui := state.Get("ui").(packer.Ui) if s.Comm.SSHPrivateKeyFile != "" { - privateKeyBytes, err := ioutil.ReadFile(s.Comm.SSHPrivateKeyFile) + ui.Say("Using existing SSH private key") + privateKeyBytes, err := s.Comm.ReadSSHPrivateKeyFile() if err != nil { - err = fmt.Errorf("Error loading configured private key file: %s", err) ui.Error(err.Error()) state.Put("error", err) return multistep.ActionHalt diff --git a/builder/profitbricks/step_create_ssh_key.go b/builder/profitbricks/step_create_ssh_key.go index f9678c751..68c34af33 100644 --- a/builder/profitbricks/step_create_ssh_key.go +++ b/builder/profitbricks/step_create_ssh_key.go @@ -5,7 +5,6 @@ import ( "crypto/x509" "encoding/pem" "fmt" - "io/ioutil" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" @@ -22,9 +21,9 @@ func (s *StepCreateSSHKey) Run(_ context.Context, state multistep.StateBag) mult c := state.Get("config").(*Config) if c.Comm.SSHPrivateKeyFile != "" { - pemBytes, err := ioutil.ReadFile(c.Comm.SSHPrivateKeyFile) - + pemBytes, err := c.Comm.ReadSSHPrivateKeyFile() if err != nil { + state.Put("error", err) ui.Error(err.Error()) return multistep.ActionHalt } diff --git a/builder/scaleway/step_create_ssh_key.go b/builder/scaleway/step_create_ssh_key.go index c58875724..b2d211a9a 100644 --- a/builder/scaleway/step_create_ssh_key.go +++ b/builder/scaleway/step_create_ssh_key.go @@ -7,7 +7,6 @@ import ( "crypto/x509" "encoding/pem" "fmt" - "io/ioutil" "log" "os" "runtime" @@ -28,10 +27,9 @@ func (s *stepCreateSSHKey) Run(_ context.Context, state multistep.StateBag) mult if config.Comm.SSHPrivateKeyFile != "" { ui.Say("Using existing SSH private key") - privateKeyBytes, err := ioutil.ReadFile(config.Comm.SSHPrivateKeyFile) + privateKeyBytes, err := config.Comm.ReadSSHPrivateKeyFile() if err != nil { - state.Put("error", fmt.Errorf( - "Error loading configured private key file: %s", err)) + state.Put("error", err) return multistep.ActionHalt } From 8400146a2e2b2765532fe15cad81a9a8fa2c085b Mon Sep 17 00:00:00 2001 From: Mikhail Ushanov Date: Tue, 6 Nov 2018 23:13:31 +0300 Subject: [PATCH 7/7] website: add note about tilde in ssh key path Signed-off-by: Mikhail Ushanov --- website/source/docs/templates/communicator.html.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/website/source/docs/templates/communicator.html.md b/website/source/docs/templates/communicator.html.md index 4702c9d1f..8f88e280d 100644 --- a/website/source/docs/templates/communicator.html.md +++ b/website/source/docs/templates/communicator.html.md @@ -75,8 +75,9 @@ The SSH communicator has the following options: - `ssh_bastion_port` (number) - The port of the bastion host. Defaults to `22`. -- `ssh_bastion_private_key_file` (string) - A private key file to use to - authenticate with the bastion host. +- `ssh_bastion_private_key_file` (string) - Path to a PEM encoded private + key file to use to authenticate with the bastion host. The `~` can be used + in path and will be expanded to the home directory of current user. - `ssh_bastion_username` (string) - The username to connect to the bastion host. @@ -111,7 +112,8 @@ The SSH communicator has the following options: - `ssh_port` (number) - The port to connect to SSH. This defaults to `22`. - `ssh_private_key_file` (string) - Path to a PEM encoded private key file to - use to authenticate with SSH. + use to authenticate with SSH. The `~` can be used in path and will be + expanded to the home directory of current user. - `ssh_proxy_host` (string) - A SOCKS proxy host to use for SSH connection