From e8e0e8c9486e7af724e2018254e166ff3aa6ebef Mon Sep 17 00:00:00 2001 From: Andrew Pryde Date: Wed, 2 Aug 2017 17:18:53 +0100 Subject: [PATCH] Add ssh_private_key_file support to BMCS builder --- builder/oracle/bmcs/builder.go | 15 +++--- builder/oracle/bmcs/ssh.go | 43 ++++++++++------- ...create_ssh_key.go => step_ssh_key_pair.go} | 46 ++++++++++++++----- .../source/docs/builders/oracle-bmcs.html.md | 12 ++--- 4 files changed, 76 insertions(+), 40 deletions(-) rename builder/oracle/bmcs/{step_create_ssh_key.go => step_ssh_key_pair.go} (68%) diff --git a/builder/oracle/bmcs/builder.go b/builder/oracle/bmcs/builder.go index 98695751b..e4cb51ade 100644 --- a/builder/oracle/bmcs/builder.go +++ b/builder/oracle/bmcs/builder.go @@ -56,16 +56,19 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe // Build the steps steps := []multistep.Step{ - &stepCreateSSHKey{ - Debug: b.config.PackerDebug, - DebugKeyPath: fmt.Sprintf("bmc_%s.pem", b.config.PackerBuildName), + &stepKeyPair{ + Debug: b.config.PackerDebug, + DebugKeyPath: fmt.Sprintf("bmcs_%s.pem", b.config.PackerBuildName), + PrivateKeyFile: b.config.Comm.SSHPrivateKey, }, &stepCreateInstance{}, &stepInstanceInfo{}, &communicator.StepConnect{ - Config: &b.config.Comm, - Host: commHost, - SSHConfig: sshConfig, + Config: &b.config.Comm, + Host: commHost, + SSHConfig: SSHConfig( + b.config.Comm.SSHUsername, + b.config.Comm.SSHPassword), }, &common.StepProvision{}, &stepImage{}, diff --git a/builder/oracle/bmcs/ssh.go b/builder/oracle/bmcs/ssh.go index c5c826991..d1079a1b1 100644 --- a/builder/oracle/bmcs/ssh.go +++ b/builder/oracle/bmcs/ssh.go @@ -19,22 +19,33 @@ func commHost(state multistep.StateBag) (string, error) { return ipAddress, nil } -func sshConfig(state multistep.StateBag) (*ssh.ClientConfig, error) { - c := state.Get("config").(*Config) - privateKey := state.Get("privateKey").(string) +// SSHConfig 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. +func SSHConfig(username, password string) func(state multistep.StateBag) (*ssh.ClientConfig, error) { + return func(state multistep.StateBag) (*ssh.ClientConfig, error) { + privateKey, hasKey := state.GetOk("privateKey") + if hasKey { - signer, err := ssh.ParsePrivateKey([]byte(privateKey)) - if err != nil { - return nil, fmt.Errorf("Error setting up SSH config: %s", err) + signer, err := ssh.ParsePrivateKey([]byte(privateKey.(string))) + if err != nil { + return nil, fmt.Errorf("Error setting up SSH config: %s", err) + } + return &ssh.ClientConfig{ + User: username, + Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)}, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + }, nil + + } + + return &ssh.ClientConfig{ + User: username, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + Auth: []ssh.AuthMethod{ + ssh.Password(password), + ssh.KeyboardInteractive(packerssh.PasswordKeyboardInteractive(password)), + }, + }, nil } - - return &ssh.ClientConfig{ - User: c.Comm.SSHUsername, - Auth: []ssh.AuthMethod{ - ssh.PublicKeys(signer), - ssh.Password(c.Comm.SSHPassword), - ssh.KeyboardInteractive( - packerssh.PasswordKeyboardInteractive(c.Comm.SSHPassword)), - }, - }, nil } diff --git a/builder/oracle/bmcs/step_create_ssh_key.go b/builder/oracle/bmcs/step_ssh_key_pair.go similarity index 68% rename from builder/oracle/bmcs/step_create_ssh_key.go rename to builder/oracle/bmcs/step_ssh_key_pair.go index 6c9f9e2d6..a3fc662d4 100644 --- a/builder/oracle/bmcs/step_create_ssh_key.go +++ b/builder/oracle/bmcs/step_ssh_key_pair.go @@ -12,6 +12,7 @@ import ( "crypto/x509" "encoding/pem" "fmt" + "io/ioutil" "os" "runtime" @@ -20,17 +21,41 @@ import ( "golang.org/x/crypto/ssh" ) -type stepCreateSSHKey struct { - Debug bool - DebugKeyPath string +type stepKeyPair struct { + Debug bool + DebugKeyPath string + PrivateKeyFile string } -func (s *stepCreateSSHKey) Run(state multistep.StateBag) multistep.StepAction { +func (s *stepKeyPair) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) + if s.PrivateKeyFile != "" { + privateKeyBytes, err := ioutil.ReadFile(s.PrivateKeyFile) + 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 + } + + key, err := ssh.ParsePrivateKey(privateKeyBytes) + if err != nil { + err = fmt.Errorf("Error parsing 'ssh_private_key_file': %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + + state.Put("publicKey", string(ssh.MarshalAuthorizedKey(key.PublicKey()))) + state.Put("privateKey", string(privateKeyBytes)) + + return multistep.ActionContinue + } + ui.Say("Creating temporary ssh key for instance...") - priv, err := rsa.GenerateKey(rand.Reader, 2014) + priv, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { err = fmt.Errorf("Error creating temporary SSH key: %s", err) ui.Error(err.Error()) @@ -40,11 +65,7 @@ func (s *stepCreateSSHKey) Run(state multistep.StateBag) multistep.StepAction { // ASN.1 DER encoded form privDer := x509.MarshalPKCS1PrivateKey(priv) - privBlk := pem.Block{ - Type: "RSA PRIVATE KEY", - Headers: nil, - Bytes: privDer, - } + privBlk := pem.Block{Type: "RSA PRIVATE KEY", Headers: nil, Bytes: privDer} // Set the private key in the statebag for later state.Put("privateKey", string(pem.EncodeToMemory(&privBlk))) @@ -61,7 +82,8 @@ func (s *stepCreateSSHKey) Run(state multistep.StateBag) multistep.StepAction { pubSSHFormat := string(ssh.MarshalAuthorizedKey(pub)) state.Put("publicKey", pubSSHFormat) - // If we're in debug mode, output the private key to the working directory. + // If we're in debug mode, output the private key to the working + // directory. if s.Debug { ui.Message(fmt.Sprintf("Saving key for debug purposes: %s", s.DebugKeyPath)) f, err := os.Create(s.DebugKeyPath) @@ -95,6 +117,6 @@ func (s *stepCreateSSHKey) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } -func (s *stepCreateSSHKey) Cleanup(state multistep.StateBag) { +func (s *stepKeyPair) Cleanup(state multistep.StateBag) { // Nothing to do } diff --git a/website/source/docs/builders/oracle-bmcs.html.md b/website/source/docs/builders/oracle-bmcs.html.md index 0bab3dacb..8eda2ecaa 100644 --- a/website/source/docs/builders/oracle-bmcs.html.md +++ b/website/source/docs/builders/oracle-bmcs.html.md @@ -67,8 +67,8 @@ builder. - `compartment_ocid` (string) - The OCID of the [compartment](https://docs.us-phoenix-1.oraclecloud.com/Content/GSG/Tasks/choosingcompartments.htm) - - `fingerprint` (string) - Fingerprint for the key pair being used. Overrides - value provided by the + - `fingerprint` (string) - Fingerprint for the BMCS API signing key. + Overrides value provided by the [BMCS config file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm) if present. @@ -104,13 +104,13 @@ builder. - `image_name` (string) - The name to assign to the resulting custom image. - - `key_file` (string) - Full path and filename of the private key. Overrides - value provided by the + - `key_file` (string) - Full path and filename of the BMCS API signing key. + Overrides value provided by the [BMCS config file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm) if present. - - `pass_phrase` (string) - Pass phrase used to decrypt the private key used - to sign requests to the BMCS API. Overrides value provided by the + - `pass_phrase` (string) - Pass phrase used to decrypt the BMCS API signing + key. Overrides value provided by the [BMCS config file](https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/sdkconfig.htm) if present.