diff --git a/helper/communicator/config.go b/helper/communicator/config.go index a2719db13..a2d93a480 100644 --- a/helper/communicator/config.go +++ b/helper/communicator/config.go @@ -2,6 +2,8 @@ package communicator import ( "errors" + "fmt" + "os" "time" "github.com/mitchellh/packer/template/interpolate" @@ -39,6 +41,16 @@ func (c *Config) Prepare(ctx *interpolate.Context) []error { if c.SSHUsername == "" { errs = append(errs, errors.New("An ssh_username must be specified")) } + + if c.SSHPrivateKey != "" { + if _, err := os.Stat(c.SSHPrivateKey); err != nil { + errs = append(errs, fmt.Errorf( + "ssh_private_key_file is invalid: %s", err)) + } else if _, err := SSHFileSigner(c.SSHPrivateKey); err != nil { + errs = append(errs, fmt.Errorf( + "ssh_private_key_file is invalid: %s", err)) + } + } } return errs diff --git a/helper/communicator/ssh.go b/helper/communicator/ssh.go new file mode 100644 index 000000000..831d620bc --- /dev/null +++ b/helper/communicator/ssh.go @@ -0,0 +1,44 @@ +package communicator + +import ( + "encoding/pem" + "fmt" + "io/ioutil" + "os" + + "golang.org/x/crypto/ssh" +) + +// SSHFileSigner returns an ssh.Signer for a key file. +func SSHFileSigner(path string) (ssh.Signer, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + keyBytes, err := ioutil.ReadAll(f) + if err != nil { + return nil, err + } + + // We parse the private key on our own first so that we can + // show a nicer error if the private key has a password. + block, _ := pem.Decode(keyBytes) + if block == nil { + return nil, fmt.Errorf( + "Failed to read key '%s': no key found", path) + } + if block.Headers["Proc-Type"] == "4,ENCRYPTED" { + return nil, fmt.Errorf( + "Failed to read key '%s': password protected keys are\n"+ + "not supported. Please decrypt the key prior to use.", path) + } + + signer, err := ssh.ParsePrivateKey(keyBytes) + if err != nil { + return nil, fmt.Errorf("Error setting up SSH config: %s", err) + } + + return signer, nil +}