ca: add test cases for rotating external trusted CA

This commit is contained in:
Daniel Nephin 2022-02-02 18:51:59 -05:00
parent aacc40012f
commit 0abaf29c10
2 changed files with 88 additions and 23 deletions

3
.changelog/11910.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
ca: support using an external root CA with the vault CA provider
```

View File

@ -98,15 +98,26 @@ func TestCAManager_Initialize_Vault_Secondary_SharedVault(t *testing.T) {
} }
func verifyLeafCert(t *testing.T, root *structs.CARoot, leafCertPEM string) { func verifyLeafCert(t *testing.T, root *structs.CARoot, leafCertPEM string) {
t.Helper()
roots := structs.IndexedCARoots{
ActiveRootID: root.ID,
Roots: []*structs.CARoot{root},
}
verifyLeafCertWithRoots(t, roots, leafCertPEM)
}
func verifyLeafCertWithRoots(t *testing.T, roots structs.IndexedCARoots, leafCertPEM string) {
t.Helper() t.Helper()
leaf, intermediates, err := connect.ParseLeafCerts(leafCertPEM) leaf, intermediates, err := connect.ParseLeafCerts(leafCertPEM)
require.NoError(t, err) require.NoError(t, err)
pool := x509.NewCertPool() pool := x509.NewCertPool()
ok := pool.AppendCertsFromPEM([]byte(root.RootCert)) for _, r := range roots.Roots {
ok := pool.AppendCertsFromPEM([]byte(r.RootCert))
if !ok { if !ok {
t.Fatalf("Failed to add root CA PEM to cert pool") t.Fatalf("Failed to add root CA PEM to cert pool")
} }
}
// verify with intermediates from leaf CertPEM // verify with intermediates from leaf CertPEM
_, err = leaf.Verify(x509.VerifyOptions{ _, err = leaf.Verify(x509.VerifyOptions{
@ -118,11 +129,13 @@ func verifyLeafCert(t *testing.T, root *structs.CARoot, leafCertPEM string) {
// verify with intermediates from the CARoot // verify with intermediates from the CARoot
intermediates = x509.NewCertPool() intermediates = x509.NewCertPool()
for _, intermediate := range root.IntermediateCerts { for _, r := range roots.Roots {
for _, intermediate := range r.IntermediateCerts {
c, err := connect.ParseCert(intermediate) c, err := connect.ParseCert(intermediate)
require.NoError(t, err) require.NoError(t, err)
intermediates.AddCert(c) intermediates.AddCert(c)
} }
}
_, err = leaf.Verify(x509.VerifyOptions{ _, err = leaf.Verify(x509.VerifyOptions{
Roots: pool, Roots: pool,
@ -628,10 +641,6 @@ func TestCAManager_Initialize_Vault_WithIntermediateAsPrimaryCA(t *testing.T) {
"Token": vault.RootToken, "Token": vault.RootToken,
"RootPKIPath": meshRootPath, "RootPKIPath": meshRootPath,
"IntermediatePKIPath": "pki-intermediate/", "IntermediatePKIPath": "pki-intermediate/",
// TODO: there are failures to init the CA system if these are not set
// to the values of the already initialized CA.
"PrivateKeyType": "ec",
"PrivateKeyBits": 256,
}, },
} }
}) })
@ -664,10 +673,6 @@ func TestCAManager_Initialize_Vault_WithIntermediateAsPrimaryCA(t *testing.T) {
"Token": vault.RootToken, "Token": vault.RootToken,
"RootPKIPath": meshRootPath, "RootPKIPath": meshRootPath,
"IntermediatePKIPath": "pki-secondary/", "IntermediatePKIPath": "pki-secondary/",
// TODO: there are failures to init the CA system if these are not set
// to the values of the already initialized CA.
"PrivateKeyType": "ec",
"PrivateKeyBits": 256,
}, },
} }
}) })
@ -727,10 +732,6 @@ func TestCAManager_Initialize_Vault_WithExternalTrustedCA(t *testing.T) {
"Token": vault.RootToken, "Token": vault.RootToken,
"RootPKIPath": primaryCAPath, "RootPKIPath": primaryCAPath,
"IntermediatePKIPath": "pki-intermediate/", "IntermediatePKIPath": "pki-intermediate/",
// TODO: there are failures to init the CA system if these are not set
// to the values of the already initialized CA.
"PrivateKeyType": "ec",
"PrivateKeyBits": 256,
}, },
} }
}) })
@ -765,15 +766,12 @@ func TestCAManager_Initialize_Vault_WithExternalTrustedCA(t *testing.T) {
} }
}) })
// TODO: renew primary leaf signing cert
// TODO: rotate root
runStep(t, "start secondary DC", func(t *testing.T) { runStep(t, "start secondary DC", func(t *testing.T) {
joinWAN(t, serverDC2, serverDC1) joinWAN(t, serverDC2, serverDC1)
testrpc.WaitForActiveCARoot(t, serverDC2.RPC, "dc2", nil) testrpc.WaitForActiveCARoot(t, serverDC2.RPC, "dc2", nil)
codec := rpcClient(t, serverDC2) codec := rpcClient(t, serverDC2)
roots := structs.IndexedCARoots{} roots = structs.IndexedCARoots{}
err := msgpackrpc.CallWithCodec(codec, "ConnectCA.Roots", &structs.DCSpecificRequest{}, &roots) err := msgpackrpc.CallWithCodec(codec, "ConnectCA.Roots", &structs.DCSpecificRequest{}, &roots)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, roots.Roots, 1) require.Len(t, roots.Roots, 1)
@ -788,6 +786,7 @@ func TestCAManager_Initialize_Vault_WithExternalTrustedCA(t *testing.T) {
renewLeafSigningCert(t, serverDC1.caManager, serverDC1.caManager.primaryRenewIntermediate) renewLeafSigningCert(t, serverDC1.caManager, serverDC1.caManager.primaryRenewIntermediate)
codec := rpcClient(t, serverDC1) codec := rpcClient(t, serverDC1)
roots = structs.IndexedCARoots{}
err := msgpackrpc.CallWithCodec(codec, "ConnectCA.Roots", &structs.DCSpecificRequest{}, &roots) err := msgpackrpc.CallWithCodec(codec, "ConnectCA.Roots", &structs.DCSpecificRequest{}, &roots)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, roots.Roots, 1) require.Len(t, roots.Roots, 1)
@ -809,6 +808,7 @@ func TestCAManager_Initialize_Vault_WithExternalTrustedCA(t *testing.T) {
renewLeafSigningCert(t, serverDC2.caManager, serverDC2.caManager.secondaryRequestNewSigningCert) renewLeafSigningCert(t, serverDC2.caManager, serverDC2.caManager.secondaryRequestNewSigningCert)
codec := rpcClient(t, serverDC2) codec := rpcClient(t, serverDC2)
roots = structs.IndexedCARoots{}
err := msgpackrpc.CallWithCodec(codec, "ConnectCA.Roots", &structs.DCSpecificRequest{}, &roots) err := msgpackrpc.CallWithCodec(codec, "ConnectCA.Roots", &structs.DCSpecificRequest{}, &roots)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, roots.Roots, 1) require.Len(t, roots.Roots, 1)
@ -824,6 +824,68 @@ func TestCAManager_Initialize_Vault_WithExternalTrustedCA(t *testing.T) {
// original certs from old signing cert should still verify // original certs from old signing cert should still verify
verifyLeafCert(t, roots.Roots[0], origLeaf) verifyLeafCert(t, roots.Roots[0], origLeaf)
}) })
runStep(t, "rotate root by changing the provider", func(t *testing.T) {
codec := rpcClient(t, serverDC1)
req := &structs.CARequest{
Op: structs.CAOpSetConfig,
Config: &structs.CAConfiguration{
Provider: "consul",
},
}
var resp error
err := msgpackrpc.CallWithCodec(codec, "ConnectCA.ConfigurationSet", req, &resp)
require.NoError(t, err)
require.Nil(t, resp)
roots = structs.IndexedCARoots{}
err = msgpackrpc.CallWithCodec(codec, "ConnectCA.Roots", &structs.DCSpecificRequest{}, &roots)
require.NoError(t, err)
require.Len(t, roots.Roots, 2)
active := roots.Active()
require.Len(t, active.IntermediateCerts, 1)
leafPEM := getLeafCert(t, codec, roots.TrustDomain, "dc1")
verifyLeafCert(t, roots.Active(), leafPEM)
// original certs from old root cert should still verify
verifyLeafCertWithRoots(t, roots, origLeaf)
})
runStep(t, "rotate to a different external root", func(t *testing.T) {
setupPrimaryCA(t, vclient, "pki-primary-2/", rootPEM)
codec := rpcClient(t, serverDC1)
req := &structs.CARequest{
Op: structs.CAOpSetConfig,
Config: &structs.CAConfiguration{
Provider: "vault",
Config: map[string]interface{}{
"Address": vault.Addr,
"Token": vault.RootToken,
"RootPKIPath": "pki-primary-2/",
"IntermediatePKIPath": "pki-intermediate-2/",
},
},
}
var resp error
err := msgpackrpc.CallWithCodec(codec, "ConnectCA.ConfigurationSet", req, &resp)
require.NoError(t, err)
require.Nil(t, resp)
roots = structs.IndexedCARoots{}
err = msgpackrpc.CallWithCodec(codec, "ConnectCA.Roots", &structs.DCSpecificRequest{}, &roots)
require.NoError(t, err)
require.Len(t, roots.Roots, 3)
active := roots.Active()
require.Len(t, active.IntermediateCerts, 2)
leafPEM := getLeafCert(t, codec, roots.TrustDomain, "dc1")
verifyLeafCert(t, roots.Active(), leafPEM)
// original certs from old root cert should still verify
verifyLeafCertWithRoots(t, roots, origLeaf)
})
} }
// renewLeafSigningCert mimics RenewIntermediate. This is unfortunate, but // renewLeafSigningCert mimics RenewIntermediate. This is unfortunate, but