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