mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-11-04 04:28:08 +00:00 
			
		
		
		
	Structure of ACME Tidy (#20494)
* Structure of ACME Tidy. * The tidy endpoints/call information. * Counts for status plumbing. * Update typo calls, add information saving date of account creation. * Missed some field locations. * Set-up of Tidy command. * Proper tidy function; lock to work with * Remove order safety buffer. * Missed a field. * Read lock for account creation; Write lock for tidy (account deletion) * Type issues fixed. * fix range operator. * Fix path_tidy read. * Add fields to auto-tidy config. * Add (and standardize) Tidy Config Response * Test pass, consistent fields * Changes from PR-Reviews. * Update test to updated default due to PR-Review.
This commit is contained in:
		@@ -217,6 +217,8 @@ type acmeAccount struct {
 | 
			
		||||
	TermsOfServiceAgreed bool              `json:"termsOfServiceAgreed"`
 | 
			
		||||
	Jwk                  []byte            `json:"jwk"`
 | 
			
		||||
	AcmeDirectory        string            `json:"acme-directory"`
 | 
			
		||||
	AccountCreatedDate   time.Time         `json:"account_created_date"`
 | 
			
		||||
	AccountRevokedDate   time.Time         `json:"account_revoked_date"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type acmeOrder struct {
 | 
			
		||||
@@ -288,6 +290,7 @@ func (a *acmeState) CreateAccount(ac *acmeContext, c *jwsCtx, contact []string,
 | 
			
		||||
		Jwk:                  c.Jwk,
 | 
			
		||||
		Status:               StatusValid,
 | 
			
		||||
		AcmeDirectory:        ac.acmeDirectory,
 | 
			
		||||
		AccountCreatedDate:   time.Now(),
 | 
			
		||||
	}
 | 
			
		||||
	json, err := logical.StorageEntryJSON(acmeAccountPrefix+c.Kid, acct)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -346,6 +346,8 @@ type backend struct {
 | 
			
		||||
 | 
			
		||||
	// Context around ACME operations
 | 
			
		||||
	acmeState       *acmeState
 | 
			
		||||
	acmeAccountLock sync.RWMutex // (Write) Locked on Tidy, (Read) Locked on Account Creation
 | 
			
		||||
	// TODO: Stress test this - eg. creating an order while an account is being revoked
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type roleOperation func(ctx context.Context, req *logical.Request, data *framework.FieldData, role *roleEntry) (*logical.Response, error)
 | 
			
		||||
 
 | 
			
		||||
@@ -4030,6 +4030,12 @@ func TestBackend_RevokePlusTidy_Intermediate(t *testing.T) {
 | 
			
		||||
			"revocation_queue_deleted_count":        json.Number("0"),
 | 
			
		||||
			"cross_revoked_cert_deleted_count":      json.Number("0"),
 | 
			
		||||
			"internal_backend_uuid":                 backendUUID,
 | 
			
		||||
			"tidy_acme":                             false,
 | 
			
		||||
			"acme_account_safety_buffer":            json.Number("2592000"),
 | 
			
		||||
			"acme_orders_deleted_count":             json.Number("0"),
 | 
			
		||||
			"acme_account_revoked_count":            json.Number("0"),
 | 
			
		||||
			"acme_account_deleted_count":            json.Number("0"),
 | 
			
		||||
			"total_acme_account_count":              json.Number("0"),
 | 
			
		||||
		}
 | 
			
		||||
		// Let's copy the times from the response so that we can use deep.Equal()
 | 
			
		||||
		timeStarted, ok := tidyStatus.Data["time_started"]
 | 
			
		||||
 
 | 
			
		||||
@@ -491,6 +491,23 @@ this removes ALL issuers within the mount (and is thus not desirable
 | 
			
		||||
in most operational scenarios).`,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fields["tidy_acme"] = &framework.FieldSchema{
 | 
			
		||||
		Type: framework.TypeBool,
 | 
			
		||||
		Description: `Set to true to enable tidying ACME accounts,
 | 
			
		||||
orders and authorizations.  ACME orders are tidied (deleted) 
 | 
			
		||||
safety_buffer after the certificate associated with them expires,
 | 
			
		||||
or after the order and relevant authorizations have expired if no 
 | 
			
		||||
certificate was produced.  Authorizations are tidied with the 
 | 
			
		||||
corresponding order.
 | 
			
		||||
 | 
			
		||||
When a valid ACME Account is at least acme_account_safety_buffer
 | 
			
		||||
old, and has no remaining orders associated with it, the account is
 | 
			
		||||
marked as revoked.  After another acme_account_safety_buffer has 
 | 
			
		||||
passed from the revocation or deactivation date, a revoked or 
 | 
			
		||||
deactivated ACME account is deleted.`,
 | 
			
		||||
		Default: false,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fields["safety_buffer"] = &framework.FieldSchema{
 | 
			
		||||
		Type: framework.TypeDurationSecond,
 | 
			
		||||
		Description: `The amount of extra time that must have passed
 | 
			
		||||
@@ -509,6 +526,14 @@ Defaults to 8760 hours (1 year).`,
 | 
			
		||||
		Default: int(defaultTidyConfig.IssuerSafetyBuffer / time.Second), // TypeDurationSecond currently requires defaults to be int
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fields["acme_account_safety_buffer"] = &framework.FieldSchema{
 | 
			
		||||
		Type: framework.TypeDurationSecond,
 | 
			
		||||
		Description: `The amount of time that must pass after creation
 | 
			
		||||
that an account with no orders is marked revoked, and the amount of time
 | 
			
		||||
after being marked revoked or dea`,
 | 
			
		||||
		Default: int(defaultTidyConfig.AcmeAccountSafetyBuffer / time.Second), // TypeDurationSecond currently requires defaults to be int
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fields["pause_duration"] = &framework.FieldSchema{
 | 
			
		||||
		Type: framework.TypeString,
 | 
			
		||||
		Description: `The amount of time to wait between processing
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/hashicorp/go-secure-stdlib/strutil"
 | 
			
		||||
 | 
			
		||||
@@ -236,6 +237,8 @@ func (b *backend) acmeNewAccountCreateHandler(acmeCtx *acmeContext, userCtx *jws
 | 
			
		||||
	//	return nil, fmt.Errorf("terms of service not agreed to: %w", ErrUserActionRequired)
 | 
			
		||||
	//}
 | 
			
		||||
 | 
			
		||||
	b.acmeAccountLock.RLock() // Prevents Account Creation and Tidy Interfering
 | 
			
		||||
	defer b.acmeAccountLock.RUnlock()
 | 
			
		||||
	accountByKid, err := b.acmeState.CreateAccount(acmeCtx, userCtx, contact, termsOfServiceAgreed)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("failed to create account: %w", err)
 | 
			
		||||
@@ -282,6 +285,7 @@ func (b *backend) acmeNewAccountUpdateHandler(acmeCtx *acmeContext, userCtx *jws
 | 
			
		||||
		// TODO: This should cancel any ongoing operations (do not revoke certs),
 | 
			
		||||
		//       perhaps we should delete this account here?
 | 
			
		||||
		account.Status = StatusDeactivated
 | 
			
		||||
		account.AccountRevokedDate = time.Now()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if shouldUpdate {
 | 
			
		||||
@@ -294,3 +298,90 @@ func (b *backend) acmeNewAccountUpdateHandler(acmeCtx *acmeContext, userCtx *jws
 | 
			
		||||
	resp := formatAccountResponse(acmeCtx, account)
 | 
			
		||||
	return resp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *backend) tidyAcmeAccountByThumbprint(as *acmeState, ac *acmeContext, keyThumbprint string, certTidyBuffer, accountTidyBuffer time.Duration) error {
 | 
			
		||||
	thumbprintEntry, err := ac.sc.Storage.Get(ac.sc.Context, acmeThumbprintPrefix+keyThumbprint)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("error retrieving thumbprint entry %v, unable to find corresponding account entry: %w", keyThumbprint, err)
 | 
			
		||||
	}
 | 
			
		||||
	if thumbprintEntry == nil {
 | 
			
		||||
		return fmt.Errorf("empty thumbprint entry %v, unable to find corresponding account entry", keyThumbprint)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var thumbprint acmeThumbprint
 | 
			
		||||
	err = thumbprintEntry.DecodeJSON(&thumbprint)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("unable to decode thumbprint entry %v to find account entry: %w", keyThumbprint, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(thumbprint.Kid) == 0 {
 | 
			
		||||
		return fmt.Errorf("unable to find account entry: empty kid within thumbprint entry: %s", keyThumbprint)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Now Get the Account:
 | 
			
		||||
	accountEntry, err := ac.sc.Storage.Get(ac.sc.Context, acmeAccountPrefix+thumbprint.Kid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if accountEntry == nil {
 | 
			
		||||
		// We delete the Thumbprint Associated with the Account, and we are done
 | 
			
		||||
		err = ac.sc.Storage.Delete(ac.sc.Context, acmeThumbprintPrefix+keyThumbprint)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		b.tidyStatusIncDeletedAcmeAccountCount()
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var account acmeAccount
 | 
			
		||||
	err = accountEntry.DecodeJSON(&account)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Tidy Orders On the Account
 | 
			
		||||
	orderIds, err := as.ListOrderIds(ac, thumbprint.Kid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	allOrdersTidied := true
 | 
			
		||||
	for _, orderId := range orderIds {
 | 
			
		||||
		wasTidied, err := b.acmeTidyOrder(ac, thumbprint.Kid, acmeAccountPrefix+thumbprint.Kid+"/orders/"+orderId, ac.sc, certTidyBuffer)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if !wasTidied {
 | 
			
		||||
			allOrdersTidied = false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if allOrdersTidied && time.Now().After(account.AccountCreatedDate.Add(accountTidyBuffer)) {
 | 
			
		||||
		// Tidy this account
 | 
			
		||||
		// If it is Revoked or Deactivated:
 | 
			
		||||
		if (account.Status == StatusRevoked || account.Status == StatusDeactivated) && time.Now().After(account.AccountRevokedDate.Add(accountTidyBuffer)) {
 | 
			
		||||
			// We Delete the Account Associated with this Thumbprint:
 | 
			
		||||
			err = ac.sc.Storage.Delete(ac.sc.Context, acmeAccountPrefix+thumbprint.Kid)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Now we delete the Thumbprint Associated with the Account:
 | 
			
		||||
			err = ac.sc.Storage.Delete(ac.sc.Context, acmeThumbprintPrefix+keyThumbprint)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			b.tidyStatusIncDeletedAcmeAccountCount()
 | 
			
		||||
		} else if account.Status == StatusValid {
 | 
			
		||||
			// Revoke This Account
 | 
			
		||||
			account.AccountRevokedDate = time.Now()
 | 
			
		||||
			account.Status = StatusRevoked
 | 
			
		||||
			err := as.UpdateAccount(ac, &account)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			b.tidyStatusIncRevAcmeAccountCount()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -884,3 +884,64 @@ func parseOrderIdentifiers(data map[string]interface{}) ([]*ACMEIdentifier, erro
 | 
			
		||||
 | 
			
		||||
	return identifiers, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *backend) acmeTidyOrder(ac *acmeContext, accountId string, orderPath string, sc *storageContext, certTidyBuffer time.Duration) (wasTidied bool, err error) {
 | 
			
		||||
	// First we get the order; note that the orderPath includes the account
 | 
			
		||||
	// It's only accessed at acme/orders/<order_id> with the account context
 | 
			
		||||
	// It's saved at acme/<account_id>/orders/<orderId>
 | 
			
		||||
	entry, err := ac.sc.Storage.Get(ac.sc.Context, orderPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, fmt.Errorf("error loading order: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	if entry == nil {
 | 
			
		||||
		return false, fmt.Errorf("order does not exist: %w", ErrMalformed)
 | 
			
		||||
	}
 | 
			
		||||
	var order acmeOrder
 | 
			
		||||
	err = entry.DecodeJSON(&order)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, fmt.Errorf("error decoding order: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Determine whether we should tidy this order
 | 
			
		||||
	shouldTidy := false
 | 
			
		||||
	// It is faster to check certificate information on the order entry rather than fetch the cert entry to parse:
 | 
			
		||||
	if !order.CertificateExpiry.IsZero() {
 | 
			
		||||
		// This implies that a certificate exists
 | 
			
		||||
		// When a certificate exists, we want to expire and tidy the order when we tidy the certificate:
 | 
			
		||||
		if time.Now().After(order.CertificateExpiry.Add(certTidyBuffer)) { // It's time to clean
 | 
			
		||||
			shouldTidy = true
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		// This implies that no certificate exists
 | 
			
		||||
		// In this case, we want to expire the order after it has expired (+ some safety buffer)
 | 
			
		||||
		if time.Now().After(order.Expires) {
 | 
			
		||||
			shouldTidy = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if shouldTidy == false {
 | 
			
		||||
		return shouldTidy, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Tidy this Order
 | 
			
		||||
	// That includes any certificate acme/<account_id>/orders/orderPath/cert
 | 
			
		||||
	// That also includes any related authorizations: acme/<account_id>/authorizations/<auth_id>
 | 
			
		||||
 | 
			
		||||
	// First Authorizations
 | 
			
		||||
	for _, authorizationId := range order.AuthorizationIds {
 | 
			
		||||
		err = ac.sc.Storage.Delete(ac.sc.Context, getAuthorizationPath(accountId, authorizationId))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return false, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Normal Tidy will Take Care of the Certificate
 | 
			
		||||
 | 
			
		||||
	// And Finally, the order:
 | 
			
		||||
	err = ac.sc.Storage.Delete(ac.sc.Context, orderPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	b.tidyStatusIncDelAcmeOrderCount()
 | 
			
		||||
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -38,6 +38,7 @@ type tidyStatus struct {
 | 
			
		||||
	safetyBuffer            int
 | 
			
		||||
	issuerSafetyBuffer      int
 | 
			
		||||
	revQueueSafetyBuffer    int
 | 
			
		||||
	acmeAccountSafetyBuffer int
 | 
			
		||||
 | 
			
		||||
	tidyCertStore         bool
 | 
			
		||||
	tidyRevokedCerts      bool
 | 
			
		||||
@@ -46,6 +47,7 @@ type tidyStatus struct {
 | 
			
		||||
	tidyBackupBundle      bool
 | 
			
		||||
	tidyRevocationQueue   bool
 | 
			
		||||
	tidyCrossRevokedCerts bool
 | 
			
		||||
	tidyAcme              bool
 | 
			
		||||
	pauseDuration         string
 | 
			
		||||
 | 
			
		||||
	// Status
 | 
			
		||||
@@ -62,6 +64,11 @@ type tidyStatus struct {
 | 
			
		||||
	missingIssuerCertCount   uint
 | 
			
		||||
	revQueueDeletedCount     uint
 | 
			
		||||
	crossRevokedDeletedCount uint
 | 
			
		||||
 | 
			
		||||
	acmeAccountsCount        uint
 | 
			
		||||
	acmeAccountsRevokedCount uint
 | 
			
		||||
	acmeAccountsDeletedCount uint
 | 
			
		||||
	acmeOrdersDeletedCount   uint
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type tidyConfig struct {
 | 
			
		||||
@@ -72,8 +79,10 @@ type tidyConfig struct {
 | 
			
		||||
	IssuerAssocs            bool          `json:"tidy_revoked_cert_issuer_associations"`
 | 
			
		||||
	ExpiredIssuers          bool          `json:"tidy_expired_issuers"`
 | 
			
		||||
	BackupBundle            bool          `json:"tidy_move_legacy_ca_bundle"`
 | 
			
		||||
	TidyAcme                bool          `json:"tidy_acme"`
 | 
			
		||||
	SafetyBuffer            time.Duration `json:"safety_buffer"`
 | 
			
		||||
	IssuerSafetyBuffer      time.Duration `json:"issuer_safety_buffer"`
 | 
			
		||||
	AcmeAccountSafetyBuffer time.Duration `json:"acme_account_safety_buffer"`
 | 
			
		||||
	PauseDuration           time.Duration `json:"pause_duration"`
 | 
			
		||||
	MaintainCount           bool          `json:"maintain_stored_certificate_counts"`
 | 
			
		||||
	PublishMetrics          bool          `json:"publish_stored_certificate_count_metrics"`
 | 
			
		||||
@@ -90,8 +99,10 @@ var defaultTidyConfig = tidyConfig{
 | 
			
		||||
	IssuerAssocs:            false,
 | 
			
		||||
	ExpiredIssuers:          false,
 | 
			
		||||
	BackupBundle:            false,
 | 
			
		||||
	TidyAcme:                false,
 | 
			
		||||
	SafetyBuffer:            72 * time.Hour,
 | 
			
		||||
	IssuerSafetyBuffer:      365 * 24 * time.Hour,
 | 
			
		||||
	AcmeAccountSafetyBuffer: 30 * 24 * time.Hour,
 | 
			
		||||
	PauseDuration:           0 * time.Second,
 | 
			
		||||
	MaintainCount:           false,
 | 
			
		||||
	PublishMetrics:          false,
 | 
			
		||||
@@ -174,6 +185,16 @@ func pathTidyCancel(b *backend) *framework.Path {
 | 
			
		||||
								Description: `Tidy revoked certificate issuer associations`,
 | 
			
		||||
								Required:    false,
 | 
			
		||||
							},
 | 
			
		||||
							"tidy_acme": {
 | 
			
		||||
								Type:        framework.TypeBool,
 | 
			
		||||
								Description: `Tidy Unused Acme Accounts, and Orders`,
 | 
			
		||||
								Required:    false,
 | 
			
		||||
							},
 | 
			
		||||
							"acme_account_safety_buffer": {
 | 
			
		||||
								Type:        framework.TypeInt,
 | 
			
		||||
								Description: `Safety buffer after creation after which accounts lacking orders are revoked`,
 | 
			
		||||
								Required:    false,
 | 
			
		||||
							},
 | 
			
		||||
							"tidy_expired_issuers": {
 | 
			
		||||
								Type:        framework.TypeBool,
 | 
			
		||||
								Description: `Tidy expired issuers`,
 | 
			
		||||
@@ -262,6 +283,26 @@ func pathTidyCancel(b *backend) *framework.Path {
 | 
			
		||||
								Type:     framework.TypeString,
 | 
			
		||||
								Required: false,
 | 
			
		||||
							},
 | 
			
		||||
							"total_acme_account_count": {
 | 
			
		||||
								Type:        framework.TypeInt,
 | 
			
		||||
								Description: `Total number of acme accounts iterated over`,
 | 
			
		||||
								Required:    false,
 | 
			
		||||
							},
 | 
			
		||||
							"acme_account_deleted_count": {
 | 
			
		||||
								Type:        framework.TypeInt,
 | 
			
		||||
								Description: `The number of revoked acme accounts removed`,
 | 
			
		||||
								Required:    false,
 | 
			
		||||
							},
 | 
			
		||||
							"acme_account_revoked_count": {
 | 
			
		||||
								Type:        framework.TypeInt,
 | 
			
		||||
								Description: `The number of unused acme accounts revoked`,
 | 
			
		||||
								Required:    false,
 | 
			
		||||
							},
 | 
			
		||||
							"acme_orders_deleted_count": {
 | 
			
		||||
								Type:        framework.TypeInt,
 | 
			
		||||
								Description: `The number of expired, unused acme orders removed`,
 | 
			
		||||
								Required:    false,
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
@@ -305,6 +346,11 @@ func pathTidyStatus(b *backend) *framework.Path {
 | 
			
		||||
								Description: `Revocation queue safety buffer`,
 | 
			
		||||
								Required:    true,
 | 
			
		||||
							},
 | 
			
		||||
							"acme_account_safety_buffer": {
 | 
			
		||||
								Type:        framework.TypeInt,
 | 
			
		||||
								Description: `Safety buffer after creation after which accounts lacking orders are revoked`,
 | 
			
		||||
								Required:    false,
 | 
			
		||||
							},
 | 
			
		||||
							"tidy_cert_store": {
 | 
			
		||||
								Type:        framework.TypeBool,
 | 
			
		||||
								Description: `Tidy certificate store`,
 | 
			
		||||
@@ -330,6 +376,11 @@ func pathTidyStatus(b *backend) *framework.Path {
 | 
			
		||||
								Description: ``,
 | 
			
		||||
								Required:    false,
 | 
			
		||||
							},
 | 
			
		||||
							"tidy_acme": {
 | 
			
		||||
								Type:        framework.TypeBool,
 | 
			
		||||
								Description: `Tidy Unused Acme Accounts, and Orders`,
 | 
			
		||||
								Required:    true,
 | 
			
		||||
							},
 | 
			
		||||
							"pause_duration": {
 | 
			
		||||
								Type:        framework.TypeString,
 | 
			
		||||
								Description: `Duration to pause between tidying certificates`,
 | 
			
		||||
@@ -410,6 +461,26 @@ func pathTidyStatus(b *backend) *framework.Path {
 | 
			
		||||
								Type:     framework.TypeString,
 | 
			
		||||
								Required: true,
 | 
			
		||||
							},
 | 
			
		||||
							"total_acme_account_count": {
 | 
			
		||||
								Type:        framework.TypeInt,
 | 
			
		||||
								Description: `Total number of acme accounts iterated over`,
 | 
			
		||||
								Required:    false,
 | 
			
		||||
							},
 | 
			
		||||
							"acme_account_deleted_count": {
 | 
			
		||||
								Type:        framework.TypeInt,
 | 
			
		||||
								Description: `The number of revoked acme accounts removed`,
 | 
			
		||||
								Required:    false,
 | 
			
		||||
							},
 | 
			
		||||
							"acme_account_revoked_count": {
 | 
			
		||||
								Type:        framework.TypeInt,
 | 
			
		||||
								Description: `The number of unused acme accounts revoked`,
 | 
			
		||||
								Required:    false,
 | 
			
		||||
							},
 | 
			
		||||
							"acme_orders_deleted_count": {
 | 
			
		||||
								Type:        framework.TypeInt,
 | 
			
		||||
								Description: `The number of expired, unused acme orders removed`,
 | 
			
		||||
								Required:    false,
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
@@ -478,6 +549,11 @@ func pathConfigAutoTidy(b *backend) *framework.Path {
 | 
			
		||||
								Description: `Specifies whether tidy expired issuers`,
 | 
			
		||||
								Required:    true,
 | 
			
		||||
							},
 | 
			
		||||
							"tidy_acme": {
 | 
			
		||||
								Type:        framework.TypeBool,
 | 
			
		||||
								Description: `Tidy Unused Acme Accounts, and Orders`,
 | 
			
		||||
								Required:    true,
 | 
			
		||||
							},
 | 
			
		||||
							"safety_buffer": {
 | 
			
		||||
								Type:        framework.TypeInt,
 | 
			
		||||
								Description: `Safety buffer time duration`,
 | 
			
		||||
@@ -488,6 +564,11 @@ func pathConfigAutoTidy(b *backend) *framework.Path {
 | 
			
		||||
								Description: `Issuer safety buffer`,
 | 
			
		||||
								Required:    true,
 | 
			
		||||
							},
 | 
			
		||||
							"acme_account_safety_buffer": {
 | 
			
		||||
								Type:        framework.TypeInt,
 | 
			
		||||
								Description: `Safety buffer after creation after which accounts lacking orders are revoked`,
 | 
			
		||||
								Required:    false,
 | 
			
		||||
							},
 | 
			
		||||
							"pause_duration": {
 | 
			
		||||
								Type:        framework.TypeString,
 | 
			
		||||
								Description: `Duration to pause between tidying certificates`,
 | 
			
		||||
@@ -561,6 +642,11 @@ func pathConfigAutoTidy(b *backend) *framework.Path {
 | 
			
		||||
								Description: `Specifies whether tidy expired issuers`,
 | 
			
		||||
								Required:    true,
 | 
			
		||||
							},
 | 
			
		||||
							"tidy_acme": {
 | 
			
		||||
								Type:        framework.TypeBool,
 | 
			
		||||
								Description: `Tidy Unused Acme Accounts, and Orders`,
 | 
			
		||||
								Required:    true,
 | 
			
		||||
							},
 | 
			
		||||
							"safety_buffer": {
 | 
			
		||||
								Type:        framework.TypeInt,
 | 
			
		||||
								Description: `Safety buffer time duration`,
 | 
			
		||||
@@ -571,6 +657,11 @@ func pathConfigAutoTidy(b *backend) *framework.Path {
 | 
			
		||||
								Description: `Issuer safety buffer`,
 | 
			
		||||
								Required:    true,
 | 
			
		||||
							},
 | 
			
		||||
							"acme_account_safety_buffer": {
 | 
			
		||||
								Type:        framework.TypeInt,
 | 
			
		||||
								Description: `Safety buffer after creation after which accounts lacking orders are revoked`,
 | 
			
		||||
								Required:    true,
 | 
			
		||||
							},
 | 
			
		||||
							"pause_duration": {
 | 
			
		||||
								Type:        framework.TypeString,
 | 
			
		||||
								Description: `Duration to pause between tidying certificates`,
 | 
			
		||||
@@ -592,6 +683,14 @@ func pathConfigAutoTidy(b *backend) *framework.Path {
 | 
			
		||||
								Type:     framework.TypeDurationSecond,
 | 
			
		||||
								Required: true,
 | 
			
		||||
							},
 | 
			
		||||
							"publish_stored_certificate_count_metrics": {
 | 
			
		||||
								Type:     framework.TypeBool,
 | 
			
		||||
								Required: true,
 | 
			
		||||
							},
 | 
			
		||||
							"maintain_stored_certificate_counts": {
 | 
			
		||||
								Type:     framework.TypeBool,
 | 
			
		||||
								Required: true,
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					}},
 | 
			
		||||
				},
 | 
			
		||||
@@ -618,6 +717,8 @@ func (b *backend) pathTidyWrite(ctx context.Context, req *logical.Request, d *fr
 | 
			
		||||
	tidyRevocationQueue := d.Get("tidy_revocation_queue").(bool)
 | 
			
		||||
	queueSafetyBuffer := d.Get("revocation_queue_safety_buffer").(int)
 | 
			
		||||
	tidyCrossRevokedCerts := d.Get("tidy_cross_cluster_revoked_certs").(bool)
 | 
			
		||||
	tidyAcme := d.Get("tidy_acme").(bool)
 | 
			
		||||
	acmeAccountSafetyBuffer := d.Get("acme_account_safety_buffer").(int)
 | 
			
		||||
 | 
			
		||||
	if safetyBuffer < 1 {
 | 
			
		||||
		return logical.ErrorResponse("safety_buffer must be greater than zero"), nil
 | 
			
		||||
@@ -631,6 +732,10 @@ func (b *backend) pathTidyWrite(ctx context.Context, req *logical.Request, d *fr
 | 
			
		||||
		return logical.ErrorResponse("revocation_queue_safety_buffer must be greater than zero"), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if acmeAccountSafetyBuffer < 1 {
 | 
			
		||||
		return logical.ErrorResponse("acme_account_safety_buffer must be greater than zero"), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if pauseDurationStr != "" {
 | 
			
		||||
		var err error
 | 
			
		||||
		pauseDuration, err = time.ParseDuration(pauseDurationStr)
 | 
			
		||||
@@ -646,6 +751,7 @@ func (b *backend) pathTidyWrite(ctx context.Context, req *logical.Request, d *fr
 | 
			
		||||
	bufferDuration := time.Duration(safetyBuffer) * time.Second
 | 
			
		||||
	issuerBufferDuration := time.Duration(issuerSafetyBuffer) * time.Second
 | 
			
		||||
	queueSafetyBufferDuration := time.Duration(queueSafetyBuffer) * time.Second
 | 
			
		||||
	acmeAccountSafetyBufferDuration := time.Duration(acmeAccountSafetyBuffer) * time.Second
 | 
			
		||||
 | 
			
		||||
	// Manual run with constructed configuration.
 | 
			
		||||
	config := &tidyConfig{
 | 
			
		||||
@@ -662,6 +768,8 @@ func (b *backend) pathTidyWrite(ctx context.Context, req *logical.Request, d *fr
 | 
			
		||||
		RevocationQueue:         tidyRevocationQueue,
 | 
			
		||||
		QueueSafetyBuffer:       queueSafetyBufferDuration,
 | 
			
		||||
		CrossRevokedCerts:       tidyCrossRevokedCerts,
 | 
			
		||||
		TidyAcme:                tidyAcme,
 | 
			
		||||
		AcmeAccountSafetyBuffer: acmeAccountSafetyBufferDuration,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !atomic.CompareAndSwapUint32(b.tidyCASGuard, 0, 1) {
 | 
			
		||||
@@ -777,6 +885,17 @@ func (b *backend) startTidyOperation(req *logical.Request, config *tidyConfig) {
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Check for cancel before continuing.
 | 
			
		||||
			if atomic.CompareAndSwapUint32(b.tidyCancelCAS, 1, 0) {
 | 
			
		||||
				return tidyCancelledError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if config.TidyAcme {
 | 
			
		||||
				if err := b.doTidyAcme(ctx, req, logger, config); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -1381,6 +1500,53 @@ func (b *backend) doTidyCrossRevocationStore(ctx context.Context, req *logical.R
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *backend) doTidyAcme(ctx context.Context, req *logical.Request, logger hclog.Logger, config *tidyConfig) error {
 | 
			
		||||
	b.acmeAccountLock.Lock()
 | 
			
		||||
	defer b.acmeAccountLock.Unlock()
 | 
			
		||||
 | 
			
		||||
	sc := b.makeStorageContext(ctx, req.Storage)
 | 
			
		||||
	list, err := sc.Storage.List(ctx, acmeThumbprintPrefix)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b.tidyStatusLock.Lock()
 | 
			
		||||
	b.tidyStatus.acmeAccountsCount = uint(len(list))
 | 
			
		||||
	b.tidyStatusLock.Unlock()
 | 
			
		||||
 | 
			
		||||
	baseUrl, _, err := getAcmeBaseUrl(sc, req.Path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	acmeCtx := &acmeContext{
 | 
			
		||||
		baseUrl: baseUrl,
 | 
			
		||||
		sc:      sc,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, thumbprint := range list {
 | 
			
		||||
		err := b.tidyAcmeAccountByThumbprint(b.acmeState, acmeCtx, thumbprint, config.SafetyBuffer, config.AcmeAccountSafetyBuffer)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logger.Warn("error tidying account %v: %v", thumbprint, err.Error())
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Check for cancel before continuing.
 | 
			
		||||
		if atomic.CompareAndSwapUint32(b.tidyCancelCAS, 1, 0) {
 | 
			
		||||
			return tidyCancelledError
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Check for pause duration to reduce resource consumption.
 | 
			
		||||
		if config.PauseDuration > (0 * time.Second) {
 | 
			
		||||
			b.acmeAccountLock.Unlock() // Correct the Lock
 | 
			
		||||
			time.Sleep(config.PauseDuration)
 | 
			
		||||
			b.acmeAccountLock.Lock()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *backend) pathTidyCancelWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
 | 
			
		||||
	if atomic.LoadUint32(b.tidyCASGuard) == 0 {
 | 
			
		||||
		resp := &logical.Response{}
 | 
			
		||||
@@ -1419,6 +1585,7 @@ func (b *backend) pathTidyStatusRead(_ context.Context, _ *logical.Request, _ *f
 | 
			
		||||
			"tidy_move_legacy_ca_bundle":            nil,
 | 
			
		||||
			"tidy_revocation_queue":                 nil,
 | 
			
		||||
			"tidy_cross_cluster_revoked_certs":      nil,
 | 
			
		||||
			"tidy_acme":                             nil,
 | 
			
		||||
			"pause_duration":                        nil,
 | 
			
		||||
			"state":                                 "Inactive",
 | 
			
		||||
			"error":                                 nil,
 | 
			
		||||
@@ -1433,6 +1600,11 @@ func (b *backend) pathTidyStatusRead(_ context.Context, _ *logical.Request, _ *f
 | 
			
		||||
			"internal_backend_uuid":                 nil,
 | 
			
		||||
			"revocation_queue_deleted_count":        nil,
 | 
			
		||||
			"cross_revoked_cert_deleted_count":      nil,
 | 
			
		||||
			"total_acme_account_count":              nil,
 | 
			
		||||
			"acme_account_deleted_count":            nil,
 | 
			
		||||
			"acme_account_revoked_count":            nil,
 | 
			
		||||
			"acme_orders_deleted_count":             nil,
 | 
			
		||||
			"acme_account_safety_buffer":            nil,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1463,6 +1635,7 @@ func (b *backend) pathTidyStatusRead(_ context.Context, _ *logical.Request, _ *f
 | 
			
		||||
	resp.Data["tidy_move_legacy_ca_bundle"] = b.tidyStatus.tidyBackupBundle
 | 
			
		||||
	resp.Data["tidy_revocation_queue"] = b.tidyStatus.tidyRevocationQueue
 | 
			
		||||
	resp.Data["tidy_cross_cluster_revoked_certs"] = b.tidyStatus.tidyCrossRevokedCerts
 | 
			
		||||
	resp.Data["tidy_acme"] = b.tidyStatus.tidyAcme
 | 
			
		||||
	resp.Data["pause_duration"] = b.tidyStatus.pauseDuration
 | 
			
		||||
	resp.Data["time_started"] = b.tidyStatus.timeStarted
 | 
			
		||||
	resp.Data["message"] = b.tidyStatus.message
 | 
			
		||||
@@ -1473,6 +1646,11 @@ func (b *backend) pathTidyStatusRead(_ context.Context, _ *logical.Request, _ *f
 | 
			
		||||
	resp.Data["cross_revoked_cert_deleted_count"] = b.tidyStatus.crossRevokedDeletedCount
 | 
			
		||||
	resp.Data["revocation_queue_safety_buffer"] = b.tidyStatus.revQueueSafetyBuffer
 | 
			
		||||
	resp.Data["last_auto_tidy_finished"] = b.lastTidy
 | 
			
		||||
	resp.Data["total_acme_account_count"] = b.tidyStatus.acmeAccountsCount
 | 
			
		||||
	resp.Data["acme_account_deleted_count"] = b.tidyStatus.acmeAccountsDeletedCount
 | 
			
		||||
	resp.Data["acme_account_revoked_count"] = b.tidyStatus.acmeAccountsRevokedCount
 | 
			
		||||
	resp.Data["acme_orders_deleted_count"] = b.tidyStatus.acmeOrdersDeletedCount
 | 
			
		||||
	resp.Data["acme_account_safety_buffer"] = b.tidyStatus.acmeAccountSafetyBuffer
 | 
			
		||||
 | 
			
		||||
	switch b.tidyStatus.state {
 | 
			
		||||
	case tidyStatusStarted:
 | 
			
		||||
@@ -1505,23 +1683,7 @@ func (b *backend) pathConfigAutoTidyRead(ctx context.Context, req *logical.Reque
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &logical.Response{
 | 
			
		||||
		Data: map[string]interface{}{
 | 
			
		||||
			"enabled":                                  config.Enabled,
 | 
			
		||||
			"interval_duration":                        int(config.Interval / time.Second),
 | 
			
		||||
			"tidy_cert_store":                          config.CertStore,
 | 
			
		||||
			"tidy_revoked_certs":                       config.RevokedCerts,
 | 
			
		||||
			"tidy_revoked_cert_issuer_associations":    config.IssuerAssocs,
 | 
			
		||||
			"tidy_expired_issuers":                     config.ExpiredIssuers,
 | 
			
		||||
			"safety_buffer":                            int(config.SafetyBuffer / time.Second),
 | 
			
		||||
			"issuer_safety_buffer":                     int(config.IssuerSafetyBuffer / time.Second),
 | 
			
		||||
			"pause_duration":                           config.PauseDuration.String(),
 | 
			
		||||
			"publish_stored_certificate_count_metrics": config.PublishMetrics,
 | 
			
		||||
			"maintain_stored_certificate_counts":       config.MaintainCount,
 | 
			
		||||
			"tidy_move_legacy_ca_bundle":               config.BackupBundle,
 | 
			
		||||
			"tidy_revocation_queue":                    config.RevocationQueue,
 | 
			
		||||
			"revocation_queue_safety_buffer":           int(config.QueueSafetyBuffer / time.Second),
 | 
			
		||||
			"tidy_cross_cluster_revoked_certs":         config.CrossRevokedCerts,
 | 
			
		||||
		},
 | 
			
		||||
		Data: getTidyConfigData(*config),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1623,21 +1785,7 @@ func (b *backend) pathConfigAutoTidyWrite(ctx context.Context, req *logical.Requ
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &logical.Response{
 | 
			
		||||
		Data: map[string]interface{}{
 | 
			
		||||
			"enabled":                               config.Enabled,
 | 
			
		||||
			"interval_duration":                     int(config.Interval / time.Second),
 | 
			
		||||
			"tidy_cert_store":                       config.CertStore,
 | 
			
		||||
			"tidy_revoked_certs":                    config.RevokedCerts,
 | 
			
		||||
			"tidy_revoked_cert_issuer_associations": config.IssuerAssocs,
 | 
			
		||||
			"tidy_expired_issuers":                  config.ExpiredIssuers,
 | 
			
		||||
			"tidy_move_legacy_ca_bundle":            config.BackupBundle,
 | 
			
		||||
			"safety_buffer":                         int(config.SafetyBuffer / time.Second),
 | 
			
		||||
			"issuer_safety_buffer":                  int(config.IssuerSafetyBuffer / time.Second),
 | 
			
		||||
			"pause_duration":                        config.PauseDuration.String(),
 | 
			
		||||
			"tidy_revocation_queue":                 config.RevocationQueue,
 | 
			
		||||
			"revocation_queue_safety_buffer":        int(config.QueueSafetyBuffer / time.Second),
 | 
			
		||||
			"tidy_cross_cluster_revoked_certs":      config.CrossRevokedCerts,
 | 
			
		||||
		},
 | 
			
		||||
		Data: getTidyConfigData(*config),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1649,6 +1797,7 @@ func (b *backend) tidyStatusStart(config *tidyConfig) {
 | 
			
		||||
		safetyBuffer:            int(config.SafetyBuffer / time.Second),
 | 
			
		||||
		issuerSafetyBuffer:      int(config.IssuerSafetyBuffer / time.Second),
 | 
			
		||||
		revQueueSafetyBuffer:    int(config.QueueSafetyBuffer / time.Second),
 | 
			
		||||
		acmeAccountSafetyBuffer: int(config.AcmeAccountSafetyBuffer / time.Second),
 | 
			
		||||
		tidyCertStore:           config.CertStore,
 | 
			
		||||
		tidyRevokedCerts:        config.RevokedCerts,
 | 
			
		||||
		tidyRevokedAssocs:       config.IssuerAssocs,
 | 
			
		||||
@@ -1656,6 +1805,7 @@ func (b *backend) tidyStatusStart(config *tidyConfig) {
 | 
			
		||||
		tidyBackupBundle:        config.BackupBundle,
 | 
			
		||||
		tidyRevocationQueue:     config.RevocationQueue,
 | 
			
		||||
		tidyCrossRevokedCerts:   config.CrossRevokedCerts,
 | 
			
		||||
		tidyAcme:                config.TidyAcme,
 | 
			
		||||
		pauseDuration:           config.PauseDuration.String(),
 | 
			
		||||
 | 
			
		||||
		state:       tidyStatusStarted,
 | 
			
		||||
@@ -1737,6 +1887,27 @@ func (b *backend) tidyStatusIncCrossRevCertCount() {
 | 
			
		||||
	b.tidyStatus.crossRevokedDeletedCount++
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *backend) tidyStatusIncRevAcmeAccountCount() {
 | 
			
		||||
	b.tidyStatusLock.Lock()
 | 
			
		||||
	defer b.tidyStatusLock.Unlock()
 | 
			
		||||
 | 
			
		||||
	b.tidyStatus.acmeAccountsRevokedCount++
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *backend) tidyStatusIncDeletedAcmeAccountCount() {
 | 
			
		||||
	b.tidyStatusLock.Lock()
 | 
			
		||||
	defer b.tidyStatusLock.Unlock()
 | 
			
		||||
 | 
			
		||||
	b.tidyStatus.acmeAccountsDeletedCount++
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *backend) tidyStatusIncDelAcmeOrderCount() {
 | 
			
		||||
	b.tidyStatusLock.Lock()
 | 
			
		||||
	defer b.tidyStatusLock.Unlock()
 | 
			
		||||
 | 
			
		||||
	b.tidyStatus.acmeOrdersDeletedCount++
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const pathTidyHelpSyn = `
 | 
			
		||||
Tidy up the backend by removing expired certificates, revocation information,
 | 
			
		||||
or both.
 | 
			
		||||
@@ -1806,6 +1977,12 @@ The result includes the following fields:
 | 
			
		||||
* 'tidy_cross_cluster_revoked_certs': the value of this parameter when initiating the tidy operation
 | 
			
		||||
* 'cross_revoked_cert_deleted_count': the number of cross-cluster revoked certificate entries deleted
 | 
			
		||||
* 'revocation_queue_safety_buffer': the value of this parameter when initiating the tidy operation
 | 
			
		||||
* 'tidy_acme': the value of this parameter when initiating the tidy operation
 | 
			
		||||
* 'acme_account_safety_buffer': the value of this parameter when initiating the tidy operation
 | 
			
		||||
* 'total_acme_account_count': the total number of acme accounts in the list to be iterated over
 | 
			
		||||
* 'acme_account_deleted_count': the number of revoked acme accounts deleted during the operation
 | 
			
		||||
* 'acme_account_revoked_count': the number of acme accounts revoked during the operation
 | 
			
		||||
* 'acme_orders_deleted_count': the number of acme orders deleted during the operation
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
const pathConfigAutoTidySyn = `
 | 
			
		||||
@@ -1821,3 +1998,26 @@ controls the frequency of auto-tidy execution).
 | 
			
		||||
Once enabled, a tidy operation will be kicked off automatically, as if it
 | 
			
		||||
were executed with the posted configuration.
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
func getTidyConfigData(config tidyConfig) map[string]interface{} {
 | 
			
		||||
	return map[string]interface{}{
 | 
			
		||||
		// This map is in the same order as tidyConfig to ensure that all fields are accounted for
 | 
			
		||||
		"enabled":                                  config.Enabled,
 | 
			
		||||
		"interval_duration":                        int(config.Interval / time.Second),
 | 
			
		||||
		"tidy_cert_store":                          config.CertStore,
 | 
			
		||||
		"tidy_revoked_certs":                       config.RevokedCerts,
 | 
			
		||||
		"tidy_revoked_cert_issuer_associations":    config.IssuerAssocs,
 | 
			
		||||
		"tidy_expired_issuers":                     config.ExpiredIssuers,
 | 
			
		||||
		"tidy_move_legacy_ca_bundle":               config.BackupBundle,
 | 
			
		||||
		"tidy_acme":                                config.TidyAcme,
 | 
			
		||||
		"safety_buffer":                            int(config.SafetyBuffer / time.Second),
 | 
			
		||||
		"issuer_safety_buffer":                     int(config.IssuerSafetyBuffer / time.Second),
 | 
			
		||||
		"acme_account_safety_buffer":               int(config.AcmeAccountSafetyBuffer / time.Second),
 | 
			
		||||
		"pause_duration":                           config.PauseDuration.String(),
 | 
			
		||||
		"publish_stored_certificate_count_metrics": config.PublishMetrics,
 | 
			
		||||
		"maintain_stored_certificate_counts":       config.MaintainCount,
 | 
			
		||||
		"tidy_revocation_queue":                    config.RevocationQueue,
 | 
			
		||||
		"revocation_queue_safety_buffer":           int(config.QueueSafetyBuffer / time.Second),
 | 
			
		||||
		"tidy_cross_cluster_revoked_certs":         config.CrossRevokedCerts,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -408,6 +408,7 @@ func TestTidyIssuerConfig(t *testing.T) {
 | 
			
		||||
	defaultConfigMap["safety_buffer"] = int(time.Duration(defaultConfigMap["safety_buffer"].(float64)) / time.Second)
 | 
			
		||||
	defaultConfigMap["pause_duration"] = time.Duration(defaultConfigMap["pause_duration"].(float64)).String()
 | 
			
		||||
	defaultConfigMap["revocation_queue_safety_buffer"] = int(time.Duration(defaultConfigMap["revocation_queue_safety_buffer"].(float64)) / time.Second)
 | 
			
		||||
	defaultConfigMap["acme_account_safety_buffer"] = int(time.Duration(defaultConfigMap["acme_account_safety_buffer"].(float64)) / time.Second)
 | 
			
		||||
 | 
			
		||||
	require.Equal(t, defaultConfigMap, resp.Data)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user