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