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 }