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