Skip to content

Commit 5f87aea

Browse files
committed
Retry with new account if account disappeared remotely
1 parent 3dd8f7d commit 5f87aea

File tree

2 files changed

+39
-6
lines changed

2 files changed

+39
-6
lines changed

account.go

+10
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,16 @@ func (am *ACMEIssuer) saveAccount(ctx context.Context, ca string, account acme.A
171171
return storeTx(ctx, am.config.Storage, all)
172172
}
173173

174+
// deleteAccountLocally deletes the registration info and private key of the account
175+
// for the given CA from storage.
176+
func (am *ACMEIssuer) deleteAccountLocally(ctx context.Context, ca string, account acme.Account) error {
177+
primaryContact := getPrimaryContact(account)
178+
if err := am.config.Storage.Delete(ctx, am.storageKeyUserReg(ca, primaryContact)); err != nil {
179+
return err
180+
}
181+
return am.config.Storage.Delete(ctx, am.storageKeyUserPrivateKey(ca, primaryContact))
182+
}
183+
174184
// setEmail does everything it can to obtain an email address
175185
// from the user within the scope of memory and storage to use
176186
// for ACME TLS. If it cannot get an email address, it does nothing

acmeissuer.go

+29-6
Original file line numberDiff line numberDiff line change
@@ -393,12 +393,35 @@ func (am *ACMEIssuer) doIssue(ctx context.Context, csr *x509.CertificateRequest,
393393
}
394394
}
395395

396-
certChains, err := client.acmeClient.ObtainCertificateUsingCSR(ctx, client.account, csr)
397-
if err != nil {
398-
return nil, usingTestCA, fmt.Errorf("%v %w (ca=%s)", nameSet, err, client.acmeClient.Directory)
399-
}
400-
if len(certChains) == 0 {
401-
return nil, usingTestCA, fmt.Errorf("no certificate chains")
396+
// do this in a loop because there's an error case that may necessitate a retry, but not more than once
397+
var certChains []acme.Certificate
398+
for i := 0; i < 2; i++ {
399+
certChains, err = client.acmeClient.ObtainCertificateUsingCSR(ctx, client.account, csr)
400+
if err != nil {
401+
var prob acme.Problem
402+
if errors.As(err, &prob) && prob.Type == acme.ProblemTypeAccountDoesNotExist {
403+
// the account we have no longer exists on the CA, so we need to create a new one;
404+
// we could use the same key pair, but this is a good opportunity to rotate keys
405+
// (see https://caddy.community/t/acme-account-is-not-regenerated-when-acme-server-gets-reinstalled/22627)
406+
// (basically this happens if the CA gets reset or reinstalled; usually just internal PKI)
407+
err := am.deleteAccountLocally(ctx, client.iss.CA, client.account)
408+
if err != nil {
409+
return nil, usingTestCA, fmt.Errorf("%v ACME account no longer exists on CA, but resetting our local copy of the account info failed: %v", nameSet, err)
410+
}
411+
412+
// recreate account and try again
413+
client, err = am.newACMEClientWithAccount(ctx, useTestCA, false)
414+
if err != nil {
415+
return nil, false, err
416+
}
417+
continue
418+
}
419+
return nil, usingTestCA, fmt.Errorf("%v %w (ca=%s)", nameSet, err, client.acmeClient.Directory)
420+
}
421+
if len(certChains) == 0 {
422+
return nil, usingTestCA, fmt.Errorf("no certificate chains")
423+
}
424+
break
402425
}
403426

404427
preferredChain := am.selectPreferredChain(certChains)

0 commit comments

Comments
 (0)