// features provides the Config struct, which is used to define feature flags // that can affect behavior across Boulder components. It also maintains a // global singleton Config which can be referenced by arbitrary Boulder code // without having to pass a collection of feature flags through the function // call graph. package features import ( "sync" ) // Config contains one boolean field for every Boulder feature flag. It can be // included directly in an executable's Config struct to have feature flags be // automatically parsed by the json config loader; executables that do so must // then call features.Set(parsedConfig) to load the parsed struct into this // package's global Config. type Config struct { // Deprecated features. These features have no effect. Removing them from // configuration is safe. // // Once all references to them have been removed from deployed configuration, // they can be deleted from this struct, after which Boulder will fail to // start if they are present in configuration. CAAAfterValidation bool AllowNoCommonName bool SHA256SubjectKeyIdentifier bool EnforceMultiVA bool MultiVAFullResults bool CertCheckerRequiresCorrespondence bool ECDSAForAll bool // ServeRenewalInfo exposes the renewalInfo endpoint in the directory and for // GET requests. WARNING: This feature is a draft and highly unstable. ServeRenewalInfo bool // ExpirationMailerUsesJoin enables using a JOIN query in expiration-mailer // rather than a SELECT from certificateStatus followed by thousands of // one-row SELECTs from certificates. ExpirationMailerUsesJoin bool // CertCheckerChecksValidations enables an extra query for each certificate // checked, to find the relevant authzs. Since this query might be // expensive, we gate it behind a feature flag. CertCheckerChecksValidations bool // CertCheckerRequiresValidations causes cert-checker to fail if the // query enabled by CertCheckerChecksValidations didn't find corresponding // authorizations. CertCheckerRequiresValidations bool // AsyncFinalize enables the RA to return approximately immediately from // requests to finalize orders. This allows us to take longer getting SCTs, // issuing certs, and updating the database; it indirectly reduces the number // of issuances that fail due to timeouts during storage. However, it also // requires clients to properly implement polling the Order object to wait // for the cert URL to appear. AsyncFinalize bool // DOH enables DNS-over-HTTPS queries for validation DOH bool // EnforceMultiCAA causes the VA to kick off remote CAA rechecks when true. // When false, no remote CAA rechecks will be performed. The primary VA will // make a valid/invalid decision with the results. The primary VA will // return an early decision if MultiCAAFullResults is false. EnforceMultiCAA bool // MultiCAAFullResults will cause the main VA to block and wait for all of // the remote VA CAA recheck results instead of returning early if the // number of failures is greater than the configured // maxRemoteValidationFailures. Only used when EnforceMultiCAA is true. MultiCAAFullResults bool // TrackReplacementCertificatesARI, when enabled, triggers the following // behavior: // - SA.NewOrderAndAuthzs: upon receiving a NewOrderRequest with a // 'replacesSerial' value, will create a new entry in the 'replacement // Orders' table. This will occur inside of the new order transaction. // - SA.FinalizeOrder will update the 'replaced' column of any row with // a 'orderID' matching the finalized order to true. This will occur // inside of the finalize (order) transaction. TrackReplacementCertificatesARI bool // MultipleCertificateProfiles, when enabled, triggers the following // behavior: // - SA.NewOrderAndAuthzs: upon receiving a NewOrderRequest with a // `certificateProfileName` value, will add that value to the database's // `orders.certificateProfileName` column. Values in this column are // allowed to be empty. MultipleCertificateProfiles bool // CheckRenewalExemptionAtWFE when enabled, triggers the following behavior: // - WFE.NewOrder: checks if the order is a renewal and if so skips checks // for NewOrdersPerAccount and NewOrdersPerDomain limits. // - RA.NewOrderAndAuthzs: skips checks for legacy NewOrdersPerAccount and // NewOrdersPerDomain limits if the WFE indicates that the order is a // renewal. // // TODO(#7511): Remove this feature flag. CheckRenewalExemptionAtWFE bool // CheckIdentifiersPaused checks if any of the identifiers in the order are // currently paused at NewOrder time. If any are paused, an error is // returned to the Subscriber indicating that the order cannot be processed // until the paused identifiers are unpaused and the order is resubmitted. CheckIdentifiersPaused bool // UseKvLimitsForNewOrder when enabled, causes the key-value rate limiter to // be the authoritative source of rate limiting information for new-order // callers and disables the legacy rate limiting checks. // // Note: this flag does not disable writes to the certificatesPerName or // fqdnSets tables at Finalize time. UseKvLimitsForNewOrder bool // UseKvLimitsForNewAccount when enabled, causes the key-value rate limiter // to be the authoritative source of rate limiting information for // new-account callers and disables the legacy rate limiting checks. UseKvLimitsForNewAccount bool } var fMu = new(sync.RWMutex) var global = Config{} // Set changes the global FeatureSet to match the input FeatureSet. This // overrides any previous changes made to the global FeatureSet. // // When used in tests, the caller must defer features.Reset() to avoid leaving // dirty global state. func Set(fs Config) { fMu.Lock() defer fMu.Unlock() // If the FeatureSet type ever changes, this must be updated to still copy // the input argument, never hold a reference to it. global = fs } // Reset resets all features to their initial state (false). func Reset() { fMu.Lock() defer fMu.Unlock() global = Config{} } // Get returns a copy of the current global FeatureSet, indicating which // features are currently enabled (set to true). Expected caller behavior looks // like: // // if features.Get().FeatureName { ... func Get() Config { fMu.RLock() defer fMu.RUnlock() // If the FeatureSet type ever changes, this must be updated to still return // only a copy of the current state, never a reference directly to it. return global }