connect: ensure that updates to the secondary root CA configuration use the correct signing key ID values for comparison (#7012)
Fixes #6886
This commit is contained in:
parent
71eca6330c
commit
446f0533cd
|
@ -319,7 +319,7 @@ func (s *Server) initializeRootCA(provider ca.Provider, conf *structs.CAConfigur
|
||||||
// initializeSecondaryCA runs the routine for generating an intermediate CA CSR and getting
|
// initializeSecondaryCA 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.
|
// intermediate.
|
||||||
func (s *Server) initializeSecondaryCA(provider ca.Provider, roots structs.IndexedCARoots) error {
|
func (s *Server) initializeSecondaryCA(provider ca.Provider, primaryRoots structs.IndexedCARoots) error {
|
||||||
activeIntermediate, err := provider.ActiveIntermediate()
|
activeIntermediate, err := provider.ActiveIntermediate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -328,8 +328,19 @@ func (s *Server) initializeSecondaryCA(provider ca.Provider, roots structs.Index
|
||||||
var (
|
var (
|
||||||
storedRootID string
|
storedRootID string
|
||||||
expectedSigningKeyID string
|
expectedSigningKeyID string
|
||||||
|
currentSigningKeyID string
|
||||||
|
activeSecondaryRoot *structs.CARoot
|
||||||
)
|
)
|
||||||
if activeIntermediate != "" {
|
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()
|
storedRoot, err := provider.ActiveRoot()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -337,7 +348,7 @@ func (s *Server) initializeSecondaryCA(provider ca.Provider, roots structs.Index
|
||||||
|
|
||||||
storedRootID, err = connect.CalculateCertFingerprint(storedRoot)
|
storedRootID, err = connect.CalculateCertFingerprint(storedRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error parsing root fingerprint: %v, %#v", err, roots)
|
return fmt.Errorf("error parsing root fingerprint: %v, %#v", err, primaryRoots)
|
||||||
}
|
}
|
||||||
|
|
||||||
intermediateCert, err := connect.ParseCert(activeIntermediate)
|
intermediateCert, err := connect.ParseCert(activeIntermediate)
|
||||||
|
@ -345,11 +356,25 @@ func (s *Server) initializeSecondaryCA(provider ca.Provider, roots structs.Index
|
||||||
return fmt.Errorf("error parsing active intermediate cert: %v", err)
|
return fmt.Errorf("error parsing active intermediate cert: %v", err)
|
||||||
}
|
}
|
||||||
expectedSigningKeyID = connect.EncodeSigningKeyID(intermediateCert.SubjectKeyId)
|
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 = s.fsm.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
|
var newActiveRoot *structs.CARoot
|
||||||
for _, root := range roots.Roots {
|
for _, root := range primaryRoots.Roots {
|
||||||
if root.ID == roots.ActiveRootID && root.Active {
|
if root.ID == primaryRoots.ActiveRootID && root.Active {
|
||||||
newActiveRoot = root
|
newActiveRoot = root
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -361,13 +386,13 @@ func (s *Server) initializeSecondaryCA(provider ca.Provider, roots structs.Index
|
||||||
// Get a signed intermediate from the primary DC if the provider
|
// Get a signed intermediate from the primary DC if the provider
|
||||||
// hasn't been initialized yet or if the primary's root has changed.
|
// hasn't been initialized yet or if the primary's root has changed.
|
||||||
needsNewIntermediate := false
|
needsNewIntermediate := false
|
||||||
if activeIntermediate == "" || storedRootID != roots.ActiveRootID {
|
if activeIntermediate == "" || storedRootID != primaryRoots.ActiveRootID {
|
||||||
needsNewIntermediate = true
|
needsNewIntermediate = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also we take this opportunity to correct an incorrectly persisted SigningKeyID
|
// Also we take this opportunity to correct an incorrectly persisted SigningKeyID
|
||||||
// in secondary datacenters (see PR-6513).
|
// in secondary datacenters (see PR-6513).
|
||||||
if expectedSigningKeyID != "" && newActiveRoot.SigningKeyID != expectedSigningKeyID {
|
if expectedSigningKeyID != "" && currentSigningKeyID != expectedSigningKeyID {
|
||||||
needsNewIntermediate = true
|
needsNewIntermediate = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,12 +419,17 @@ func (s *Server) initializeSecondaryCA(provider ca.Provider, roots structs.Index
|
||||||
return fmt.Errorf("error parsing intermediate cert: %v", err)
|
return fmt.Errorf("error parsing intermediate cert: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append the new intermediate to our local active root entry.
|
// Append the new intermediate to our local active root entry. This is
|
||||||
|
// where the root representations start to diverge.
|
||||||
newActiveRoot.IntermediateCerts = append(newActiveRoot.IntermediateCerts, intermediatePEM)
|
newActiveRoot.IntermediateCerts = append(newActiveRoot.IntermediateCerts, intermediatePEM)
|
||||||
newActiveRoot.SigningKeyID = connect.EncodeSigningKeyID(intermediateCert.SubjectKeyId)
|
newActiveRoot.SigningKeyID = connect.EncodeSigningKeyID(intermediateCert.SubjectKeyId)
|
||||||
newIntermediate = true
|
newIntermediate = true
|
||||||
|
|
||||||
s.logger.Printf("[INFO] connect: received new intermediate certificate from primary datacenter")
|
s.logger.Printf("[INFO] connect: received new intermediate certificate from primary datacenter")
|
||||||
|
} 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.
|
// Update the roots list in the state store if there's a new active root.
|
||||||
|
|
|
@ -334,36 +334,29 @@ func TestLeader_SecondaryCA_FixSigningKeyID_via_IntermediateRefresh(t *testing.T
|
||||||
|
|
||||||
// Restore the pre-1.6.1 behavior of the SigningKeyID not being derived
|
// Restore the pre-1.6.1 behavior of the SigningKeyID not being derived
|
||||||
// from the intermediates.
|
// from the intermediates.
|
||||||
|
var secondaryRootSigningKeyID string
|
||||||
{
|
{
|
||||||
require := require.New(t)
|
|
||||||
|
|
||||||
state := s2pre.fsm.State()
|
state := s2pre.fsm.State()
|
||||||
|
|
||||||
// Get the highest index
|
// Get the highest index
|
||||||
idx, roots, err := state.CARoots(nil)
|
idx, activeSecondaryRoot, err := state.CARootActive(nil)
|
||||||
require.NoError(err)
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, activeSecondaryRoot)
|
||||||
|
|
||||||
var activeRoot *structs.CARoot
|
rootCert, err := connect.ParseCert(activeSecondaryRoot.RootCert)
|
||||||
for _, root := range roots {
|
require.NoError(t, err)
|
||||||
if root.Active {
|
|
||||||
activeRoot = root
|
|
||||||
}
|
|
||||||
}
|
|
||||||
require.NotNil(activeRoot)
|
|
||||||
|
|
||||||
rootCert, err := connect.ParseCert(activeRoot.RootCert)
|
|
||||||
require.NoError(err)
|
|
||||||
|
|
||||||
// Force this to be derived just from the root, not the intermediate.
|
// Force this to be derived just from the root, not the intermediate.
|
||||||
activeRoot.SigningKeyID = connect.EncodeSigningKeyID(rootCert.SubjectKeyId)
|
secondaryRootSigningKeyID = connect.EncodeSigningKeyID(rootCert.SubjectKeyId)
|
||||||
|
activeSecondaryRoot.SigningKeyID = secondaryRootSigningKeyID
|
||||||
|
|
||||||
// Store the root cert in raft
|
// Store the root cert in raft
|
||||||
resp, err := s2pre.raftApply(structs.ConnectCARequestType, &structs.CARequest{
|
resp, err := s2pre.raftApply(structs.ConnectCARequestType, &structs.CARequest{
|
||||||
Op: structs.CAOpSetRoots,
|
Op: structs.CAOpSetRoots,
|
||||||
Index: idx,
|
Index: idx,
|
||||||
Roots: []*structs.CARoot{activeRoot},
|
Roots: []*structs.CARoot{activeSecondaryRoot},
|
||||||
})
|
})
|
||||||
require.NoError(err)
|
require.NoError(t, err)
|
||||||
if respErr, ok := resp.(error); ok {
|
if respErr, ok := resp.(error); ok {
|
||||||
t.Fatalf("respErr: %v", respErr)
|
t.Fatalf("respErr: %v", respErr)
|
||||||
}
|
}
|
||||||
|
@ -372,6 +365,7 @@ func TestLeader_SecondaryCA_FixSigningKeyID_via_IntermediateRefresh(t *testing.T
|
||||||
// Shutdown s2pre and restart it to trigger the secondary CA init to correct
|
// Shutdown s2pre and restart it to trigger the secondary CA init to correct
|
||||||
// the SigningKeyID.
|
// the SigningKeyID.
|
||||||
s2pre.Shutdown()
|
s2pre.Shutdown()
|
||||||
|
|
||||||
dir2, s2 := testServerWithConfig(t, func(c *Config) {
|
dir2, s2 := testServerWithConfig(t, func(c *Config) {
|
||||||
c.DataDir = s2pre.config.DataDir
|
c.DataDir = s2pre.config.DataDir
|
||||||
c.Datacenter = "dc2"
|
c.Datacenter = "dc2"
|
||||||
|
@ -401,7 +395,15 @@ func TestLeader_SecondaryCA_FixSigningKeyID_via_IntermediateRefresh(t *testing.T
|
||||||
|
|
||||||
// Force this to be derived just from the root, not the intermediate.
|
// Force this to be derived just from the root, not the intermediate.
|
||||||
expect := connect.EncodeSigningKeyID(intermediateCert.SubjectKeyId)
|
expect := connect.EncodeSigningKeyID(intermediateCert.SubjectKeyId)
|
||||||
|
|
||||||
|
// The in-memory representation was saw the correction via a setCAProvider call.
|
||||||
require.Equal(r, expect, activeRoot.SigningKeyID)
|
require.Equal(r, expect, activeRoot.SigningKeyID)
|
||||||
|
|
||||||
|
// The state store saw the correction, too.
|
||||||
|
_, activeSecondaryRoot, err := s2.fsm.State().CARootActive(nil)
|
||||||
|
require.NoError(r, err)
|
||||||
|
require.NotNil(r, activeSecondaryRoot)
|
||||||
|
require.Equal(r, expect, activeSecondaryRoot.SigningKeyID)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue