Merge pull request #11663 from hashicorp/dnephin/ca-remove-one-call-to-active-root-2
ca: remove second call to Provider.ActiveRoot
This commit is contained in:
commit
d56a1dfb2c
|
@ -197,11 +197,18 @@ func (c *CAManager) secondarySetPrimaryRoots(newRoots structs.IndexedCARoots) {
|
|||
c.primaryRoots = newRoots
|
||||
}
|
||||
|
||||
func (c *CAManager) secondaryGetPrimaryRoots() structs.IndexedCARoots {
|
||||
func (c *CAManager) secondaryGetActivePrimaryCARoot() (*structs.CARoot, error) {
|
||||
// TODO: this could be a different lock, as long as its the same lock in secondarySetPrimaryRoots
|
||||
c.stateLock.Lock()
|
||||
defer c.stateLock.Unlock()
|
||||
return c.primaryRoots
|
||||
primaryRoots := c.primaryRoots
|
||||
c.stateLock.Unlock()
|
||||
|
||||
for _, root := range primaryRoots.Roots {
|
||||
if root.ID == primaryRoots.ActiveRootID && root.Active {
|
||||
return root, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("primary datacenter does not have an active root CA for Connect")
|
||||
}
|
||||
|
||||
// initializeCAConfig is used to initialize the CA config if necessary
|
||||
|
@ -602,79 +609,45 @@ func (c *CAManager) getLeafSigningCertFromRoot(root *structs.CARoot) string {
|
|||
return root.IntermediateCerts[len(root.IntermediateCerts)-1]
|
||||
}
|
||||
|
||||
// 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
|
||||
// intermediate. It should only be called while the state lock is held by setting the state
|
||||
// to non-ready.
|
||||
// secondaryInitializeIntermediateCA generates a Certificate Signing Request (CSR)
|
||||
// for the intermediate CA that is used to sign leaf certificates in the secondary.
|
||||
// The CSR is signed by the primary DC and then persisted in the state store.
|
||||
//
|
||||
// This method should only be called while the state lock is held by setting the
|
||||
// state to non-ready.
|
||||
func (c *CAManager) secondaryInitializeIntermediateCA(provider ca.Provider, config *structs.CAConfiguration) error {
|
||||
activeIntermediate, err := provider.ActiveIntermediate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
storedRootID string
|
||||
expectedSigningKeyID string
|
||||
currentSigningKeyID string
|
||||
activeSecondaryRoot *structs.CARoot
|
||||
)
|
||||
_, activeRoot, err := c.delegate.State().CARootActive(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var currentSigningKeyID string
|
||||
if activeRoot != nil {
|
||||
currentSigningKeyID = activeRoot.SigningKeyID
|
||||
}
|
||||
|
||||
var expectedSigningKeyID string
|
||||
if activeIntermediate != "" {
|
||||
// In the event that we already have an intermediate, we must have
|
||||
// already replicated some primary root information locally, so check
|
||||
// to see if we're up to date by fetching the rootID and the
|
||||
// signingKeyID used in the secondary.
|
||||
//
|
||||
// Note that for the same rootID the primary representation of the root
|
||||
// will have a different SigningKeyID field than the secondary
|
||||
// representation of the same root. This is because it's derived from
|
||||
// the intermediate which is different in all datacenters.
|
||||
storedRoot, err := provider.ActiveRoot()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
storedRootID, err = connect.CalculateCertFingerprint(storedRoot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing root fingerprint: %v, %#v", err, storedRoot)
|
||||
}
|
||||
|
||||
intermediateCert, err := connect.ParseCert(activeIntermediate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing active intermediate cert: %v", err)
|
||||
}
|
||||
expectedSigningKeyID = connect.EncodeSigningKeyID(intermediateCert.SubjectKeyId)
|
||||
|
||||
// This will fetch the secondary's exact current representation of the
|
||||
// active root. Note that this data should only be used if the IDs
|
||||
// match, otherwise it's out of date and should be regenerated.
|
||||
_, activeSecondaryRoot, err = c.delegate.State().CARootActive(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if activeSecondaryRoot != nil {
|
||||
currentSigningKeyID = activeSecondaryRoot.SigningKeyID
|
||||
}
|
||||
}
|
||||
|
||||
// Determine which of the provided PRIMARY representations of roots is the
|
||||
// active one. We'll use this as a template to generate any new root
|
||||
// representations meant for this secondary.
|
||||
var newActiveRoot *structs.CARoot
|
||||
primaryRoots := c.secondaryGetPrimaryRoots()
|
||||
for _, root := range primaryRoots.Roots {
|
||||
if root.ID == primaryRoots.ActiveRootID && root.Active {
|
||||
newActiveRoot = root
|
||||
break
|
||||
}
|
||||
}
|
||||
if newActiveRoot == nil {
|
||||
return fmt.Errorf("primary datacenter does not have an active root CA for Connect")
|
||||
newActiveRoot, err := c.secondaryGetActivePrimaryCARoot()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get a signed intermediate from the primary DC if the provider
|
||||
// hasn't been initialized yet or if the primary's root has changed.
|
||||
needsNewIntermediate := false
|
||||
if activeIntermediate == "" || storedRootID != primaryRoots.ActiveRootID {
|
||||
needsNewIntermediate := activeIntermediate == ""
|
||||
if activeRoot != nil && newActiveRoot.ID != activeRoot.ID {
|
||||
needsNewIntermediate = true
|
||||
}
|
||||
|
||||
|
@ -684,28 +657,19 @@ func (c *CAManager) secondaryInitializeIntermediateCA(provider ca.Provider, conf
|
|||
needsNewIntermediate = true
|
||||
}
|
||||
|
||||
newIntermediate := false
|
||||
if needsNewIntermediate {
|
||||
if err := c.secondaryRenewIntermediate(provider, newActiveRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
newIntermediate = true
|
||||
} else {
|
||||
// Discard the primary's representation since our local one is
|
||||
// sufficiently up to date.
|
||||
newActiveRoot = activeSecondaryRoot
|
||||
}
|
||||
|
||||
// Update the roots list in the state store if there's a new active root.
|
||||
state := c.delegate.State()
|
||||
_, activeRoot, err := state.CARootActive(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
newActiveRoot = activeRoot
|
||||
}
|
||||
|
||||
// Determine whether a root update is needed, and persist the roots/config accordingly.
|
||||
var newRoot *structs.CARoot
|
||||
if activeRoot == nil || activeRoot.ID != newActiveRoot.ID || newIntermediate {
|
||||
if activeRoot == nil || needsNewIntermediate {
|
||||
newRoot = newActiveRoot
|
||||
}
|
||||
if err := c.persistNewRootAndConfig(provider, newRoot, config); err != nil {
|
||||
|
|
|
@ -76,9 +76,14 @@ type CARoot struct {
|
|||
// SerialNumber is the x509 serial number of the certificate.
|
||||
SerialNumber uint64
|
||||
|
||||
// SigningKeyID is the ID of the public key that corresponds to the private
|
||||
// key used to sign leaf certificates. Is is the HexString format of the
|
||||
// raw AuthorityKeyID bytes.
|
||||
// SigningKeyID is the connect.HexString encoded id of the public key that
|
||||
// corresponds to the private key used to sign leaf certificates in the
|
||||
// local datacenter.
|
||||
//
|
||||
// The value comes from x509.Certificate.SubjectKeyId of the local leaf
|
||||
// signing cert.
|
||||
//
|
||||
// See https://www.rfc-editor.org/rfc/rfc3280#section-4.2.1.1 for more detail.
|
||||
SigningKeyID string
|
||||
|
||||
// ExternalTrustDomain is the trust domain this root was generated under. It
|
||||
|
@ -192,10 +197,14 @@ type IssuedCert struct {
|
|||
// This is encoded in standard hex separated by :.
|
||||
SerialNumber string
|
||||
|
||||
// CertPEM and PrivateKeyPEM are the PEM-encoded certificate and private
|
||||
// key for that cert, respectively. This should not be stored in the
|
||||
// state store, but is present in the sign API response.
|
||||
CertPEM string `json:",omitempty"`
|
||||
// CertPEM is a PEM encoded bundle of a leaf certificate, optionally followed
|
||||
// by one or more intermediate certificates that will form a chain of trust
|
||||
// back to a root CA.
|
||||
//
|
||||
// This field is not persisted in the state store, but is present in the
|
||||
// sign API response.
|
||||
CertPEM string `json:",omitempty"`
|
||||
// PrivateKeyPEM is the PEM encoded private key associated with CertPEM.
|
||||
PrivateKeyPEM string `json:",omitempty"`
|
||||
|
||||
// Service is the name of the service for which the cert was issued.
|
||||
|
|
|
@ -144,6 +144,7 @@ func WaitForTestAgent(t *testing.T, rpc rpcFn, dc string, options ...waitOption)
|
|||
// raft leadership is gained so WaitForLeader isn't sufficient to be sure that
|
||||
// the CA is fully initialized.
|
||||
func WaitForActiveCARoot(t *testing.T, rpc rpcFn, dc string, expect *structs.CARoot) {
|
||||
t.Helper()
|
||||
retry.Run(t, func(r *retry.R) {
|
||||
args := &structs.DCSpecificRequest{
|
||||
Datacenter: dc,
|
||||
|
|
Loading…
Reference in New Issue