Connect CA Primary Provider refactor (#16749)

* Rename Intermediate cert references to LeafSigningCert

Within the Consul CA subsystem, the term "Intermediate"
is confusing because the meaning changes depending on
provider and datacenter (primary vs secondary). For
example, when using the Consul CA the "ActiveIntermediate"
may return the root certificate in a primary datacenter.

At a high level, we are interested in knowing which
CA is responsible for signing leaf certs, regardless of
its position in a certificate chain. This rename makes
the intent clearer.

* Move provider state check earlier

* Remove calls to GenerateLeafSigningCert

GenerateLeafSigningCert (formerly known
as GenerateIntermediate) is vestigial in
non-Vault providers, as it simply returns
the root certificate in primary
datacenters.

By folding Vault's intermediate cert logic
into `GenerateRoot` we can encapsulate
the intermediate cert handling within
`newCARoot`.

* Move GenerateLeafSigningCert out of PrimaryProvidder

Now that the Vault Provider calls
GenerateLeafSigningCert within
GenerateRoot, we can remove the method
from all other providers that never
used it in a meaningful way.

* Add test for IntermediatePEM

* Rename GenerateRoot to GenerateCAChain

"Root" was being overloaded in the Consul CA
context, as different providers and configs
resulted in a single root certificate or
a chain originating from an external trusted
CA. Since the Vault provider also generates
intermediates, it seems more accurate to
call this a CAChain.
This commit is contained in:
Chris S. Kim 2023-04-03 11:40:33 -04:00 committed by GitHub
parent f8d6cce902
commit 8671448b73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 248 additions and 271 deletions

View File

@ -1,4 +1,4 @@
// Code generated by mockery v2.15.0. DO NOT EDIT.
// Code generated by mockery v2.20.0. DO NOT EDIT.
package ca
@ -13,18 +13,21 @@ type MockProvider struct {
mock.Mock
}
// ActiveIntermediate provides a mock function with given fields:
func (_m *MockProvider) ActiveIntermediate() (string, error) {
// ActiveLeafSigningCert provides a mock function with given fields:
func (_m *MockProvider) ActiveLeafSigningCert() (string, error) {
ret := _m.Called()
var r0 string
var r1 error
if rf, ok := ret.Get(0).(func() (string, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
@ -67,13 +70,16 @@ func (_m *MockProvider) CrossSignCA(_a0 *x509.Certificate) (string, error) {
ret := _m.Called(_a0)
var r0 string
var r1 error
if rf, ok := ret.Get(0).(func(*x509.Certificate) (string, error)); ok {
return rf(_a0)
}
if rf, ok := ret.Get(0).(func(*x509.Certificate) string); ok {
r0 = rf(_a0)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(*x509.Certificate) error); ok {
r1 = rf(_a0)
} else {
@ -83,46 +89,28 @@ func (_m *MockProvider) CrossSignCA(_a0 *x509.Certificate) (string, error) {
return r0, r1
}
// GenerateIntermediate provides a mock function with given fields:
func (_m *MockProvider) GenerateIntermediate() (string, error) {
ret := _m.Called()
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GenerateIntermediateCSR provides a mock function with given fields:
func (_m *MockProvider) GenerateIntermediateCSR() (string, string, error) {
ret := _m.Called()
var r0 string
var r1 string
var r2 error
if rf, ok := ret.Get(0).(func() (string, string, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
var r1 string
if rf, ok := ret.Get(1).(func() string); ok {
r1 = rf()
} else {
r1 = ret.Get(1).(string)
}
var r2 error
if rf, ok := ret.Get(2).(func() error); ok {
r2 = rf()
} else {
@ -132,18 +120,21 @@ func (_m *MockProvider) GenerateIntermediateCSR() (string, string, error) {
return r0, r1, r2
}
// GenerateRoot provides a mock function with given fields:
func (_m *MockProvider) GenerateRoot() (RootResult, error) {
// GenerateCAChain provides a mock function with given fields:
func (_m *MockProvider) GenerateCAChain() (CAChainResult, error) {
ret := _m.Called()
var r0 RootResult
if rf, ok := ret.Get(0).(func() RootResult); ok {
var r0 CAChainResult
var r1 error
if rf, ok := ret.Get(0).(func() (CAChainResult, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() CAChainResult); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(RootResult)
r0 = ret.Get(0).(CAChainResult)
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
@ -172,13 +163,16 @@ func (_m *MockProvider) Sign(_a0 *x509.CertificateRequest) (string, error) {
ret := _m.Called(_a0)
var r0 string
var r1 error
if rf, ok := ret.Get(0).(func(*x509.CertificateRequest) (string, error)); ok {
return rf(_a0)
}
if rf, ok := ret.Get(0).(func(*x509.CertificateRequest) string); ok {
r0 = rf(_a0)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(*x509.CertificateRequest) error); ok {
r1 = rf(_a0)
} else {
@ -193,13 +187,16 @@ func (_m *MockProvider) SignIntermediate(_a0 *x509.CertificateRequest) (string,
ret := _m.Called(_a0)
var r0 string
var r1 error
if rf, ok := ret.Get(0).(func(*x509.CertificateRequest) (string, error)); ok {
return rf(_a0)
}
if rf, ok := ret.Get(0).(func(*x509.CertificateRequest) string); ok {
r0 = rf(_a0)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(*x509.CertificateRequest) error); ok {
r1 = rf(_a0)
} else {
@ -214,6 +211,10 @@ func (_m *MockProvider) State() (map[string]string, error) {
ret := _m.Called()
var r0 map[string]string
var r1 error
if rf, ok := ret.Get(0).(func() (map[string]string, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() map[string]string); ok {
r0 = rf()
} else {
@ -222,7 +223,6 @@ func (_m *MockProvider) State() (map[string]string, error) {
}
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
@ -237,13 +237,16 @@ func (_m *MockProvider) SupportsCrossSigning() (bool, error) {
ret := _m.Called()
var r0 bool
var r1 error
if rf, ok := ret.Get(0).(func() (bool, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() bool); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(bool)
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {

View File

@ -17,12 +17,16 @@ import (
// on servers and CA provider.
var ErrRateLimited = errors.New("operation rate limited by CA provider")
// PrimaryUsesIntermediate is an optional interface that CA providers may implement
// PrimaryUsesIntermediate is an optional interface that CA providers may implement
// to indicate that they use an intermediate cert in the primary datacenter as
// well as the secondary. This is used when determining whether to run the
// intermediate renewal routine in the primary.
type PrimaryUsesIntermediate interface {
PrimaryUsesIntermediate()
// GenerateLeafSigningCert returns a new intermediate signing cert and sets it to
// the active intermediate. If multiple intermediates are needed to complete
// the chain from the signing certificate back to the active root, they should
// all by bundled here.
GenerateLeafSigningCert() (string, error)
}
// ProviderConfig encapsulates all the data Consul passes to `Configure` on a
@ -87,12 +91,12 @@ type Provider interface {
// in the Provider struct so it won't change after being returned.
State() (map[string]string, error)
// ActiveIntermediate returns the current signing cert used by this provider
// ActiveLeafSigningCert returns the current signing cert used by this provider
// for generating SPIFFE leaf certs. Note that this must not change except
// when Consul requests the change via GenerateIntermediate. Changing the
// when Consul requests the change via GenerateLeafSigningCert. Changing the
// signing cert will break Consul's assumptions about which validation paths
// are active.
ActiveIntermediate() (string, error)
ActiveLeafSigningCert() (string, error)
// Sign signs a leaf certificate used by Connect proxies from a CSR. The PEM
// returned should include only the leaf certificate as all Intermediates
@ -121,25 +125,19 @@ type Provider interface {
}
type PrimaryProvider interface {
// GenerateRoot is called:
// GenerateCAChain is called:
// * to initialize the CA system when a server is elected as a raft leader
// * when the CA configuration is updated in a way that might require
// generating a new root certificate.
//
// In both cases GenerateRoot is always called on a newly created provider
// In both cases GenerateCAChain is always called on a newly created provider
// after calling Provider.Configure, and before any other calls to the
// provider.
//
// The provider should return an existing root certificate if one exists,
// otherwise it should generate a new root certificate and return it.
GenerateRoot() (RootResult, error)
// GenerateIntermediate returns a new intermediate signing cert and sets it to
// the active intermediate. If multiple intermediates are needed to complete
// the chain from the signing certificate back to the active root, they should
// all by bundled here.
// TODO: replace with GenerateLeafSigningCert (https://github.com/hashicorp/consul/issues/12386)
GenerateIntermediate() (string, error)
// Depending on the provider and its configuration, GenerateCAChain may return
// a single root certificate or a chain of certs. The provider should return an
// existing CA chain if one exists or generate a new one and return it.
GenerateCAChain() (CAChainResult, error)
// SignIntermediate will validate the CSR to ensure the trust domain in the
// URI SAN matches the local one and that basic constraints for a CA
@ -195,10 +193,8 @@ type SecondaryProvider interface {
SetIntermediate(intermediatePEM, rootPEM, opaque string) error
}
// RootResult is the result returned by PrimaryProvider.GenerateRoot.
//
// TODO: rename this struct
type RootResult struct {
// CAChainResult is the result returned by PrimaryProvider.GenerateCAChain.
type CAChainResult struct {
// PEM encoded bundle of CA certificates. The first certificate must be the
// primary CA used to sign intermediates for secondary datacenters, and the
// last certificate must be the trusted CA.
@ -206,6 +202,11 @@ type RootResult struct {
// If there is only a single certificate in the bundle then it will be used
// as both the primary CA and the trusted CA.
PEM string
// IntermediatePEM is an encoded bundle of CA certificates used only by
// providers that use an intermediate CA to sign leaf certificates (e.g.
// Vault). Its issuer should form a chain leading to the trusted CA in PEM.
IntermediatePEM string
}
// NeedsStop is an optional interface that allows a CA to define a function

View File

@ -139,20 +139,20 @@ func (a *AWSProvider) State() (map[string]string, error) {
return state, nil
}
// GenerateRoot implements Provider
func (a *AWSProvider) GenerateRoot() (RootResult, error) {
// GenerateCAChain implements Provider
func (a *AWSProvider) GenerateCAChain() (CAChainResult, error) {
if !a.isPrimary {
return RootResult{}, fmt.Errorf("provider is not the root certificate authority")
return CAChainResult{}, fmt.Errorf("provider is not the root certificate authority")
}
if err := a.ensureCA(); err != nil {
return RootResult{}, err
return CAChainResult{}, err
}
if a.rootPEM == "" {
return RootResult{}, fmt.Errorf("AWS CA provider not fully Initialized")
return CAChainResult{}, fmt.Errorf("AWS CA provider not fully Initialized")
}
return RootResult{PEM: a.rootPEM}, nil
return CAChainResult{PEM: a.rootPEM}, nil
}
// ensureCA loads the CA resource to check it exists if configured by User or in
@ -545,8 +545,8 @@ func (a *AWSProvider) SetIntermediate(intermediatePEM string, rootPEM string, _
return nil
}
// ActiveIntermediate implements Provider
func (a *AWSProvider) ActiveIntermediate() (string, error) {
// ActiveLeafSigningCert implements Provider
func (a *AWSProvider) ActiveLeafSigningCert() (string, error) {
err := a.ensureCA()
if err != nil {
return "", err
@ -574,19 +574,6 @@ func (a *AWSProvider) ActiveIntermediate() (string, error) {
return a.intermediatePEM, nil
}
// GenerateIntermediate implements Provider
func (a *AWSProvider) GenerateIntermediate() (string, error) {
// Like the consul provider, for now the Primary DC just gets a root and no
// intermediate to sign with. so just return this. Secondaries use
// intermediates but this method is only called during primary DC (root)
// initialization in case a provider generates separate root and
// intermediates.
//
// TODO(banks) support user-supplied CA being a Subordinate even in the
// primary DC.
return a.ActiveIntermediate()
}
// Sign implements Provider
func (a *AWSProvider) Sign(csr *x509.CertificateRequest) (string, error) {
connect.HackSANExtensionForCSR(csr)

View File

@ -49,18 +49,10 @@ func TestAWSBootstrapAndSignPrimary(t *testing.T) {
provider := testAWSProvider(t, testProviderConfigPrimary(t, cfg))
defer provider.Cleanup(true, nil)
root, err := provider.GenerateRoot()
root, err := provider.GenerateCAChain()
require.NoError(t, err)
rootPEM := root.PEM
// Generate Intermediate (not actually needed for this provider for now
// but this simulates the calls in Server.initializeRoot).
interPEM, err := provider.GenerateIntermediate()
require.NoError(t, err)
// Should be the same for now
require.Equal(t, rootPEM, interPEM)
// Ensure they use the right key type
rootCert, err := connect.ParseCert(rootPEM)
require.NoError(t, err)
@ -84,7 +76,7 @@ func TestAWSBootstrapAndSignPrimary(t *testing.T) {
provider := testAWSProvider(t, testProviderConfigPrimary(t, nil))
defer provider.Cleanup(true, nil)
root, err := provider.GenerateRoot()
root, err := provider.GenerateCAChain()
require.NoError(t, err)
rootPEM := root.PEM
@ -119,7 +111,7 @@ func TestAWSBootstrapAndSignSecondary(t *testing.T) {
p1 := testAWSProvider(t, testProviderConfigPrimary(t, nil))
defer p1.Cleanup(true, nil)
root, err := p1.GenerateRoot()
root, err := p1.GenerateCAChain()
require.NoError(t, err)
rootPEM := root.PEM
@ -129,7 +121,7 @@ func TestAWSBootstrapAndSignSecondary(t *testing.T) {
testSignIntermediateCrossDC(t, p1, p2)
// Fetch intermediate from s2 now for later comparison
intPEM, err := p2.ActiveIntermediate()
intPEM, err := p2.ActiveLeafSigningCert()
require.NoError(t, err)
// Capture the state of the providers we've setup
@ -148,15 +140,15 @@ func TestAWSBootstrapAndSignSecondary(t *testing.T) {
cfg1 := testProviderConfigPrimary(t, nil)
cfg1.State = p1State
p1 = testAWSProvider(t, cfg1)
root, err := p1.GenerateRoot()
root, err := p1.GenerateCAChain()
require.NoError(t, err)
newRootPEM := root.PEM
cfg2 := testProviderConfigPrimary(t, nil)
cfg2.State = p2State
p2 = testAWSProvider(t, cfg2)
// Need call ActiveIntermediate like leader would to trigger loading from PCA
newIntPEM, err := p2.ActiveIntermediate()
// Need call ActiveLeafSigningCert like leader would to trigger loading from PCA
newIntPEM, err := p2.ActiveLeafSigningCert()
require.NoError(t, err)
// Root cert should not have changed
@ -182,7 +174,7 @@ func TestAWSBootstrapAndSignSecondary(t *testing.T) {
"ExistingARN": p1State[AWSStateCAARNKey],
})
p1 = testAWSProvider(t, cfg1)
root, err := p1.GenerateRoot()
root, err := p1.GenerateCAChain()
require.NoError(t, err)
newRootPEM := root.PEM
@ -191,8 +183,8 @@ func TestAWSBootstrapAndSignSecondary(t *testing.T) {
})
cfg1.RawConfig["ExistingARN"] = p2State[AWSStateCAARNKey]
p2 = testAWSProvider(t, cfg2)
// Need call ActiveIntermediate like leader would to trigger loading from PCA
newIntPEM, err := p2.ActiveIntermediate()
// Need call ActiveLeafSigningCert like leader would to trigger loading from PCA
newIntPEM, err := p2.ActiveLeafSigningCert()
require.NoError(t, err)
// Root cert should not have changed
@ -221,10 +213,10 @@ func TestAWSBootstrapAndSignSecondary(t *testing.T) {
p2 = testAWSProvider(t, cfg2)
require.NoError(t, p2.SetIntermediate(newIntPEM, newRootPEM, ""))
root, err = p1.GenerateRoot()
root, err = p1.GenerateCAChain()
require.NoError(t, err)
newRootPEM = root.PEM
newIntPEM, err = p2.ActiveIntermediate()
newIntPEM, err = p2.ActiveLeafSigningCert()
require.NoError(t, err)
require.Equal(t, rootPEM, newRootPEM)
@ -243,7 +235,7 @@ func TestAWSBootstrapAndSignSecondaryConsul(t *testing.T) {
p1 := TestConsulProvider(t, delegate)
cfg := testProviderConfig(conf)
require.NoError(t, p1.Configure(cfg))
_, err := p1.GenerateRoot()
_, err := p1.GenerateCAChain()
require.NoError(t, err)
p2 := testAWSProvider(t, testProviderConfigSecondary(t, nil))
@ -256,7 +248,7 @@ func TestAWSBootstrapAndSignSecondaryConsul(t *testing.T) {
p1 := testAWSProvider(t, testProviderConfigPrimary(t, nil))
defer p1.Cleanup(true, nil)
_, err := p1.GenerateRoot()
_, err := p1.GenerateCAChain()
require.NoError(t, err)
conf := testConsulCAConfig()
@ -334,7 +326,7 @@ func TestAWSProvider_Cleanup(t *testing.T) {
// create a provider with the default config which will create the CA
p1Conf := testProviderConfigPrimary(t, nil)
p1 := testAWSProvider(t, p1Conf)
p1.GenerateRoot()
p1.GenerateCAChain()
t.Cleanup(func() {
// This is a fail safe just in case the Cleanup routine of the
@ -368,7 +360,7 @@ func TestAWSProvider_Cleanup(t *testing.T) {
// create a provider with the default config which will create the CA
p1Conf := testProviderConfigPrimary(t, nil)
p1 := testAWSProvider(t, p1Conf)
p1.GenerateRoot()
p1.GenerateCAChain()
t.Cleanup(func() {
// This is a fail safe just in case the Cleanup routine of the
@ -405,7 +397,7 @@ func TestAWSProvider_Cleanup(t *testing.T) {
// create a provider with the default config which will create the CA
p1Conf := testProviderConfigPrimary(t, nil)
p1 := testAWSProvider(t, p1Conf)
p1.GenerateRoot()
p1.GenerateCAChain()
t.Cleanup(func() {
// the p2 provider should not remove the CA but we need to ensure that

View File

@ -154,18 +154,18 @@ func (c *ConsulProvider) State() (map[string]string, error) {
return c.testState, nil
}
// GenerateRoot initializes a new root certificate and private key if needed.
func (c *ConsulProvider) GenerateRoot() (RootResult, error) {
// GenerateCAChain initializes a new root certificate and private key if needed.
func (c *ConsulProvider) GenerateCAChain() (CAChainResult, error) {
providerState, err := c.getState()
if err != nil {
return RootResult{}, err
return CAChainResult{}, err
}
if !c.isPrimary {
return RootResult{}, fmt.Errorf("provider is not the root certificate authority")
return CAChainResult{}, fmt.Errorf("provider is not the root certificate authority")
}
if providerState.RootCert != "" {
return RootResult{PEM: providerState.RootCert}, nil
return CAChainResult{PEM: providerState.RootCert}, nil
}
// Generate a private key if needed
@ -173,7 +173,7 @@ func (c *ConsulProvider) GenerateRoot() (RootResult, error) {
if c.config.PrivateKey == "" {
_, pk, err := connect.GeneratePrivateKeyWithConfig(c.config.PrivateKeyType, c.config.PrivateKeyBits)
if err != nil {
return RootResult{}, err
return CAChainResult{}, err
}
newState.PrivateKey = pk
} else {
@ -184,12 +184,12 @@ func (c *ConsulProvider) GenerateRoot() (RootResult, error) {
if c.config.RootCert == "" {
nextSerial, err := c.incrementAndGetNextSerialNumber()
if err != nil {
return RootResult{}, fmt.Errorf("error computing next serial number: %v", err)
return CAChainResult{}, fmt.Errorf("error computing next serial number: %v", err)
}
ca, err := c.generateCA(newState.PrivateKey, nextSerial, c.config.RootCertTTL)
if err != nil {
return RootResult{}, fmt.Errorf("error generating CA: %v", err)
return CAChainResult{}, fmt.Errorf("error generating CA: %v", err)
}
newState.RootCert = ca
} else {
@ -202,10 +202,10 @@ func (c *ConsulProvider) GenerateRoot() (RootResult, error) {
ProviderState: &newState,
}
if _, err := c.Delegate.ApplyCARequest(args); err != nil {
return RootResult{}, err
return CAChainResult{}, err
}
return RootResult{PEM: newState.RootCert}, nil
return CAChainResult{PEM: newState.RootCert}, nil
}
// GenerateIntermediateCSR creates a private key and generates a CSR
@ -280,7 +280,7 @@ func (c *ConsulProvider) SetIntermediate(intermediatePEM, rootPEM, _ string) err
return nil
}
func (c *ConsulProvider) ActiveIntermediate() (string, error) {
func (c *ConsulProvider) ActiveLeafSigningCert() (string, error) {
providerState, err := c.getState()
if err != nil {
return "", err
@ -292,12 +292,6 @@ func (c *ConsulProvider) ActiveIntermediate() (string, error) {
return providerState.IntermediateCert, nil
}
// We aren't maintaining separate root/intermediate CAs for the builtin
// provider, so just return the root.
func (c *ConsulProvider) GenerateIntermediate() (string, error) {
return c.ActiveIntermediate()
}
// Remove the state store entry for this provider instance.
func (c *ConsulProvider) Cleanup(_ bool, _ map[string]interface{}) error {
// This method only gets called for final cleanup. Therefore we don't
@ -354,7 +348,7 @@ func (c *ConsulProvider) Sign(csr *x509.CertificateRequest) (string, error) {
}
// Parse the CA cert
certPEM, err := c.ActiveIntermediate()
certPEM, err := c.ActiveLeafSigningCert()
if err != nil {
return "", err
}

View File

@ -87,11 +87,11 @@ func TestConsulCAProvider_Bootstrap(t *testing.T) {
provider := TestConsulProvider(t, delegate)
require.NoError(t, provider.Configure(testProviderConfig(conf)))
root, err := provider.GenerateRoot()
root, err := provider.GenerateCAChain()
require.NoError(t, err)
// Intermediate should be the same cert.
inter, err := provider.ActiveIntermediate()
inter, err := provider.ActiveLeafSigningCert()
require.NoError(t, err)
require.Equal(t, root.PEM, inter)
@ -126,7 +126,7 @@ func TestConsulCAProvider_Bootstrap_WithCert(t *testing.T) {
provider := TestConsulProvider(t, delegate)
require.NoError(t, provider.Configure(testProviderConfig(conf)))
root, err := provider.GenerateRoot()
root, err := provider.GenerateCAChain()
require.NoError(t, err)
require.Equal(t, root.PEM, rootCA.RootCert)
@ -161,7 +161,7 @@ func TestConsulCAProvider_SignLeaf(t *testing.T) {
provider := TestConsulProvider(t, delegate)
require.NoError(t, provider.Configure(testProviderConfig(conf)))
_, err := provider.GenerateRoot()
_, err := provider.GenerateCAChain()
require.NoError(t, err)
spiffeService := &connect.SpiffeIDService{
@ -274,7 +274,7 @@ func TestConsulCAProvider_CrossSignCA(t *testing.T) {
conf1.Config["PrivateKeyType"] = tc.SigningKeyType
conf1.Config["PrivateKeyBits"] = tc.SigningKeyBits
require.NoError(t, provider1.Configure(testProviderConfig(conf1)))
_, err := provider1.GenerateRoot()
_, err := provider1.GenerateCAChain()
require.NoError(t, err)
conf2 := testConsulCAConfig()
@ -284,7 +284,7 @@ func TestConsulCAProvider_CrossSignCA(t *testing.T) {
conf2.Config["PrivateKeyType"] = tc.CSRKeyType
conf2.Config["PrivateKeyBits"] = tc.CSRKeyBits
require.NoError(t, provider2.Configure(testProviderConfig(conf2)))
_, err = provider2.GenerateRoot()
_, err = provider2.GenerateCAChain()
require.NoError(t, err)
testCrossSignProviders(t, provider1, provider2)
@ -295,7 +295,7 @@ func TestConsulCAProvider_CrossSignCA(t *testing.T) {
func testCrossSignProviders(t *testing.T, provider1, provider2 Provider) {
// Get the root from the new provider to be cross-signed.
root, err := provider2.GenerateRoot()
root, err := provider2.GenerateCAChain()
require.NoError(t, err)
newRoot, err := connect.ParseCert(root.PEM)
@ -304,7 +304,7 @@ func testCrossSignProviders(t *testing.T, provider1, provider2 Provider) {
requireNotEncoded(t, newRoot.SubjectKeyId)
requireNotEncoded(t, newRoot.AuthorityKeyId)
newInterPEM, err := provider2.ActiveIntermediate()
newInterPEM, err := provider2.ActiveLeafSigningCert()
require.NoError(t, err)
newIntermediate, err := connect.ParseCert(newInterPEM)
require.NoError(t, err)
@ -319,7 +319,7 @@ func testCrossSignProviders(t *testing.T, provider1, provider2 Provider) {
requireNotEncoded(t, xc.SubjectKeyId)
requireNotEncoded(t, xc.AuthorityKeyId)
p1Root, err := provider1.GenerateRoot()
p1Root, err := provider1.GenerateCAChain()
require.NoError(t, err)
oldRoot, err := connect.ParseCert(p1Root.PEM)
require.NoError(t, err)
@ -382,7 +382,7 @@ func testCrossSignProvidersShouldFail(t *testing.T, provider1, provider2 Provide
t.Helper()
// Get the root from the new provider to be cross-signed.
root, err := provider2.GenerateRoot()
root, err := provider2.GenerateCAChain()
require.NoError(t, err)
newRoot, err := connect.ParseCert(root.PEM)
@ -390,7 +390,7 @@ func testCrossSignProvidersShouldFail(t *testing.T, provider1, provider2 Provide
requireNotEncoded(t, newRoot.SubjectKeyId)
requireNotEncoded(t, newRoot.AuthorityKeyId)
newInterPEM, err := provider2.ActiveIntermediate()
newInterPEM, err := provider2.ActiveLeafSigningCert()
require.NoError(t, err)
newIntermediate, err := connect.ParseCert(newInterPEM)
require.NoError(t, err)
@ -421,7 +421,7 @@ func TestConsulProvider_SignIntermediate(t *testing.T) {
conf1.Config["PrivateKeyType"] = tc.SigningKeyType
conf1.Config["PrivateKeyBits"] = tc.SigningKeyBits
require.NoError(t, provider1.Configure(testProviderConfig(conf1)))
_, err := provider1.GenerateRoot()
_, err := provider1.GenerateCAChain()
require.NoError(t, err)
conf2 := testConsulCAConfig()
@ -452,7 +452,7 @@ func testSignIntermediateCrossDC(t *testing.T, provider1, provider2 Provider) {
// Sign the CSR with provider1.
intermediatePEM, err := provider1.SignIntermediate(csr)
require.NoError(t, err)
root, err := provider1.GenerateRoot()
root, err := provider1.GenerateCAChain()
require.NoError(t, err)
rootPEM := root.PEM
@ -527,7 +527,7 @@ func TestConsulCAProvider_MigrateOldID(t *testing.T) {
provider := TestConsulProvider(t, delegate)
require.NoError(t, provider.Configure(testProviderConfig(conf)))
_, err = provider.GenerateRoot()
_, err = provider.GenerateCAChain()
require.NoError(t, err)
// After running Configure, the old ID entry should be gone.

View File

@ -279,10 +279,10 @@ func (v *VaultProvider) State() (map[string]string, error) {
return nil, nil
}
// GenerateRoot mounts and initializes a new root PKI backend if needed.
func (v *VaultProvider) GenerateRoot() (RootResult, error) {
// GenerateCAChain mounts and initializes a new root PKI backend if needed.
func (v *VaultProvider) GenerateCAChain() (CAChainResult, error) {
if !v.isPrimary {
return RootResult{}, fmt.Errorf("provider is not the root certificate authority")
return CAChainResult{}, fmt.Errorf("provider is not the root certificate authority")
}
// Set up the root PKI backend if necessary.
@ -302,7 +302,7 @@ func (v *VaultProvider) GenerateRoot() (RootResult, error) {
},
})
if err != nil {
return RootResult{}, fmt.Errorf("failed to mount root CA backend: %w", err)
return CAChainResult{}, fmt.Errorf("failed to mount root CA backend: %w", err)
}
// We want to initialize afterwards
@ -310,7 +310,7 @@ func (v *VaultProvider) GenerateRoot() (RootResult, error) {
case ErrBackendNotInitialized:
uid, err := connect.CompactUID()
if err != nil {
return RootResult{}, err
return CAChainResult{}, err
}
resp, err := v.writeNamespaced(v.config.RootPKINamespace, v.config.RootPKIPath+"root/generate/internal", map[string]interface{}{
"common_name": connect.CACN("vault", uid, v.clusterID, v.isPrimary),
@ -319,23 +319,23 @@ func (v *VaultProvider) GenerateRoot() (RootResult, error) {
"key_bits": v.config.PrivateKeyBits,
})
if err != nil {
return RootResult{}, fmt.Errorf("failed to initialize root CA: %w", err)
return CAChainResult{}, fmt.Errorf("failed to initialize root CA: %w", err)
}
var ok bool
rootPEM, ok = resp.Data["certificate"].(string)
if !ok {
return RootResult{}, fmt.Errorf("unexpected response from Vault: %v", resp.Data["certificate"])
return CAChainResult{}, fmt.Errorf("unexpected response from Vault: %v", resp.Data["certificate"])
}
default:
if err != nil {
return RootResult{}, fmt.Errorf("unexpected error while setting root PKI backend: %w", err)
return CAChainResult{}, fmt.Errorf("unexpected error while setting root PKI backend: %w", err)
}
}
rootChain, err := v.getCAChain(v.config.RootPKINamespace, v.config.RootPKIPath)
if err != nil {
return RootResult{}, err
return CAChainResult{}, err
}
// Workaround for a bug in the Vault PKI API.
@ -344,7 +344,18 @@ func (v *VaultProvider) GenerateRoot() (RootResult, error) {
rootChain = rootPEM
}
return RootResult{PEM: rootChain}, nil
intermediate, err := v.ActiveLeafSigningCert()
if err != nil {
return CAChainResult{}, fmt.Errorf("error fetching active intermediate: %w", err)
}
if intermediate == "" {
intermediate, err = v.GenerateLeafSigningCert()
if err != nil {
return CAChainResult{}, fmt.Errorf("error generating intermediate: %w", err)
}
}
return CAChainResult{PEM: rootChain, IntermediatePEM: intermediate}, nil
}
// GenerateIntermediateCSR creates a private key and generates a CSR
@ -498,7 +509,7 @@ func (v *VaultProvider) SetIntermediate(intermediatePEM, rootPEM, keyId string)
}
// ActiveIntermediate returns the current intermediate certificate.
func (v *VaultProvider) ActiveIntermediate() (string, error) {
func (v *VaultProvider) ActiveLeafSigningCert() (string, error) {
cert, err := v.getCA(v.config.IntermediatePKINamespace, v.config.IntermediatePKIPath)
// This error is expected when calling initializeSecondaryCA for the
@ -571,7 +582,7 @@ func (v *VaultProvider) getCAChain(namespace, path string) (string, error) {
// GenerateIntermediate mounts the configured intermediate PKI backend if
// necessary, then generates and signs a new CA CSR using the root PKI backend
// and updates the intermediate backend to use that new certificate.
func (v *VaultProvider) GenerateIntermediate() (string, error) {
func (v *VaultProvider) GenerateLeafSigningCert() (string, error) {
csr, keyId, err := v.generateIntermediateCSR()
if err != nil {
return "", err
@ -607,7 +618,7 @@ func (v *VaultProvider) GenerateIntermediate() (string, error) {
}
}
return v.ActiveIntermediate()
return v.ActiveLeafSigningCert()
}
// setDefaultIntermediateIssuer updates the default issuer for
@ -814,8 +825,6 @@ func (v *VaultProvider) Stop() {
v.stopWatcher()
}
func (v *VaultProvider) PrimaryUsesIntermediate() {}
// We use raw path here
func (v *VaultProvider) mountNamespaced(namespace, path string, mountInfo *vaultapi.MountInput) error {
defer v.setNamespace(namespace)()

View File

@ -245,7 +245,7 @@ func TestVaultCAProvider_SecondaryActiveIntermediate(t *testing.T) {
"IntermediatePKIPath": "pki-intermediate/",
})
cert, err := provider.ActiveIntermediate()
cert, err := provider.ActiveLeafSigningCert()
require.Empty(t, cert)
require.NoError(t, err)
}
@ -405,7 +405,7 @@ func TestVaultCAProvider_Bootstrap(t *testing.T) {
"LeafCertTTL": "1h",
},
certFunc: func(provider *VaultProvider) (string, error) {
root, err := provider.GenerateRoot()
root, err := provider.GenerateCAChain()
return root.PEM, err
},
backendPath: "pki-root/",
@ -419,7 +419,7 @@ func TestVaultCAProvider_Bootstrap(t *testing.T) {
"RootCertTTL": "8761h",
},
certFunc: func(provider *VaultProvider) (string, error) {
return provider.ActiveIntermediate()
return provider.ActiveLeafSigningCert()
},
backendPath: "pki-intermediate/",
rootCaCreation: false,
@ -483,12 +483,12 @@ func TestVaultCAProvider_SignLeaf(t *testing.T) {
Service: "foo",
}
root, err := provider.GenerateRoot()
root, err := provider.GenerateCAChain()
require.NoError(t, err)
rootPEM := root.PEM
assertCorrectKeyType(t, tc.KeyType, rootPEM)
intPEM, err := provider.ActiveIntermediate()
intPEM, err := provider.ActiveLeafSigningCert()
require.NoError(t, err)
assertCorrectKeyType(t, tc.KeyType, intPEM)
@ -586,11 +586,11 @@ func TestVaultCAProvider_CrossSignCA(t *testing.T) {
})
testutil.RunStep(t, "init", func(t *testing.T) {
root, err := provider1.GenerateRoot()
root, err := provider1.GenerateCAChain()
require.NoError(t, err)
assertCorrectKeyType(t, tc.SigningKeyType, root.PEM)
intPEM, err := provider1.ActiveIntermediate()
intPEM, err := provider1.ActiveLeafSigningCert()
require.NoError(t, err)
assertCorrectKeyType(t, tc.SigningKeyType, intPEM)
})
@ -614,11 +614,11 @@ func TestVaultCAProvider_CrossSignCA(t *testing.T) {
})
testutil.RunStep(t, "swap", func(t *testing.T) {
root, err := provider2.GenerateRoot()
root, err := provider2.GenerateCAChain()
require.NoError(t, err)
assertCorrectKeyType(t, tc.CSRKeyType, root.PEM)
intPEM, err := provider2.ActiveIntermediate()
intPEM, err := provider2.ActiveLeafSigningCert()
require.NoError(t, err)
assertCorrectKeyType(t, tc.CSRKeyType, intPEM)
@ -738,7 +738,7 @@ func TestVaultProvider_SignIntermediateConsul(t *testing.T) {
delegate := newMockDelegate(t, conf)
provider1 := TestConsulProvider(t, delegate)
require.NoError(t, provider1.Configure(testProviderConfig(conf)))
_, err := provider1.GenerateRoot()
_, err := provider1.GenerateCAChain()
require.NoError(t, err)
// Ensure that we don't configure vault to try and mint leafs that
@ -1076,9 +1076,9 @@ func TestVaultProvider_ReconfigureIntermediateTTL(t *testing.T) {
t.Cleanup(provider.Stop)
err = provider.Configure(makeProviderConfWithTTL("222h"))
require.NoError(t, err)
_, err = provider.GenerateRoot()
_, err = provider.GenerateCAChain()
require.NoError(t, err)
_, err = provider.GenerateIntermediate()
_, err = provider.GenerateLeafSigningCert()
require.NoError(t, err)
// Attempt to update the ttl without permissions for the tune endpoint - shouldn't
@ -1127,16 +1127,16 @@ func TestVaultCAProvider_GenerateIntermediate(t *testing.T) {
"IntermediatePKIPath": "pki-intermediate/",
})
orig, err := provider.ActiveIntermediate()
orig, err := provider.ActiveLeafSigningCert()
require.NoError(t, err)
// This test was created to ensure that our calls to Vault
// returns a new Intermediate certificate and further calls
// to ActiveIntermediate return the same new cert.
new, err := provider.GenerateIntermediate()
// to ActiveLeafSigningCert return the same new cert.
new, err := provider.GenerateLeafSigningCert()
require.NoError(t, err)
newActive, err := provider.ActiveIntermediate()
newActive, err := provider.ActiveLeafSigningCert()
require.NoError(t, err)
require.Equal(t, new, newActive)
@ -1153,7 +1153,7 @@ func TestVaultCAProvider_GenerateIntermediate_inSecondary(t *testing.T) {
delegate := newMockDelegate(t, conf)
primaryProvider := TestConsulProvider(t, delegate)
require.NoError(t, primaryProvider.Configure(testProviderConfig(conf)))
_, err := primaryProvider.GenerateRoot()
_, err := primaryProvider.GenerateCAChain()
require.NoError(t, err)
// Ensure that we don't configure vault to try and mint leafs that
@ -1187,14 +1187,14 @@ func TestVaultCAProvider_GenerateIntermediate_inSecondary(t *testing.T) {
// Sign the CSR with primaryProvider.
intermediatePEM, err := primaryProvider.SignIntermediate(csr)
require.NoError(t, err)
root, err := primaryProvider.GenerateRoot()
root, err := primaryProvider.GenerateCAChain()
require.NoError(t, err)
rootPEM := root.PEM
// Give the new intermediate to provider to use.
require.NoError(t, provider.SetIntermediate(intermediatePEM, rootPEM, issuerID))
origIntermediate, err = provider.ActiveIntermediate()
origIntermediate, err = provider.ActiveLeafSigningCert()
require.NoError(t, err)
})
@ -1208,7 +1208,7 @@ func TestVaultCAProvider_GenerateIntermediate_inSecondary(t *testing.T) {
// Sign the CSR with primaryProvider.
intermediatePEM, err := primaryProvider.SignIntermediate(csr)
require.NoError(t, err)
root, err := primaryProvider.GenerateRoot()
root, err := primaryProvider.GenerateCAChain()
require.NoError(t, err)
rootPEM := root.PEM
@ -1217,8 +1217,8 @@ func TestVaultCAProvider_GenerateIntermediate_inSecondary(t *testing.T) {
// This test was created to ensure that our calls to Vault
// returns a new Intermediate certificate and further calls
// to ActiveIntermediate return the same new cert.
newActiveIntermediate, err := provider.ActiveIntermediate()
// to ActiveLeafSigningCert return the same new cert.
newActiveIntermediate, err := provider.ActiveLeafSigningCert()
require.NoError(t, err)
require.NotEqual(t, origIntermediate, newActiveIntermediate)
@ -1358,9 +1358,9 @@ func createVaultProvider(t *testing.T, isPrimary bool, addr, token string, rawCo
t.Cleanup(provider.Stop)
require.NoError(t, provider.Configure(cfg))
if isPrimary {
_, err := provider.GenerateRoot()
_, err := provider.GenerateCAChain()
require.NoError(t, err)
_, err = provider.GenerateIntermediate()
_, err = provider.GenerateLeafSigningCert()
require.NoError(t, err)
}

View File

@ -258,8 +258,8 @@ func (c *CAManager) initializeCAConfig() (*structs.CAConfiguration, error) {
}
// newCARoot returns a filled-in structs.CARoot from a raw PEM value.
func newCARoot(pemValue, provider, clusterID string) (*structs.CARoot, error) {
primaryCert, err := connect.ParseCert(pemValue)
func newCARoot(rootResult ca.CAChainResult, provider, clusterID string) (*structs.CARoot, error) {
primaryCert, err := connect.ParseCert(rootResult.PEM)
if err != nil {
return nil, err
}
@ -267,7 +267,7 @@ func newCARoot(pemValue, provider, clusterID string) (*structs.CARoot, error) {
if err != nil {
return nil, fmt.Errorf("error extracting root key info: %v", err)
}
return &structs.CARoot{
caRoot := &structs.CARoot{
ID: connect.CalculateCertFingerprint(primaryCert.Raw),
Name: fmt.Sprintf("%s CA Primary Cert", providerPrettyName(provider)),
SerialNumber: primaryCert.SerialNumber.Uint64(),
@ -275,11 +275,18 @@ func newCARoot(pemValue, provider, clusterID string) (*structs.CARoot, error) {
ExternalTrustDomain: clusterID,
NotBefore: primaryCert.NotBefore,
NotAfter: primaryCert.NotAfter,
RootCert: lib.EnsureTrailingNewline(pemValue),
RootCert: lib.EnsureTrailingNewline(rootResult.PEM),
PrivateKeyType: keyType,
PrivateKeyBits: keyBits,
Active: true,
}, nil
}
if rootResult.IntermediatePEM == "" {
return caRoot, nil
}
if err := setLeafSigningCert(caRoot, rootResult.IntermediatePEM); err != nil {
return nil, fmt.Errorf("error setting leaf signing cert: %w", err)
}
return caRoot, nil
}
// getCAProvider returns the currently active instance of the CA Provider,
@ -482,25 +489,6 @@ func (c *CAManager) primaryInitialize(provider ca.Provider, conf *structs.CAConf
if err := provider.Configure(pCfg); err != nil {
return fmt.Errorf("error configuring provider: %v", err)
}
root, err := provider.GenerateRoot()
if err != nil {
return fmt.Errorf("error generating CA root certificate: %v", err)
}
rootCA, err := newCARoot(root.PEM, conf.Provider, conf.ClusterID)
if err != nil {
return err
}
// TODO: https://github.com/hashicorp/consul/issues/12386
interPEM, err := provider.GenerateIntermediate()
if err != nil {
return fmt.Errorf("error generating intermediate cert: %v", err)
}
intermediateCert, err := connect.ParseCert(interPEM)
if err != nil {
return fmt.Errorf("error getting intermediate cert: %v", err)
}
// If the provider has state to persist and it's changed or new then update
// CAConfig.
@ -520,24 +508,18 @@ func (c *CAManager) primaryInitialize(provider ca.Provider, conf *structs.CAConf
}
}
var rootUpdateRequired bool
// Versions prior to 1.9.3, 1.8.8, and 1.7.12 incorrectly used the primary
// rootCA's subjectKeyID here instead of the intermediate. For
// provider=consul this didn't matter since there are no intermediates in
// the primaryDC, but for vault it does matter.
expectedSigningKeyID := connect.EncodeSigningKeyID(intermediateCert.SubjectKeyId)
if rootCA.SigningKeyID != expectedSigningKeyID {
c.logger.Info("Correcting stored CARoot values",
"previous-signing-key", rootCA.SigningKeyID, "updated-signing-key", expectedSigningKeyID)
rootCA.SigningKeyID = expectedSigningKeyID
rootUpdateRequired = true
root, err := provider.GenerateCAChain()
if err != nil {
return fmt.Errorf("error generating CA root certificate: %v", err)
}
// Add the local leaf signing cert to the rootCA struct. This handles both
// upgrades of existing state, and new rootCA.
if c.getLeafSigningCertFromRoot(rootCA) != interPEM {
rootCA.IntermediateCerts = append(rootCA.IntermediateCerts, interPEM)
rootCA, err := newCARoot(root, conf.Provider, conf.ClusterID)
if err != nil {
return err
}
var rootUpdateRequired bool
if len(rootCA.IntermediateCerts) > 0 {
rootUpdateRequired = true
}
@ -580,14 +562,10 @@ func (c *CAManager) primaryInitialize(provider ca.Provider, conf *structs.CAConf
// sign leaf certificates in the local datacenter. The SubjectKeyId of the
// returned cert should always match the SigningKeyID of the CARoot.
//
// TODO: fix the data model so that we don't need this complicated lookup to
// find the leaf signing cert. See github.com/hashicorp/consul/issues/11347.
// TODO: only used in tests. Remove if possible.
func (c *CAManager) getLeafSigningCertFromRoot(root *structs.CARoot) string {
if !c.isIntermediateUsedToSignLeaf() {
return root.RootCert
}
if len(root.IntermediateCerts) == 0 {
return ""
return root.RootCert
}
return root.IntermediateCerts[len(root.IntermediateCerts)-1]
}
@ -599,7 +577,7 @@ func (c *CAManager) getLeafSigningCertFromRoot(root *structs.CARoot) string {
// 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()
activeIntermediate, err := provider.ActiveLeafSigningCert()
if err != nil {
return err
}
@ -879,34 +857,6 @@ type ValidateConfigUpdater interface {
}
func (c *CAManager) primaryUpdateRootCA(newProvider ca.Provider, args *structs.CARequest, config *structs.CAConfiguration) error {
providerRoot, err := newProvider.GenerateRoot()
if err != nil {
return fmt.Errorf("error generating CA root certificate: %v", err)
}
newRootPEM := providerRoot.PEM
newActiveRoot, err := newCARoot(newRootPEM, args.Config.Provider, args.Config.ClusterID)
if err != nil {
return err
}
// TODO: https://github.com/hashicorp/consul/issues/12386
intermediate, err := newProvider.ActiveIntermediate()
if err != nil {
return fmt.Errorf("error fetching active intermediate: %w", err)
}
if intermediate == "" {
intermediate, err = newProvider.GenerateIntermediate()
if err != nil {
return fmt.Errorf("error generating intermediate: %w", err)
}
}
if intermediate != newRootPEM {
if err := setLeafSigningCert(newActiveRoot, intermediate); err != nil {
return err
}
}
// See if the provider needs to persist any state along with the config
pState, err := newProvider.State()
if err != nil {
@ -914,6 +864,17 @@ func (c *CAManager) primaryUpdateRootCA(newProvider ca.Provider, args *structs.C
}
args.Config.State = pState
providerRoot, err := newProvider.GenerateCAChain()
if err != nil {
return fmt.Errorf("error generating CA root certificate: %v", err)
}
newRootPEM := providerRoot.PEM
newActiveRoot, err := newCARoot(providerRoot, args.Config.Provider, args.Config.ClusterID)
if err != nil {
return err
}
state := c.delegate.State()
// Compare the new provider's root CA ID to the current one. If they
// match, just update the existing provider with the new config.
@ -1042,8 +1003,12 @@ func (c *CAManager) primaryUpdateRootCA(newProvider ca.Provider, args *structs.C
// 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.
func (c *CAManager) primaryRenewIntermediate(provider ca.Provider, newActiveRoot *structs.CARoot) error {
p, ok := provider.(ca.PrimaryUsesIntermediate)
if !ok {
return fmt.Errorf("tried to renew intermediates for a provider that does not use them")
}
// Generate and sign an intermediate cert using the root CA.
intermediatePEM, err := provider.GenerateIntermediate()
intermediatePEM, err := p.GenerateLeafSigningCert()
if err != nil {
return fmt.Errorf("error generating new intermediate cert: %v", err)
}
@ -1187,7 +1152,7 @@ func (c *CAManager) renewIntermediate(ctx context.Context, forceNow bool) error
return nil
}
activeIntermediate, err := provider.ActiveIntermediate()
activeIntermediate, err := provider.ActiveLeafSigningCert()
if err != nil {
return err
}

View File

@ -259,8 +259,8 @@ type mockCAProvider struct {
func (m *mockCAProvider) Configure(cfg ca.ProviderConfig) error { return nil }
func (m *mockCAProvider) State() (map[string]string, error) { return nil, nil }
func (m *mockCAProvider) GenerateRoot() (ca.RootResult, error) {
return ca.RootResult{PEM: m.rootPEM}, nil
func (m *mockCAProvider) GenerateCAChain() (ca.CAChainResult, error) {
return ca.CAChainResult{PEM: m.rootPEM}, nil
}
func (m *mockCAProvider) GenerateIntermediateCSR() (string, string, error) {
m.callbackCh <- "provider/GenerateIntermediateCSR"
@ -270,13 +270,13 @@ func (m *mockCAProvider) SetIntermediate(intermediatePEM, rootPEM, _ string) err
m.callbackCh <- "provider/SetIntermediate"
return nil
}
func (m *mockCAProvider) ActiveIntermediate() (string, error) {
func (m *mockCAProvider) ActiveLeafSigningCert() (string, error) {
if m.intermediatePem == "" {
return m.rootPEM, nil
}
return m.intermediatePem, nil
}
func (m *mockCAProvider) GenerateIntermediate() (string, error) { return "", nil }
func (m *mockCAProvider) Sign(*x509.CertificateRequest) (string, error) { return "", nil }
func (m *mockCAProvider) SignIntermediate(*x509.CertificateRequest) (string, error) { return "", nil }
func (m *mockCAProvider) CrossSignCA(*x509.Certificate) (string, error) { return "", nil }

View File

@ -15,7 +15,7 @@ import (
"time"
msgpackrpc "github.com/hashicorp/consul-net-rpc/net-rpc-msgpackrpc"
uuid "github.com/hashicorp/go-uuid"
"github.com/hashicorp/go-uuid"
"github.com/stretchr/testify/require"
"gotest.tools/v3/assert"
@ -254,7 +254,7 @@ func TestCAManager_Initialize_Secondary(t *testing.T) {
retry.Run(t, func(r *retry.R) {
_, caRoot = getCAProviderWithLock(s1)
secondaryProvider, _ = getCAProviderWithLock(s2)
intermediatePEM, err = secondaryProvider.ActiveIntermediate()
intermediatePEM, err = secondaryProvider.ActiveLeafSigningCert()
require.NoError(r, err)
// Sanity check CA is using the correct key type
@ -581,7 +581,7 @@ func TestConnectCA_ConfigurationSet_RootRotation_Secondary(t *testing.T) {
// Get the original intermediate
secondaryProvider, _ := getCAProviderWithLock(s2)
oldIntermediatePEM, err := secondaryProvider.ActiveIntermediate()
oldIntermediatePEM, err := secondaryProvider.ActiveLeafSigningCert()
require.NoError(t, err)
require.NotEmpty(t, oldIntermediatePEM)
@ -634,7 +634,7 @@ func TestConnectCA_ConfigurationSet_RootRotation_Secondary(t *testing.T) {
// Wait for dc2's intermediate to be refreshed.
var intermediatePEM string
retry.Run(t, func(r *retry.R) {
intermediatePEM, err = secondaryProvider.ActiveIntermediate()
intermediatePEM, err = secondaryProvider.ActiveLeafSigningCert()
r.Check(err)
if intermediatePEM == oldIntermediatePEM {
r.Fatal("not a new intermediate")
@ -856,7 +856,7 @@ func TestCAManager_Initialize_Vault_FixesSigningKeyID_Primary(t *testing.T) {
require.NotNil(r, provider)
require.NotNil(r, activeRoot)
activeIntermediate, err := provider.ActiveIntermediate()
activeIntermediate, err := provider.ActiveLeafSigningCert()
require.NoError(r, err)
intermediateCert, err := connect.ParseCert(activeIntermediate)
@ -957,7 +957,7 @@ func TestCAManager_Initialize_FixesSigningKeyID_Secondary(t *testing.T) {
require.NotNil(r, provider)
require.NotNil(r, activeRoot)
activeIntermediate, err := provider.ActiveIntermediate()
activeIntermediate, err := provider.ActiveLeafSigningCert()
require.NoError(r, err)
intermediateCert, err := connect.ParseCert(activeIntermediate)
@ -1146,13 +1146,13 @@ func TestCAManager_Initialize_SecondaryBeforePrimary(t *testing.T) {
require.Equal(r, roots1[0].RootCert, roots2[0].RootCert)
secondaryProvider, _ := getCAProviderWithLock(s2)
inter, err := secondaryProvider.ActiveIntermediate()
inter, err := secondaryProvider.ActiveLeafSigningCert()
require.NoError(r, err)
require.NotEmpty(r, inter, "should have valid intermediate")
})
secondaryProvider, _ := getCAProviderWithLock(s2)
intermediatePEM, err := secondaryProvider.ActiveIntermediate()
intermediatePEM, err := secondaryProvider.ActiveLeafSigningCert()
require.NoError(t, err)
_, caRoot := getCAProviderWithLock(s1)
@ -1354,20 +1354,24 @@ func TestConnectCA_ConfigurationSet_PersistsRoots(t *testing.T) {
func TestNewCARoot(t *testing.T) {
type testCase struct {
name string
pem string
expected *structs.CARoot
expectedErr string
name string
pem string
intermediatePem string
expected *structs.CARoot
expectedErr string
}
run := func(t *testing.T, tc testCase) {
root, err := newCARoot(tc.pem, "provider-name", "cluster-id")
root, err := newCARoot(ca.CAChainResult{
PEM: tc.pem,
IntermediatePEM: tc.intermediatePem,
}, "provider-name", "cluster-id")
if tc.expectedErr != "" {
testutil.RequireErrorContains(t, err, tc.expectedErr)
return
}
require.NoError(t, err)
assert.DeepEqual(t, root, tc.expected)
assert.DeepEqual(t, tc.expected, root)
}
// Test certs can be generated with
@ -1464,6 +1468,28 @@ func TestNewCARoot(t *testing.T) {
PrivateKeyBits: 256,
},
},
{
// Although the intermediate pem doesn't have pem as the issuer
// as in a real certificate chain, we are testing that the IntermediateCerts
// are being populated and that the signing key is from the intermediatePem.
name: "pem with intermediate pem",
pem: readTestData(t, "cert-with-rsa-4096-key.pem"),
intermediatePem: readTestData(t, "cert-with-ec-256-key.pem"),
expected: &structs.CARoot{
ID: "3a:6a:e3:e2:2d:44:85:5a:e9:44:3b:ef:d2:90:78:83:7f:61:a2:84",
Name: "Provider-Name CA Primary Cert",
SerialNumber: 5186695743100577491,
SigningKeyID: "97:4d:17:81:64:f8:b4:af:05:e8:6c:79:c5:40:3b:0e:3e:8b:c0:ae:38:51:54:8a:2f:05:db:e3:e8:e4:24:ec",
ExternalTrustDomain: "cluster-id",
NotBefore: time.Date(2019, 10, 17, 11, 53, 15, 0, time.UTC),
NotAfter: time.Date(2029, 10, 17, 11, 53, 15, 0, time.UTC),
RootCert: readTestData(t, "cert-with-rsa-4096-key.pem"),
IntermediateCerts: []string{readTestData(t, "cert-with-ec-256-key.pem")},
Active: true,
PrivateKeyType: "rsa",
PrivateKeyBits: 4096,
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {