Skip to content

Commit fceb463

Browse files
committed
RequestCertificate: reset unconditionally instead of during retrieve
In Venafi#269, I got convinced that it would be a good solution to call "Reset" only if the "Retrieve" call returned a known message. Later on, we realized that there was a bad interaction between "Request" and "Reset(restart=true)". For some reason, when a problem arises (such as CA being down), TPP returns the old certificate, and vcert ends up showing the message "unmatched key modulus". We realized that calling "Reset(restart=false)" before Request prevents this bug. Although that's one extra HTTP call, it seems this call is very inexpensive. One downside that was brought up during the PR Venafi#269 was that any extra HTTP call would slow the TPP server because the HTTP called are "queued" (not concurrently processed).
1 parent 641994a commit fceb463

2 files changed

Lines changed: 76 additions & 1 deletion

File tree

pkg/venafi/tpp/connector.go

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -678,18 +678,46 @@ func (c *Connector) proccessLocation(req *certificate.Request) error {
678678
return nil
679679
}
680680

681-
// RequestCertificate submits the CSR to TPP returning the DN of the requested Certificate
681+
// RequestCertificate requests the certificate to TPP and returns the DN of the
682+
// requested Certificate (this is called the "pickup ID" in the vcert CLI). If
683+
// the certificate is in the middle of an enrollment, the enrollment will be
684+
// reset before requesting (this doesn't affect the existing certificate if one
685+
// was already issued).
682686
func (c *Connector) RequestCertificate(req *certificate.Request) (requestID string, err error) {
683687
if req.Location != nil {
684688
err = c.proccessLocation(req)
685689
if err != nil {
686690
return
687691
}
688692
}
693+
689694
tppCertificateRequest, err := prepareRequest(req, c.zone)
690695
if err != nil {
691696
return "", err
692697
}
698+
699+
certDN := getCertificateDN(c.zone, req.Subject.CommonName)
700+
701+
// We unconditionally reset any prior failed enrollment so that we don't get
702+
// stuck with "Fix any errors, and then click Retry." (60% of the time) or
703+
// "WebSDK CertRequest" (40% of the time).
704+
//
705+
// It would be preferable to only reset when necessary to avoid the extra
706+
// call. We tried that in https://github.com/Venafi/vcert/pull/269. It turns
707+
// out that calling "request" followed by "reset(restart=true)" causes a
708+
// race in TPP.
709+
//
710+
// Unconditionally resetting isn't optimal, but "reset(restart=false)" is
711+
// lightweight. We haven't verified that it doesn't slow things down on
712+
// large TPP instances.
713+
//
714+
// Note that resetting won't affect the existing certificate if one was
715+
// already issued.
716+
err = c.reset(certDN, false)
717+
if err != nil {
718+
return "", fmt.Errorf("while resetting certificate in case of previous enrollment: %w", err)
719+
}
720+
693721
statusCode, status, body, err := c.request("POST", urlResourceCertificateRequest, tppCertificateRequest)
694722
if err != nil {
695723
return "", err
@@ -758,6 +786,43 @@ func (c *Connector) RequestCertificate(req *certificate.Request) (requestID stri
758786
return
759787
}
760788

789+
// For the purpose of using this function in RequestCertificate, we ignore the
790+
// errors "certificate not found" and "reset was not needed".
791+
func (c *Connector) reset(certificateDN string, restart bool) error {
792+
statusCode, status, body, err := c.request("POST", urlResourceCertificateReset, certificateResetRequest{
793+
CertificateDN: certificateDN,
794+
Restart: restart,
795+
})
796+
if err != nil {
797+
return fmt.Errorf("while resetting certificate due to a past failed enrollment: %w", err)
798+
}
799+
800+
switch {
801+
case statusCode == http.StatusOK:
802+
return nil
803+
case statusCode == http.StatusBadRequest:
804+
var decodedResetResponse certificateRequestResponse
805+
if err := json.Unmarshal(body, &decodedResetResponse); err != nil {
806+
return fmt.Errorf("failed to decode reset response: HTTP %d: %s: %s", statusCode, status, body)
807+
}
808+
809+
// We don't need to error out if the certificate is already reset.
810+
if decodedResetResponse.Error == "Reset is not completed. No reset is required for the certificate." {
811+
return nil
812+
}
813+
814+
// This is specific to RequestCertificate: we want to silently skip
815+
// certificates that don't exist.
816+
if strings.HasSuffix(decodedResetResponse.Error, "does not exist or you do not have sufficient rights to the object.") {
817+
return nil
818+
}
819+
820+
return fmt.Errorf("while resetting certificate due to a past failed enrollment. Status: %s, Body: ", status, string(body))
821+
default:
822+
return fmt.Errorf("while resetting certificate due to a past failed enrollment. Status: %s, Body: ", status, string(body))
823+
}
824+
}
825+
761826
func (c *Connector) GetPolicy(name string) (*policy.PolicySpecification, error) {
762827
var ps *policy.PolicySpecification
763828
var tp policy.TppPolicy

pkg/venafi/tpp/tpp.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,15 @@ type certificateRenewResponse struct {
146146
Error string `json:",omitempty"`
147147
}
148148

149+
type certificateResetRequest struct {
150+
CertificateDN string `json:",omitempty"`
151+
Restart bool `json:",omitempty"`
152+
}
153+
154+
type certificateResetResponse struct {
155+
Error string `json:""`
156+
}
157+
149158
type sanItem struct {
150159
Type int `json:""`
151160
Name string `json:""`
@@ -355,6 +364,7 @@ const (
355364
urlResourceCertificateRevoke urlResource = "vedsdk/certificates/revoke"
356365
urlResourceCertificatesAssociate urlResource = "vedsdk/certificates/associate"
357366
urlResourceCertificatesDissociate urlResource = "vedsdk/certificates/dissociate"
367+
urlResourceCertificateReset urlResource = "vedsdk/certificates/reset"
358368
urlResourceCertificate urlResource = "vedsdk/certificates/"
359369
urlResourceCertificateSearch = urlResourceCertificate
360370
urlResourceCertificatesList = urlResourceCertificate

0 commit comments

Comments
 (0)