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