From 15fbc2fd97e42e083fc391321ae11c7985615835 Mon Sep 17 00:00:00 2001 From: Kyle Havlovitz Date: Thu, 26 Apr 2018 23:02:18 -0700 Subject: [PATCH] Add more tests for built-in provider --- agent/consul/connect_ca_provider.go | 1 - agent/consul/connect_ca_provider_test.go | 150 +++++++++++++++++++++++ agent/consul/server_test.go | 2 + 3 files changed, 152 insertions(+), 1 deletion(-) diff --git a/agent/consul/connect_ca_provider.go b/agent/consul/connect_ca_provider.go index afb74fe78b..1c509e2b0a 100644 --- a/agent/consul/connect_ca_provider.go +++ b/agent/consul/connect_ca_provider.go @@ -341,7 +341,6 @@ func (c *ConsulCAProvider) incrementSerialIndex(providerState *structs.CAConsulP func generatePrivateKey() (string, error) { var pk *ecdsa.PrivateKey - // If we have no key, then create a new one. pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return "", fmt.Errorf("error generating private key: %s", err) diff --git a/agent/consul/connect_ca_provider_test.go b/agent/consul/connect_ca_provider_test.go index 583f91722d..ead41309f9 100644 --- a/agent/consul/connect_ca_provider_test.go +++ b/agent/consul/connect_ca_provider_test.go @@ -3,7 +3,9 @@ package consul import ( "os" "testing" + "time" + "github.com/hashicorp/consul/agent/connect" "github.com/hashicorp/consul/testrpc" "github.com/stretchr/testify/assert" ) @@ -25,8 +27,156 @@ func TestCAProvider_Bootstrap(t *testing.T) { root, err := provider.ActiveRoot() assert.NoError(err) + // Intermediate should be the same cert. + inter, err := provider.ActiveIntermediate() + assert.NoError(err) + + // Make sure we initialize without errors and that the + // root cert gets set to the active cert. state := s1.fsm.State() _, activeRoot, err := state.CARootActive(nil) assert.NoError(err) assert.Equal(root, activeRoot.RootCert) + assert.Equal(inter, activeRoot.RootCert) +} + +func TestCAProvider_Bootstrap_WithCert(t *testing.T) { + t.Parallel() + + // Make sure setting a custom private key/root cert works. + assert := assert.New(t) + rootCA := connect.TestCA(t, nil) + dir1, s1 := testServerWithConfig(t, func(c *Config) { + c.CAConfig.Config["PrivateKey"] = rootCA.SigningKey + c.CAConfig.Config["RootCert"] = rootCA.RootCert + }) + defer os.RemoveAll(dir1) + defer s1.Shutdown() + codec := rpcClient(t, s1) + defer codec.Close() + + testrpc.WaitForLeader(t, s1.RPC, "dc1") + + provider := s1.getCAProvider() + + root, err := provider.ActiveRoot() + assert.NoError(err) + + // Make sure we initialize without errors and that the + // root cert we provided gets set to the active cert. + state := s1.fsm.State() + _, activeRoot, err := state.CARootActive(nil) + assert.NoError(err) + assert.Equal(root, activeRoot.RootCert) + assert.Equal(rootCA.RootCert, activeRoot.RootCert) +} + +func TestCAProvider_SignLeaf(t *testing.T) { + t.Parallel() + + assert := assert.New(t) + dir1, s1 := testServer(t) + defer os.RemoveAll(dir1) + defer s1.Shutdown() + codec := rpcClient(t, s1) + defer codec.Close() + + testrpc.WaitForLeader(t, s1.RPC, "dc1") + + provider := s1.getCAProvider() + + spiffeService := &connect.SpiffeIDService{ + Host: s1.config.NodeName, + Namespace: "default", + Datacenter: s1.config.Datacenter, + Service: "foo", + } + + // Generate a leaf cert for the service. + { + raw, _ := connect.TestCSR(t, spiffeService) + + csr, err := connect.ParseCSR(raw) + assert.NoError(err) + + cert, err := provider.Sign(csr) + assert.NoError(err) + + parsed, err := connect.ParseCert(cert) + assert.NoError(err) + assert.Equal(parsed.URIs[0], spiffeService.URI()) + assert.Equal(parsed.Subject.CommonName, "foo") + assert.Equal(parsed.SerialNumber.Uint64(), uint64(1)) + + // Ensure the cert is valid now and expires within the correct limit. + assert.True(parsed.NotAfter.Sub(time.Now()) < 3*24*time.Hour) + assert.True(parsed.NotBefore.Before(time.Now())) + } + + // Generate a new cert for another service and make sure + // the serial number is incremented. + spiffeService.Service = "bar" + { + raw, _ := connect.TestCSR(t, spiffeService) + + csr, err := connect.ParseCSR(raw) + assert.NoError(err) + + cert, err := provider.Sign(csr) + assert.NoError(err) + + parsed, err := connect.ParseCert(cert) + assert.NoError(err) + assert.Equal(parsed.URIs[0], spiffeService.URI()) + assert.Equal(parsed.Subject.CommonName, "bar") + assert.Equal(parsed.SerialNumber.Uint64(), uint64(2)) + + // Ensure the cert is valid now and expires within the correct limit. + assert.True(parsed.NotAfter.Sub(time.Now()) < 3*24*time.Hour) + assert.True(parsed.NotBefore.Before(time.Now())) + } +} + +func TestCAProvider_CrossSignCA(t *testing.T) { + t.Parallel() + + assert := assert.New(t) + + // Make sure setting a custom private key/root cert works. + dir1, s1 := testServer(t) + defer os.RemoveAll(dir1) + defer s1.Shutdown() + codec := rpcClient(t, s1) + defer codec.Close() + + testrpc.WaitForLeader(t, s1.RPC, "dc1") + + provider := s1.getCAProvider() + + rootCA := connect.TestCA(t, nil) + rootPEM, err := provider.ActiveRoot() + assert.NoError(err) + root, err := connect.ParseCert(rootPEM) + assert.NoError(err) + + // Have the provider cross sign our new CA cert. + cert, err := connect.ParseCert(rootCA.RootCert) + assert.NoError(err) + oldSubject := cert.Subject.CommonName + xcPEM, err := provider.CrossSignCA(cert) + assert.NoError(err) + + xc, err := connect.ParseCert(xcPEM) + assert.NoError(err) + + // AuthorityKeyID and SubjectKeyID should be the signing root's. + assert.Equal(root.AuthorityKeyId, xc.AuthorityKeyId) + assert.Equal(root.SubjectKeyId, xc.SubjectKeyId) + + // Subject name should not have changed. + assert.NotEqual(root.Subject.CommonName, xc.Subject.CommonName) + assert.Equal(oldSubject, xc.Subject.CommonName) + + // Issuer should be the signing root. + assert.Equal(root.Issuer.CommonName, xc.Issuer.CommonName) } diff --git a/agent/consul/server_test.go b/agent/consul/server_test.go index 3afbb6f07b..84ec6743af 100644 --- a/agent/consul/server_test.go +++ b/agent/consul/server_test.go @@ -91,6 +91,8 @@ func testServerConfig(t *testing.T) (string, *Config) { // looks like several depend on it. config.RPCHoldTimeout = 5 * time.Second + config.ConnectEnabled = true + return dir, config }