github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/builder/amazon/common/step_get_password.go (about) 1 package common 2 3 import ( 4 "context" 5 "crypto/rsa" 6 "crypto/x509" 7 "encoding/base64" 8 "encoding/pem" 9 "errors" 10 "fmt" 11 "log" 12 "time" 13 14 "github.com/aws/aws-sdk-go/service/ec2" 15 commonhelper "github.com/hashicorp/packer/helper/common" 16 "github.com/hashicorp/packer/helper/communicator" 17 "github.com/hashicorp/packer/helper/multistep" 18 "github.com/hashicorp/packer/packer" 19 ) 20 21 // StepGetPassword reads the password from a Windows server and sets it 22 // on the WinRM config. 23 type StepGetPassword struct { 24 Debug bool 25 Comm *communicator.Config 26 Timeout time.Duration 27 BuildName string 28 } 29 30 func (s *StepGetPassword) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { 31 ui := state.Get("ui").(packer.Ui) 32 33 // Skip if we're not using winrm 34 if s.Comm.Type != "winrm" { 35 log.Printf("[INFO] Not using winrm communicator, skipping get password...") 36 return multistep.ActionContinue 37 } 38 39 // If we already have a password, skip it 40 if s.Comm.WinRMPassword != "" { 41 ui.Say("Skipping waiting for password since WinRM password set...") 42 return multistep.ActionContinue 43 } 44 45 // Get the password 46 var password string 47 var err error 48 cancel := make(chan struct{}) 49 waitDone := make(chan bool, 1) 50 go func() { 51 ui.Say("Waiting for auto-generated password for instance...") 52 ui.Message( 53 "It is normal for this process to take up to 15 minutes,\n" + 54 "but it usually takes around 5. Please wait.") 55 password, err = s.waitForPassword(state, cancel) 56 waitDone <- true 57 }() 58 59 timeout := time.After(s.Timeout) 60 WaitLoop: 61 for { 62 // Wait for either SSH to become available, a timeout to occur, 63 // or an interrupt to come through. 64 select { 65 case <-waitDone: 66 if err != nil { 67 ui.Error(fmt.Sprintf("Error waiting for password: %s", err)) 68 state.Put("error", err) 69 return multistep.ActionHalt 70 } 71 72 ui.Message(fmt.Sprintf(" \nPassword retrieved!")) 73 s.Comm.WinRMPassword = password 74 break WaitLoop 75 case <-timeout: 76 err := fmt.Errorf("Timeout waiting for password.") 77 state.Put("error", err) 78 ui.Error(err.Error()) 79 close(cancel) 80 return multistep.ActionHalt 81 case <-time.After(1 * time.Second): 82 if _, ok := state.GetOk(multistep.StateCancelled); ok { 83 // The step sequence was cancelled, so cancel waiting for password 84 // and just start the halting process. 85 close(cancel) 86 log.Println("[WARN] Interrupt detected, quitting waiting for password.") 87 return multistep.ActionHalt 88 } 89 } 90 } 91 92 // In debug-mode, we output the password 93 if s.Debug { 94 ui.Message(fmt.Sprintf( 95 "Password (since debug is enabled): %s", s.Comm.WinRMPassword)) 96 } 97 // store so that we can access this later during provisioning 98 commonhelper.SetSharedState("winrm_password", s.Comm.WinRMPassword, s.BuildName) 99 100 return multistep.ActionContinue 101 } 102 103 func (s *StepGetPassword) Cleanup(multistep.StateBag) { 104 commonhelper.RemoveSharedStateFile("winrm_password", s.BuildName) 105 } 106 107 func (s *StepGetPassword) waitForPassword(state multistep.StateBag, cancel <-chan struct{}) (string, error) { 108 ec2conn := state.Get("ec2").(*ec2.EC2) 109 instance := state.Get("instance").(*ec2.Instance) 110 privateKey := state.Get("privateKey").(string) 111 112 for { 113 select { 114 case <-cancel: 115 log.Println("[INFO] Retrieve password wait cancelled. Exiting loop.") 116 return "", errors.New("Retrieve password wait cancelled") 117 case <-time.After(5 * time.Second): 118 } 119 120 resp, err := ec2conn.GetPasswordData(&ec2.GetPasswordDataInput{ 121 InstanceId: instance.InstanceId, 122 }) 123 if err != nil { 124 err := fmt.Errorf("Error retrieving auto-generated instance password: %s", err) 125 return "", err 126 } 127 128 if resp.PasswordData != nil && *resp.PasswordData != "" { 129 decryptedPassword, err := decryptPasswordDataWithPrivateKey( 130 *resp.PasswordData, []byte(privateKey)) 131 if err != nil { 132 err := fmt.Errorf("Error decrypting auto-generated instance password: %s", err) 133 return "", err 134 } 135 136 return decryptedPassword, nil 137 } 138 139 log.Printf("[DEBUG] Password is blank, will retry...") 140 } 141 } 142 143 func decryptPasswordDataWithPrivateKey(passwordData string, pemBytes []byte) (string, error) { 144 encryptedPasswd, err := base64.StdEncoding.DecodeString(passwordData) 145 if err != nil { 146 return "", err 147 } 148 149 block, _ := pem.Decode(pemBytes) 150 var asn1Bytes []byte 151 if _, ok := block.Headers["DEK-Info"]; ok { 152 return "", errors.New("encrypted private key isn't yet supported") 153 /* 154 asn1Bytes, err = x509.DecryptPEMBlock(block, password) 155 if err != nil { 156 return "", err 157 } 158 */ 159 } else { 160 asn1Bytes = block.Bytes 161 } 162 163 key, err := x509.ParsePKCS1PrivateKey(asn1Bytes) 164 if err != nil { 165 return "", err 166 } 167 168 out, err := rsa.DecryptPKCS1v15(nil, key, encryptedPasswd) 169 if err != nil { 170 return "", err 171 } 172 173 return string(out), nil 174 }