Merge pull request #11338 from hashicorp/dnephin/ca-manager-isolate-secondary

ca: clearly identify methods that are primary-only or secondary-only
This commit is contained in:
Daniel Nephin 2021-11-01 14:10:31 -04:00 committed by GitHub
commit eaaceedf31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 56 additions and 54 deletions

View File

@ -172,8 +172,8 @@ func (e *caStateError) Error() string {
return fmt.Sprintf("CA is already in state %q", e.Current) return fmt.Sprintf("CA is already in state %q", e.Current)
} }
// setPrimaryRoots updates the most recently seen roots from the primary. // secondarySetPrimaryRoots updates the most recently seen roots from the primary.
func (c *CAManager) setPrimaryRoots(newRoots structs.IndexedCARoots) error { func (c *CAManager) secondarySetPrimaryRoots(newRoots structs.IndexedCARoots) error {
c.stateLock.Lock() c.stateLock.Lock()
defer c.stateLock.Unlock() defer c.stateLock.Unlock()
@ -185,7 +185,7 @@ func (c *CAManager) setPrimaryRoots(newRoots structs.IndexedCARoots) error {
return nil return nil
} }
func (c *CAManager) getPrimaryRoots() structs.IndexedCARoots { func (c *CAManager) secondaryGetPrimaryRoots() structs.IndexedCARoots {
c.stateLock.Lock() c.stateLock.Lock()
defer c.stateLock.Unlock() defer c.stateLock.Unlock()
return c.primaryRoots return c.primaryRoots
@ -401,11 +401,13 @@ func (c *CAManager) InitializeCA() (reterr error) {
c.setCAProvider(provider, nil) c.setCAProvider(provider, nil)
// Run the root CA initialization if this is the primary DC.
if c.serverConf.PrimaryDatacenter == c.serverConf.Datacenter { if c.serverConf.PrimaryDatacenter == c.serverConf.Datacenter {
return c.initializeRootCA(provider, conf) return c.primaryInitialize(provider, conf)
} }
return c.secondaryInitialize(provider, conf)
}
func (c *CAManager) secondaryInitialize(provider ca.Provider, conf *structs.CAConfiguration) error {
// If this isn't the primary DC, run the secondary DC routine if the primary has already been upgraded to at least 1.6.0 // If this isn't the primary DC, run the secondary DC routine if the primary has already been upgraded to at least 1.6.0
versionOk, foundPrimary := ServersInDCMeetMinimumVersion(c.delegate, c.serverConf.PrimaryDatacenter, minMultiDCConnectVersion) versionOk, foundPrimary := ServersInDCMeetMinimumVersion(c.delegate, c.serverConf.PrimaryDatacenter, minMultiDCConnectVersion)
if !foundPrimary { if !foundPrimary {
@ -428,15 +430,15 @@ func (c *CAManager) InitializeCA() (reterr error) {
if err := c.delegate.forwardDC("ConnectCA.Roots", c.serverConf.PrimaryDatacenter, &args, &roots); err != nil { if err := c.delegate.forwardDC("ConnectCA.Roots", c.serverConf.PrimaryDatacenter, &args, &roots); err != nil {
return err return err
} }
if err := c.setPrimaryRoots(roots); err != nil { if err := c.secondarySetPrimaryRoots(roots); err != nil {
return err return err
} }
// Configure the CA provider and initialize the intermediate certificate if necessary. // Configure the CA provider and initialize the intermediate certificate if necessary.
if err := c.initializeSecondaryProvider(provider, roots); err != nil { if err := c.secondaryInitializeProvider(provider, roots); err != nil {
return fmt.Errorf("error configuring provider: %v", err) return fmt.Errorf("error configuring provider: %v", err)
} }
if err := c.initializeSecondaryCA(provider, nil); err != nil { if err := c.secondaryInitializeIntermediateCA(provider, nil); err != nil {
return err return err
} }
@ -462,9 +464,9 @@ func (c *CAManager) newProvider(conf *structs.CAConfiguration) (ca.Provider, err
} }
} }
// initializeRootCA runs the initialization logic for a root CA. It should only // primaryInitialize runs the initialization logic for a root CA. It should only
// be called while the state lock is held by setting the state to non-ready. // be called while the state lock is held by setting the state to non-ready.
func (c *CAManager) initializeRootCA(provider ca.Provider, conf *structs.CAConfiguration) error { func (c *CAManager) primaryInitialize(provider ca.Provider, conf *structs.CAConfiguration) error {
pCfg := ca.ProviderConfig{ pCfg := ca.ProviderConfig{
ClusterID: conf.ClusterID, ClusterID: conf.ClusterID,
Datacenter: c.serverConf.Datacenter, Datacenter: c.serverConf.Datacenter,
@ -581,11 +583,11 @@ func (c *CAManager) initializeRootCA(provider ca.Provider, conf *structs.CAConfi
return nil return nil
} }
// initializeSecondaryCA runs the routine for generating an intermediate CA CSR and getting // secondaryInitializeIntermediateCA runs the routine for generating an intermediate CA CSR and getting
// it signed by the primary DC if the root CA of the primary DC has changed since the last // it signed by the primary DC if the root CA of the primary DC has changed since the last
// intermediate. It should only be called while the state lock is held by setting the state // intermediate. It should only be called while the state lock is held by setting the state
// to non-ready. // to non-ready.
func (c *CAManager) initializeSecondaryCA(provider ca.Provider, config *structs.CAConfiguration) error { func (c *CAManager) secondaryInitializeIntermediateCA(provider ca.Provider, config *structs.CAConfiguration) error {
activeIntermediate, err := provider.ActiveIntermediate() activeIntermediate, err := provider.ActiveIntermediate()
if err != nil { if err != nil {
return err return err
@ -639,7 +641,7 @@ func (c *CAManager) initializeSecondaryCA(provider ca.Provider, config *structs.
// active one. We'll use this as a template to generate any new root // active one. We'll use this as a template to generate any new root
// representations meant for this secondary. // representations meant for this secondary.
var newActiveRoot *structs.CARoot var newActiveRoot *structs.CARoot
primaryRoots := c.getPrimaryRoots() primaryRoots := c.secondaryGetPrimaryRoots()
for _, root := range primaryRoots.Roots { for _, root := range primaryRoots.Roots {
if root.ID == primaryRoots.ActiveRootID && root.Active { if root.ID == primaryRoots.ActiveRootID && root.Active {
newActiveRoot = root newActiveRoot = root
@ -665,7 +667,7 @@ func (c *CAManager) initializeSecondaryCA(provider ca.Provider, config *structs.
newIntermediate := false newIntermediate := false
if needsNewIntermediate { if needsNewIntermediate {
if err := c.getIntermediateCASigned(provider, newActiveRoot); err != nil { if err := c.secondaryRenewIntermediate(provider, newActiveRoot); err != nil {
return err return err
} }
newIntermediate = true newIntermediate = true
@ -810,7 +812,7 @@ func (c *CAManager) UpdateConfiguration(args *structs.CARequest) (reterr error)
// Exit early if it's a no-op change // Exit early if it's a no-op change
state := c.delegate.State() state := c.delegate.State()
confIdx, config, err := state.CAConfig(nil) _, config, err := state.CAConfig(nil)
if err != nil { if err != nil {
return err return err
} }
@ -858,26 +860,29 @@ func (c *CAManager) UpdateConfiguration(args *structs.CARequest) (reterr error)
return fmt.Errorf("error configuring provider: %v", err) return fmt.Errorf("error configuring provider: %v", err)
} }
// Set up a defer to clean up the new provider if we exit early due to an error. cleanupNewProvider := func() {
cleanupNewProvider := true
defer func() {
if cleanupNewProvider {
if err := newProvider.Cleanup(args.Config.Provider != config.Provider, args.Config.Config); err != nil { if err := newProvider.Cleanup(args.Config.Provider != config.Provider, args.Config.Config); err != nil {
c.logger.Warn("failed to clean up CA provider while handling startup failure", "provider", newProvider, "error", err) c.logger.Warn("failed to clean up CA provider while handling startup failure", "provider", newProvider, "error", err)
} }
} }
}()
// If this is a secondary, just check if the intermediate needs to be regenerated. // If this is a secondary, just check if the intermediate needs to be regenerated.
if c.serverConf.Datacenter != c.serverConf.PrimaryDatacenter { if c.serverConf.Datacenter != c.serverConf.PrimaryDatacenter {
if err := c.initializeSecondaryCA(newProvider, args.Config); err != nil { if err := c.secondaryInitializeIntermediateCA(newProvider, args.Config); err != nil {
cleanupNewProvider()
return fmt.Errorf("Error updating secondary datacenter CA config: %v", err) return fmt.Errorf("Error updating secondary datacenter CA config: %v", err)
} }
cleanupNewProvider = false
c.logger.Info("Secondary CA provider config updated") c.logger.Info("Secondary CA provider config updated")
return nil return nil
} }
if err := c.primaryUpdateRootCA(newProvider, args, config); err != nil {
cleanupNewProvider()
return err
}
return nil
}
func (c *CAManager) primaryUpdateRootCA(newProvider ca.Provider, args *structs.CARequest, config *structs.CAConfiguration) error {
if err := newProvider.GenerateRoot(); err != nil { if err := newProvider.GenerateRoot(); err != nil {
return fmt.Errorf("error generating CA root certificate: %v", err) return fmt.Errorf("error generating CA root certificate: %v", err)
} }
@ -899,6 +904,7 @@ func (c *CAManager) UpdateConfiguration(args *structs.CARequest) (reterr error)
} }
args.Config.State = pState args.Config.State = pState
state := c.delegate.State()
// Compare the new provider's root CA ID to the current one. If they // Compare the new provider's root CA ID to the current one. If they
// match, just update the existing provider with the new config. // match, just update the existing provider with the new config.
// If they don't match, begin the root rotation process. // If they don't match, begin the root rotation process.
@ -919,11 +925,8 @@ func (c *CAManager) UpdateConfiguration(args *structs.CARequest) (reterr error)
} }
// If the config has been committed, update the local provider instance // If the config has been committed, update the local provider instance
cleanupNewProvider = false
c.setCAProvider(newProvider, newActiveRoot) c.setCAProvider(newProvider, newActiveRoot)
c.logger.Info("CA provider config updated") c.logger.Info("CA provider config updated")
return nil return nil
} }
@ -1011,7 +1014,7 @@ func (c *CAManager) UpdateConfiguration(args *structs.CARequest) (reterr error)
args.Op = structs.CAOpSetRootsAndConfig args.Op = structs.CAOpSetRootsAndConfig
args.Index = idx args.Index = idx
args.Config.ModifyIndex = confIdx args.Config.ModifyIndex = config.ModifyIndex
args.Roots = newRoots args.Roots = newRoots
resp, err := c.delegate.ApplyCARequest(args) resp, err := c.delegate.ApplyCARequest(args)
if err != nil { if err != nil {
@ -1026,7 +1029,6 @@ func (c *CAManager) UpdateConfiguration(args *structs.CARequest) (reterr error)
// If the config has been committed, update the local provider instance // If the config has been committed, update the local provider instance
// and call teardown on the old provider // and call teardown on the old provider
cleanupNewProvider = false
c.setCAProvider(newProvider, newActiveRoot) c.setCAProvider(newProvider, newActiveRoot)
if err := oldProvider.Cleanup(args.Config.Provider != config.Provider, args.Config.Config); err != nil { if err := oldProvider.Cleanup(args.Config.Provider != config.Provider, args.Config.Config); err != nil {
@ -1038,10 +1040,10 @@ func (c *CAManager) UpdateConfiguration(args *structs.CARequest) (reterr error)
return nil return nil
} }
// getIntermediateCAPrimary regenerates the intermediate cert in the primary datacenter. // primaryRenewIntermediate regenerates the intermediate cert in the primary datacenter.
// This is only run for CAs that require an intermediary in the primary DC, such as Vault. // This is only run for CAs that require an intermediary in the primary DC, such as Vault.
// It should only be called while the state lock is held by setting the state to non-ready. // It should only be called while the state lock is held by setting the state to non-ready.
func (c *CAManager) getIntermediateCAPrimary(provider ca.Provider, newActiveRoot *structs.CARoot) error { func (c *CAManager) primaryRenewIntermediate(provider ca.Provider, newActiveRoot *structs.CARoot) error {
// Generate and sign an intermediate cert using the root CA. // Generate and sign an intermediate cert using the root CA.
intermediatePEM, err := provider.GenerateIntermediate() intermediatePEM, err := provider.GenerateIntermediate()
if err != nil { if err != nil {
@ -1062,9 +1064,9 @@ func (c *CAManager) getIntermediateCAPrimary(provider ca.Provider, newActiveRoot
return nil return nil
} }
// getIntermediateCASigned should only be called while the state lock is held by // secondaryRenewIntermediate should only be called while the state lock is held by
// setting the state to non-ready. // setting the state to non-ready.
func (c *CAManager) getIntermediateCASigned(provider ca.Provider, newActiveRoot *structs.CARoot) error { func (c *CAManager) secondaryRenewIntermediate(provider ca.Provider, newActiveRoot *structs.CARoot) error {
csr, err := provider.GenerateIntermediateCSR() csr, err := provider.GenerateIntermediateCSR()
if err != nil { if err != nil {
return err return err
@ -1133,7 +1135,7 @@ func (c *CAManager) RenewIntermediate(ctx context.Context, isPrimary bool) error
return nil return nil
} }
// If this isn't the primary, make sure the CA has been initialized. // If this isn't the primary, make sure the CA has been initialized.
if !isPrimary && !c.configuredSecondaryCA() { if !isPrimary && !c.secondaryIsCAConfigured() {
return fmt.Errorf("secondary CA is not yet configured.") return fmt.Errorf("secondary CA is not yet configured.")
} }
@ -1172,9 +1174,9 @@ func (c *CAManager) RenewIntermediate(ctx context.Context, isPrimary bool) error
} }
// Enough time has passed, go ahead with getting a new intermediate. // Enough time has passed, go ahead with getting a new intermediate.
renewalFunc := c.getIntermediateCAPrimary renewalFunc := c.primaryRenewIntermediate
if !isPrimary { if !isPrimary {
renewalFunc = c.getIntermediateCASigned renewalFunc = c.secondaryRenewIntermediate
} }
errCh := make(chan error, 1) errCh := make(chan error, 1)
go func() { go func() {
@ -1227,7 +1229,7 @@ func (c *CAManager) secondaryCARootWatch(ctx context.Context) error {
} }
// Attempt to update the roots using the returned data. // Attempt to update the roots using the returned data.
if err := c.UpdateRoots(roots); err != nil { if err := c.secondaryUpdateRoots(roots); err != nil {
return err return err
} }
args.QueryOptions.MinQueryIndex = nextIndexVal(args.QueryOptions.MinQueryIndex, roots.QueryMeta.Index) args.QueryOptions.MinQueryIndex = nextIndexVal(args.QueryOptions.MinQueryIndex, roots.QueryMeta.Index)
@ -1242,9 +1244,9 @@ func (c *CAManager) secondaryCARootWatch(ctx context.Context) error {
return nil return nil
} }
// UpdateRoots updates the cached roots from the primary and regenerates the intermediate // secondaryUpdateRoots updates the cached roots from the primary and regenerates the intermediate
// certificate if necessary. // certificate if necessary.
func (c *CAManager) UpdateRoots(roots structs.IndexedCARoots) error { func (c *CAManager) secondaryUpdateRoots(roots structs.IndexedCARoots) error {
// Update the state first to claim the 'lock'. // Update the state first to claim the 'lock'.
if _, err := c.setState(caStateReconfig, true); err != nil { if _, err := c.setState(caStateReconfig, true); err != nil {
return err return err
@ -1252,7 +1254,7 @@ func (c *CAManager) UpdateRoots(roots structs.IndexedCARoots) error {
defer c.setState(caStateInitialized, false) defer c.setState(caStateInitialized, false)
// Update the cached primary roots now that the lock is held. // Update the cached primary roots now that the lock is held.
if err := c.setPrimaryRoots(roots); err != nil { if err := c.secondarySetPrimaryRoots(roots); err != nil {
return err return err
} }
@ -1263,14 +1265,14 @@ func (c *CAManager) UpdateRoots(roots structs.IndexedCARoots) error {
// this happens when leadership is being revoked and this go routine will be stopped // this happens when leadership is being revoked and this go routine will be stopped
return nil return nil
} }
if !c.configuredSecondaryCA() { if !c.secondaryIsCAConfigured() {
versionOk, primaryFound := ServersInDCMeetMinimumVersion(c.delegate, c.serverConf.PrimaryDatacenter, minMultiDCConnectVersion) versionOk, primaryFound := ServersInDCMeetMinimumVersion(c.delegate, c.serverConf.PrimaryDatacenter, minMultiDCConnectVersion)
if !primaryFound { if !primaryFound {
return fmt.Errorf("Primary datacenter is unreachable - deferring secondary CA initialization") return fmt.Errorf("Primary datacenter is unreachable - deferring secondary CA initialization")
} }
if versionOk { if versionOk {
if err := c.initializeSecondaryProvider(provider, roots); err != nil { if err := c.secondaryInitializeProvider(provider, roots); err != nil {
return fmt.Errorf("Failed to initialize secondary CA provider: %v", err) return fmt.Errorf("Failed to initialize secondary CA provider: %v", err)
} }
} }
@ -1278,8 +1280,8 @@ func (c *CAManager) UpdateRoots(roots structs.IndexedCARoots) error {
// Run the secondary CA init routine to see if we need to request a new // Run the secondary CA init routine to see if we need to request a new
// intermediate. // intermediate.
if c.configuredSecondaryCA() { if c.secondaryIsCAConfigured() {
if err := c.initializeSecondaryCA(provider, nil); err != nil { if err := c.secondaryInitializeIntermediateCA(provider, nil); err != nil {
return fmt.Errorf("Failed to initialize the secondary CA: %v", err) return fmt.Errorf("Failed to initialize the secondary CA: %v", err)
} }
} }
@ -1287,8 +1289,8 @@ func (c *CAManager) UpdateRoots(roots structs.IndexedCARoots) error {
return nil return nil
} }
// initializeSecondaryProvider configures the given provider for a secondary, non-root datacenter. // secondaryInitializeProvider configures the given provider for a secondary, non-root datacenter.
func (c *CAManager) initializeSecondaryProvider(provider ca.Provider, roots structs.IndexedCARoots) error { func (c *CAManager) secondaryInitializeProvider(provider ca.Provider, roots structs.IndexedCARoots) error {
if roots.TrustDomain == "" { if roots.TrustDomain == "" {
return fmt.Errorf("trust domain from primary datacenter is not initialized") return fmt.Errorf("trust domain from primary datacenter is not initialized")
} }
@ -1310,11 +1312,11 @@ func (c *CAManager) initializeSecondaryProvider(provider ca.Provider, roots stru
return fmt.Errorf("error configuring provider: %v", err) return fmt.Errorf("error configuring provider: %v", err)
} }
return c.setSecondaryCA() return c.secondarySetCAConfigured()
} }
// setSecondaryCA sets the flag for acting as a secondary CA to true. // secondarySetCAConfigured sets the flag for acting as a secondary CA to true.
func (c *CAManager) setSecondaryCA() error { func (c *CAManager) secondarySetCAConfigured() error {
c.stateLock.Lock() c.stateLock.Lock()
defer c.stateLock.Unlock() defer c.stateLock.Unlock()
@ -1327,8 +1329,8 @@ func (c *CAManager) setSecondaryCA() error {
return nil return nil
} }
// configuredSecondaryCA returns true if we have been initialized as a secondary datacenter's CA. // secondaryIsCAConfigured returns true if we have been initialized as a secondary datacenter's CA.
func (c *CAManager) configuredSecondaryCA() bool { func (c *CAManager) secondaryIsCAConfigured() bool {
c.stateLock.Lock() c.stateLock.Lock()
defer c.stateLock.Unlock() defer c.stateLock.Unlock()
return c.actingSecondaryCA return c.actingSecondaryCA
@ -1538,12 +1540,12 @@ func (c *CAManager) SignCertificate(csr *x509.CertificateRequest, spiffeID conne
return &reply, nil return &reply, nil
} }
func (ca *CAManager) checkExpired(pem string) error { func (c *CAManager) checkExpired(pem string) error {
cert, err := connect.ParseCert(pem) cert, err := connect.ParseCert(pem)
if err != nil { if err != nil {
return err return err
} }
if cert.NotAfter.Before(ca.timeNow()) { if cert.NotAfter.Before(c.timeNow()) {
return fmt.Errorf("certificate expired, expiration date: %s ", cert.NotAfter.String()) return fmt.Errorf("certificate expired, expiration date: %s ", cert.NotAfter.String())
} }
return nil return nil