github.phpd.cn/hashicorp/packer@v1.3.2/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 99 commonhelper.SetSharedState("winrm_password", s.Comm.WinRMPassword, s.BuildName) 100 packer.LogSecretFilter.Set(s.Comm.WinRMPassword) 101 102 return multistep.ActionContinue 103 } 104 105 func (s *StepGetPassword) Cleanup(multistep.StateBag) { 106 commonhelper.RemoveSharedStateFile("winrm_password", s.BuildName) 107 } 108 109 func (s *StepGetPassword) waitForPassword(state multistep.StateBag, cancel <-chan struct{}) (string, error) { 110 ec2conn := state.Get("ec2").(*ec2.EC2) 111 instance := state.Get("instance").(*ec2.Instance) 112 privateKey := s.Comm.SSHPrivateKey 113 114 for { 115 select { 116 case <-cancel: 117 log.Println("[INFO] Retrieve password wait cancelled. Exiting loop.") 118 return "", errors.New("Retrieve password wait cancelled") 119 case <-time.After(5 * time.Second): 120 } 121 122 resp, err := ec2conn.GetPasswordData(&ec2.GetPasswordDataInput{ 123 InstanceId: instance.InstanceId, 124 }) 125 if err != nil { 126 err := fmt.Errorf("Error retrieving auto-generated instance password: %s", err) 127 return "", err 128 } 129 130 if resp.PasswordData != nil && *resp.PasswordData != "" { 131 decryptedPassword, err := decryptPasswordDataWithPrivateKey( 132 *resp.PasswordData, []byte(privateKey)) 133 if err != nil { 134 err := fmt.Errorf("Error decrypting auto-generated instance password: %s", err) 135 return "", err 136 } 137 138 return decryptedPassword, nil 139 } 140 141 log.Printf("[DEBUG] Password is blank, will retry...") 142 } 143 } 144 145 func decryptPasswordDataWithPrivateKey(passwordData string, pemBytes []byte) (string, error) { 146 encryptedPasswd, err := base64.StdEncoding.DecodeString(passwordData) 147 if err != nil { 148 return "", err 149 } 150 151 block, _ := pem.Decode(pemBytes) 152 var asn1Bytes []byte 153 if _, ok := block.Headers["DEK-Info"]; ok { 154 return "", errors.New("encrypted private key isn't yet supported") 155 /* 156 asn1Bytes, err = x509.DecryptPEMBlock(block, password) 157 if err != nil { 158 return "", err 159 } 160 */ 161 } else { 162 asn1Bytes = block.Bytes 163 } 164 165 key, err := x509.ParsePKCS1PrivateKey(asn1Bytes) 166 if err != nil { 167 return "", err 168 } 169 170 out, err := rsa.DecryptPKCS1v15(nil, key, encryptedPasswd) 171 if err != nil { 172 return "", err 173 } 174 175 return string(out), nil 176 }