func()

in gke-windows-builder/builder/builder/gce.go [471:561]


func (s *Server) resetWindowsPassword(username string) (string, error) {
	//Create random key and encode
	key, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		log.Printf("Failed to generate random RSA key: %v", err)
		return "", err
	}
	buf := make([]byte, 4)
	binary.BigEndian.PutUint32(buf, uint32(key.E))
	wpc := WindowsPasswordConfig{
		key:      key,
		UserName: username,
		Modulus:  base64.StdEncoding.EncodeToString(key.N.Bytes()),
		Exponent: base64.StdEncoding.EncodeToString(buf[1:]),
		Email:    "nobody@nowhere.com",
		ExpireOn: time.Now().Add(time.Minute * 5),
	}
	data, err := json.Marshal(wpc)
	dstring := string(data)
	if err != nil {
		log.Printf("Failed to marshal JSON: %v", err)
		return "", err
	}

	//Write key to instance metadata and wait for op to complete
	log.Print("Writing Windows instance metadata for password reset")
	var found bool
	for _, mdi := range s.instance.Metadata.Items {
		if mdi.Key == "windows-keys" {
			log.Print("Altering current key")

			mdi.Value = &dstring
			found = true
			break
		}
	}

	if !found {
		s.instance.Metadata.Items = append(s.instance.Metadata.Items, &compute.MetadataItems{Key: "windows-keys", Value: &dstring})
	}

	op, err := s.service.Instances.SetMetadata(s.projectID, s.zone, s.instance.Name, &compute.Metadata{
		Fingerprint: s.instance.Metadata.Fingerprint,
		Items:       s.instance.Metadata.Items,
	}).Do()
	if err != nil {
		log.Printf("Failed to set instance metadata: %v", err)
		return "", err
	}
	err = s.waitForComputeOperation(op)
	if err != nil {
		log.Printf("Compute operation timed out")
		return "", err
	}

	//Read and decode password
	log.Print("Waiting for Windows password response")
	timeout := time.Now().Add(time.Minute * 5)
	hash := sha1.New()
	for time.Now().Before(timeout) {
		output, err := s.service.Instances.GetSerialPortOutput(s.projectID, s.zone, s.instance.Name).Port(4).Do()
		if err != nil {
			log.Printf("Unable to get serial port output: %v", err)
			return "", err
		}
		responses := strings.Split(output.Contents, "\n")
		for _, response := range responses {
			var wpr WindowsPasswordResponse
			if err := json.Unmarshal([]byte(response), &wpr); err != nil {
				log.Printf("Cannot Unmarshal password: %v", err)
				continue
			}
			if wpr.Modulus == wpc.Modulus {
				decodedPassword, err := base64.StdEncoding.DecodeString(wpr.EncryptedPassword)
				if err != nil {
					log.Printf("Cannot Base64 decode password: %v", err)
					return "", err
				}
				password, err := rsa.DecryptOAEP(hash, rand.Reader, wpc.key, decodedPassword, nil)
				if err != nil {
					log.Printf("Cannot decrypt password response: %v", err)
					return "", err
				}
				return string(password), nil
			}
		}
		time.Sleep(2 * time.Second)
	}
	err = errors.New("Could not retrieve password before timeout")
	return "", err
}