github.com/raghuse92/packer@v1.3.2/builder/openstack/step_key_pair.go (about)

     1  package openstack
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"log"
     8  	"os"
     9  	"os/exec"
    10  	"runtime"
    11  
    12  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
    13  	"github.com/hashicorp/packer/helper/communicator"
    14  	"github.com/hashicorp/packer/helper/multistep"
    15  	"github.com/hashicorp/packer/packer"
    16  	"golang.org/x/crypto/ssh"
    17  )
    18  
    19  type StepKeyPair struct {
    20  	Debug        bool
    21  	Comm         *communicator.Config
    22  	DebugKeyPath string
    23  
    24  	doCleanup bool
    25  }
    26  
    27  func (s *StepKeyPair) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
    28  	ui := state.Get("ui").(packer.Ui)
    29  
    30  	if s.Comm.SSHPrivateKeyFile != "" {
    31  		privateKeyBytes, err := ioutil.ReadFile(s.Comm.SSHPrivateKeyFile)
    32  		if err != nil {
    33  			state.Put("error", fmt.Errorf(
    34  				"Error loading configured private key file: %s", err))
    35  			return multistep.ActionHalt
    36  		}
    37  
    38  		s.Comm.SSHPrivateKey = privateKeyBytes
    39  
    40  		return multistep.ActionContinue
    41  	}
    42  
    43  	if s.Comm.SSHAgentAuth && s.Comm.SSHKeyPairName == "" {
    44  		ui.Say("Using SSH Agent with key pair in Source image")
    45  		return multistep.ActionContinue
    46  	}
    47  
    48  	if s.Comm.SSHAgentAuth && s.Comm.SSHKeyPairName != "" {
    49  		ui.Say(fmt.Sprintf("Using SSH Agent for existing key pair %s", s.Comm.SSHKeyPairName))
    50  		s.Comm.SSHKeyPairName = ""
    51  		return multistep.ActionContinue
    52  	}
    53  
    54  	if s.Comm.SSHTemporaryKeyPairName == "" {
    55  		ui.Say("Not using temporary keypair")
    56  		s.Comm.SSHKeyPairName = ""
    57  		return multistep.ActionContinue
    58  	}
    59  
    60  	config := state.Get("config").(*Config)
    61  
    62  	// We need the v2 compute client
    63  	computeClient, err := config.computeV2Client()
    64  	if err != nil {
    65  		err = fmt.Errorf("Error initializing compute client: %s", err)
    66  		state.Put("error", err)
    67  		return multistep.ActionHalt
    68  	}
    69  
    70  	ui.Say(fmt.Sprintf("Creating temporary keypair: %s ...", s.Comm.SSHTemporaryKeyPairName))
    71  	keypair, err := keypairs.Create(computeClient, keypairs.CreateOpts{
    72  		Name: s.Comm.SSHTemporaryKeyPairName,
    73  	}).Extract()
    74  	if err != nil {
    75  		state.Put("error", fmt.Errorf("Error creating temporary keypair: %s", err))
    76  		return multistep.ActionHalt
    77  	}
    78  
    79  	if len(keypair.PrivateKey) == 0 {
    80  		state.Put("error", fmt.Errorf("The temporary keypair returned was blank"))
    81  		return multistep.ActionHalt
    82  	}
    83  
    84  	ui.Say(fmt.Sprintf("Created temporary keypair: %s", s.Comm.SSHTemporaryKeyPairName))
    85  
    86  	keypair.PrivateKey = string(berToDer([]byte(keypair.PrivateKey), ui))
    87  
    88  	// If we're in debug mode, output the private key to the working
    89  	// directory.
    90  	if s.Debug {
    91  		ui.Message(fmt.Sprintf("Saving key for debug purposes: %s", s.DebugKeyPath))
    92  		f, err := os.Create(s.DebugKeyPath)
    93  		if err != nil {
    94  			state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
    95  			return multistep.ActionHalt
    96  		}
    97  		defer f.Close()
    98  
    99  		// Write the key out
   100  		if _, err := f.Write([]byte(keypair.PrivateKey)); err != nil {
   101  			state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
   102  			return multistep.ActionHalt
   103  		}
   104  
   105  		// Chmod it so that it is SSH ready
   106  		if runtime.GOOS != "windows" {
   107  			if err := f.Chmod(0600); err != nil {
   108  				state.Put("error", fmt.Errorf("Error setting permissions of debug key: %s", err))
   109  				return multistep.ActionHalt
   110  			}
   111  		}
   112  	}
   113  
   114  	// we created a temporary key, so remember to clean it up
   115  	s.doCleanup = true
   116  
   117  	// Set some state data for use in future steps
   118  	s.Comm.SSHKeyPairName = s.Comm.SSHTemporaryKeyPairName
   119  	s.Comm.SSHPrivateKey = []byte(keypair.PrivateKey)
   120  
   121  	return multistep.ActionContinue
   122  }
   123  
   124  // Work around for https://github.com/hashicorp/packer/issues/2526
   125  func berToDer(ber []byte, ui packer.Ui) []byte {
   126  	// Check if x/crypto/ssh can parse the key
   127  	_, err := ssh.ParsePrivateKey(ber)
   128  	if err == nil {
   129  		return ber
   130  	}
   131  	// Can't parse the key, maybe it's BER encoded. Try to convert it with OpenSSL.
   132  	log.Println("Couldn't parse SSH key, trying work around for [GH-2526].")
   133  
   134  	openSslPath, err := exec.LookPath("openssl")
   135  	if err != nil {
   136  		log.Println("Couldn't find OpenSSL, aborting work around.")
   137  		return ber
   138  	}
   139  
   140  	berKey, err := ioutil.TempFile("", "packer-ber-privatekey-")
   141  	defer os.Remove(berKey.Name())
   142  	if err != nil {
   143  		return ber
   144  	}
   145  	ioutil.WriteFile(berKey.Name(), ber, os.ModeAppend)
   146  	derKey, err := ioutil.TempFile("", "packer-der-privatekey-")
   147  	defer os.Remove(derKey.Name())
   148  	if err != nil {
   149  		return ber
   150  	}
   151  
   152  	args := []string{"rsa", "-in", berKey.Name(), "-out", derKey.Name()}
   153  	log.Printf("Executing: %s %v", openSslPath, args)
   154  	if err := exec.Command(openSslPath, args...).Run(); err != nil {
   155  		log.Printf("OpenSSL failed with error: %s", err)
   156  		return ber
   157  	}
   158  
   159  	der, err := ioutil.ReadFile(derKey.Name())
   160  	if err != nil {
   161  		return ber
   162  	}
   163  	ui.Say("Successfully converted BER encoded SSH key to DER encoding.")
   164  	return der
   165  }
   166  
   167  func (s *StepKeyPair) Cleanup(state multistep.StateBag) {
   168  	if !s.doCleanup {
   169  		return
   170  	}
   171  
   172  	config := state.Get("config").(*Config)
   173  	ui := state.Get("ui").(packer.Ui)
   174  
   175  	// We need the v2 compute client
   176  	computeClient, err := config.computeV2Client()
   177  	if err != nil {
   178  		ui.Error(fmt.Sprintf(
   179  			"Error cleaning up keypair. Please delete the key manually: %s", s.Comm.SSHTemporaryKeyPairName))
   180  		return
   181  	}
   182  
   183  	ui.Say(fmt.Sprintf("Deleting temporary keypair: %s ...", s.Comm.SSHTemporaryKeyPairName))
   184  	err = keypairs.Delete(computeClient, s.Comm.SSHTemporaryKeyPairName).ExtractErr()
   185  	if err != nil {
   186  		ui.Error(fmt.Sprintf(
   187  			"Error cleaning up keypair. Please delete the key manually: %s", s.Comm.SSHTemporaryKeyPairName))
   188  	}
   189  }