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