From 9b1ac4e7ef9e468642941e69bb545c67e66e1810 Mon Sep 17 00:00:00 2001 From: tradel Date: Tue, 27 Aug 2019 14:15:10 -0700 Subject: [PATCH] add subject names to issued certs --- agent/connect/ca/provider_consul.go | 52 ++++++++++++++---------- agent/connect/ca/provider_consul_test.go | 36 ++++++++-------- agent/connect/ca/provider_vault.go | 42 +++++++++++-------- agent/connect/ca/provider_vault_test.go | 16 ++++---- agent/connect/csr.go | 7 ++-- agent/connect/testing_ca.go | 3 +- 6 files changed, 88 insertions(+), 68 deletions(-) diff --git a/agent/connect/ca/provider_consul.go b/agent/connect/ca/provider_consul.go index 730a06e690..7d87890e88 100644 --- a/agent/connect/ca/provider_consul.go +++ b/agent/connect/ca/provider_consul.go @@ -28,6 +28,8 @@ type ConsulProvider struct { config *structs.ConsulCAProviderConfig id string clusterID string + dcName string + domain string isRoot bool spiffeID *connect.SpiffeIDSigning @@ -40,7 +42,8 @@ type ConsulProviderStateDelegate interface { } // Configure sets up the provider using the given configuration. -func (c *ConsulProvider) Configure(clusterID string, isRoot bool, rawConfig map[string]interface{}) error { +func (c *ConsulProvider) Configure(clusterID string, datacenterName string, dnsDomain string, + isRoot bool, rawConfig map[string]interface{}) error { // Parse the raw config and update our ID. config, err := ParseConsulCAConfig(rawConfig) if err != nil { @@ -49,9 +52,15 @@ func (c *ConsulProvider) Configure(clusterID string, isRoot bool, rawConfig map[ c.config = config hash := sha256.Sum256([]byte(fmt.Sprintf("%s,%s,%v", config.PrivateKey, config.RootCert, isRoot))) c.id = strings.Replace(fmt.Sprintf("% x", hash), " ", ":", -1) - c.clusterID = clusterID c.isRoot = isRoot c.spiffeID = connect.SpiffeIDSigningForCluster(&structs.CAConfiguration{ClusterID: clusterID}) + c.dcName = datacenterName + c.domain = dnsDomain + + c.clusterID = clusterID + if len(c.clusterID) > 8 { + c.clusterID = c.clusterID[:8] + } // Exit early if the state store has an entry for this provider's config. _, providerState, err := c.Delegate.State().CAProviderState(c.id) @@ -189,7 +198,8 @@ func (c *ConsulProvider) GenerateIntermediateCSR() (string, error) { return "", err } - csr, err := connect.CreateCACSR(c.spiffeID, signer) + commonName := fmt.Sprintf("intermediate.ca.%s.%s.%s", c.clusterID, c.dcName, c.domain) + csr, err := connect.CreateCACSR(c.spiffeID, commonName, signer) if err != nil { return "", err } @@ -344,22 +354,22 @@ func (c *ConsulProvider) Sign(csr *x509.CertificateRequest) (string, error) { return "", err } - // Parse the SPIFFE ID - spiffeId, err := connect.ParseCertURI(csr.URIs[0]) - if err != nil { - return "", err - } - - subject := "" - switch id := spiffeId.(type) { - case *connect.SpiffeIDService: - subject = id.Service - case *connect.SpiffeIDAgent: - subject = id.Agent - default: - return "", fmt.Errorf("SPIFFE ID in CSR must be a service ID") - } + //// Parse the SPIFFE ID + //spiffeId, err := connect.ParseCertURI(csr.URIs[0]) + //if err != nil { + // return "", err + //} + //subjectName := "" + //switch id := spiffeId.(type) { + //case *connect.SpiffeIDService: + // subjectName = fmt.Sprintf("%s.%s.service.%s.%s", id.Host, id.Service, id.Datacenter, c.domain) + //case *connect.SpiffeIDAgent: + // subjectName = fmt.Sprintf("%s.agent.%s.%s", id.Agent, id.Datacenter, c.domain) + //default: + // return "", fmt.Errorf("SPIFFE ID in CSR must be a service ID") + //} + // // Parse the CA cert certPEM, err := c.ActiveIntermediate() if err != nil { @@ -379,7 +389,7 @@ func (c *ConsulProvider) Sign(csr *x509.CertificateRequest) (string, error) { effectiveNow := time.Now().Add(-1 * time.Minute) template := x509.Certificate{ SerialNumber: sn, - Subject: pkix.Name{CommonName: subject}, + Subject: csr.Subject, URIs: csr.URIs, Signature: csr.Signature, SignatureAlgorithm: csr.SignatureAlgorithm, @@ -617,7 +627,7 @@ func (c *ConsulProvider) generateCA(privateKey string, sn uint64) (string, error return "", fmt.Errorf("error parsing private key %q: %s", privateKey, err) } - name := fmt.Sprintf("Consul CA %d", sn) + commonName := fmt.Sprintf("root.ca.%s.%s.%s", c.clusterID, c.dcName, c.domain) // The URI (SPIFFE compatible) for the cert id := connect.SpiffeIDSigningForCluster(config) @@ -631,7 +641,7 @@ func (c *ConsulProvider) generateCA(privateKey string, sn uint64) (string, error serialNum.SetUint64(sn) template := x509.Certificate{ SerialNumber: serialNum, - Subject: pkix.Name{CommonName: name}, + Subject: pkix.Name{CommonName: commonName}, URIs: []*url.URL{id.URI()}, BasicConstraintsValid: true, KeyUsage: x509.KeyUsageCertSign | diff --git a/agent/connect/ca/provider_consul_test.go b/agent/connect/ca/provider_consul_test.go index 774dcc2649..1338520451 100644 --- a/agent/connect/ca/provider_consul_test.go +++ b/agent/connect/ca/provider_consul_test.go @@ -79,7 +79,7 @@ func TestConsulCAProvider_Bootstrap(t *testing.T) { delegate := newMockDelegate(t, conf) provider := &ConsulProvider{Delegate: delegate} - require.NoError(provider.Configure(conf.ClusterID, true, conf.Config)) + require.NoError(provider.Configure(conf.ClusterID, "dc1", "consul", true, conf.Config)) require.NoError(provider.GenerateRoot()) root, err := provider.ActiveRoot() @@ -110,7 +110,7 @@ func TestConsulCAProvider_Bootstrap_WithCert(t *testing.T) { delegate := newMockDelegate(t, conf) provider := &ConsulProvider{Delegate: delegate} - require.NoError(provider.Configure(conf.ClusterID, true, conf.Config)) + require.NoError(provider.Configure(conf.ClusterID, "dc1", "consul", true, conf.Config)) require.NoError(provider.GenerateRoot()) root, err := provider.ActiveRoot() @@ -127,7 +127,7 @@ func TestConsulCAProvider_SignLeaf(t *testing.T) { delegate := newMockDelegate(t, conf) provider := &ConsulProvider{Delegate: delegate} - require.NoError(provider.Configure(conf.ClusterID, true, conf.Config)) + require.NoError(provider.Configure(conf.ClusterID, "dc1", "consul", true, conf.Config)) require.NoError(provider.GenerateRoot()) spiffeService := &connect.SpiffeIDService{ @@ -139,7 +139,7 @@ func TestConsulCAProvider_SignLeaf(t *testing.T) { // Generate a leaf cert for the service. { - raw, _ := connect.TestCSR(t, spiffeService) + raw, _ := connect.TestCSR(t, spiffeService, "node1.foo.service.dc1.consul.") csr, err := connect.ParseCSR(raw) require.NoError(err) @@ -149,8 +149,8 @@ func TestConsulCAProvider_SignLeaf(t *testing.T) { parsed, err := connect.ParseCert(cert) require.NoError(err) - require.Equal(parsed.URIs[0], spiffeService.URI()) - require.Equal(parsed.Subject.CommonName, "foo") + require.Equal(spiffeService.URI(), parsed.URIs[0]) + require.Equal("node1.foo.service.dc1.consul.", parsed.Subject.CommonName) require.Equal(uint64(2), parsed.SerialNumber.Uint64()) // Ensure the cert is valid now and expires within the correct limit. @@ -163,7 +163,7 @@ func TestConsulCAProvider_SignLeaf(t *testing.T) { // the serial number is incremented. spiffeService.Service = "bar" { - raw, _ := connect.TestCSR(t, spiffeService) + raw, _ := connect.TestCSR(t, spiffeService, "node1.bar.service.dc1.consul.") csr, err := connect.ParseCSR(raw) require.NoError(err) @@ -173,8 +173,8 @@ func TestConsulCAProvider_SignLeaf(t *testing.T) { parsed, err := connect.ParseCert(cert) require.NoError(err) - require.Equal(parsed.URIs[0], spiffeService.URI()) - require.Equal(parsed.Subject.CommonName, "bar") + require.Equal(spiffeService.URI(), parsed.URIs[0]) + require.Equal("node1.bar.service.dc1.consul.", parsed.Subject.CommonName) require.Equal(parsed.SerialNumber.Uint64(), uint64(2)) // Ensure the cert is valid now and expires within the correct limit. @@ -189,7 +189,7 @@ func TestConsulCAProvider_SignLeaf(t *testing.T) { } // Generate a leaf cert for an agent. { - raw, _ := connect.TestCSR(t, spiffeAgent) + raw, _ := connect.TestCSR(t, spiffeAgent, "uuid.agent.dc1.consul.") csr, err := connect.ParseCSR(raw) require.NoError(err) @@ -200,7 +200,7 @@ func TestConsulCAProvider_SignLeaf(t *testing.T) { parsed, err := connect.ParseCert(cert) require.NoError(err) require.Equal(spiffeAgent.URI(), parsed.URIs[0]) - require.Equal("uuid", parsed.Subject.CommonName) + require.Equal("uuid.agent.dc1.consul.", parsed.Subject.CommonName) require.Equal(uint64(2), parsed.SerialNumber.Uint64()) // Ensure the cert is valid now and expires within the correct limit. @@ -218,14 +218,14 @@ func TestConsulCAProvider_CrossSignCA(t *testing.T) { conf1 := testConsulCAConfig() delegate1 := newMockDelegate(t, conf1) provider1 := &ConsulProvider{Delegate: delegate1} - require.NoError(provider1.Configure(conf1.ClusterID, true, conf1.Config)) + require.NoError(provider1.Configure(conf1.ClusterID, "dc1", "consul", true, conf1.Config)) require.NoError(provider1.GenerateRoot()) conf2 := testConsulCAConfig() conf2.CreateIndex = 10 delegate2 := newMockDelegate(t, conf2) provider2 := &ConsulProvider{Delegate: delegate2} - require.NoError(provider2.Configure(conf2.ClusterID, true, conf2.Config)) + require.NoError(provider2.Configure(conf2.ClusterID, "dc2", "consul", true, conf2.Config)) require.NoError(provider2.GenerateRoot()) testCrossSignProviders(t, provider1, provider2) @@ -274,7 +274,7 @@ func testCrossSignProviders(t *testing.T, provider1, provider2 Provider) { Datacenter: "dc1", Service: "foo", } - raw, _ := connect.TestCSR(t, spiffeService) + raw, _ := connect.TestCSR(t, spiffeService, "node1.foo.service.dc1.consul.") leafCsr, err := connect.ParseCSR(raw) require.NoError(err) @@ -310,14 +310,14 @@ func TestConsulProvider_SignIntermediate(t *testing.T) { conf1 := testConsulCAConfig() delegate1 := newMockDelegate(t, conf1) provider1 := &ConsulProvider{Delegate: delegate1} - require.NoError(provider1.Configure(conf1.ClusterID, true, conf1.Config)) + require.NoError(provider1.Configure(conf1.ClusterID, "dc1", "consul", true, conf1.Config)) require.NoError(provider1.GenerateRoot()) conf2 := testConsulCAConfig() conf2.CreateIndex = 10 delegate2 := newMockDelegate(t, conf2) provider2 := &ConsulProvider{Delegate: delegate2} - require.NoError(provider2.Configure(conf2.ClusterID, false, conf2.Config)) + require.NoError(provider2.Configure(conf2.ClusterID, "dc1", "consul", false, conf2.Config)) testSignIntermediateCrossDC(t, provider1, provider2) } @@ -347,7 +347,7 @@ func testSignIntermediateCrossDC(t *testing.T, provider1, provider2 Provider) { Datacenter: "dc1", Service: "foo", } - raw, _ := connect.TestCSR(t, spiffeService) + raw, _ := connect.TestCSR(t, spiffeService, "node1.foo.service.dc1.consul.") leafCsr, err := connect.ParseCSR(raw) require.NoError(err) @@ -392,7 +392,7 @@ func TestConsulCAProvider_MigrateOldID(t *testing.T) { require.NotNil(providerState) provider := &ConsulProvider{Delegate: delegate} - require.NoError(provider.Configure(conf.ClusterID, true, conf.Config)) + require.NoError(provider.Configure(conf.ClusterID, "dc1", "consul", true, conf.Config)) require.NoError(provider.GenerateRoot()) // After running Configure, the old ID entry should be gone. diff --git a/agent/connect/ca/provider_vault.go b/agent/connect/ca/provider_vault.go index f1fef4706c..96a8a20198 100644 --- a/agent/connect/ca/provider_vault.go +++ b/agent/connect/ca/provider_vault.go @@ -25,7 +25,9 @@ type VaultProvider struct { config *structs.VaultCAProviderConfig client *vaultapi.Client isRoot bool - clusterId string + clusterID string + dcName string + domain string } func vaultTLSConfig(config *structs.VaultCAProviderConfig) *vaultapi.TLSConfig { @@ -40,7 +42,8 @@ func vaultTLSConfig(config *structs.VaultCAProviderConfig) *vaultapi.TLSConfig { } // Configure sets up the provider using the given configuration. -func (v *VaultProvider) Configure(clusterId string, isRoot bool, rawConfig map[string]interface{}) error { +func (v *VaultProvider) Configure(clusterId string, datacenterName string, dnsDomain string, + isRoot bool, rawConfig map[string]interface{}) error { config, err := ParseVaultCAConfig(rawConfig) if err != nil { return err @@ -62,7 +65,13 @@ func (v *VaultProvider) Configure(clusterId string, isRoot bool, rawConfig map[s v.config = config v.client = client v.isRoot = isRoot - v.clusterId = clusterId + v.dcName = datacenterName + v.domain = dnsDomain + + v.clusterID = clusterId + if len(v.clusterID) > 8 { + v.clusterID = v.clusterID[:8] + } return nil } @@ -96,13 +105,10 @@ func (v *VaultProvider) GenerateRoot() error { fallthrough case ErrBackendNotInitialized: - spiffeID := connect.SpiffeIDSigning{ClusterID: v.clusterId, Domain: "consul"} - uuid, err := uuid.GenerateUUID() - if err != nil { - return err - } + uuid, _ := uuid.GenerateUUID() + spiffeID := connect.SpiffeIDSigning{ClusterID: v.clusterID, Domain: v.domain} _, err = v.client.Logical().Write(v.config.RootPKIPath+"root/generate/internal", map[string]interface{}{ - "common_name": fmt.Sprintf("Vault CA Root Authority %s", uuid), + "common_name": fmt.Sprintf("%s.root.ca.%s.%s.%s", uuid[:8], v.clusterID, v.dcName, v.domain), "uri_sans": spiffeID.URI().String(), "key_type": v.config.PrivateKeyType, "key_bits": v.config.PrivateKeyBits, @@ -158,15 +164,16 @@ func (v *VaultProvider) generateIntermediateCSR() (string, error) { if err != nil { return "", err } - spiffeID := connect.SpiffeIDSigning{ClusterID: v.clusterId, Domain: "consul"} + spiffeID := connect.SpiffeIDSigning{ClusterID: v.clusterID, Domain: "consul"} if role == nil { _, err := v.client.Logical().Write(rolePath, map[string]interface{}{ - "allow_any_name": true, - "allowed_uri_sans": "spiffe://*", - "key_type": "any", - "max_ttl": v.config.LeafCertTTL.String(), - "no_store": true, - "require_cn": false, + "allow_any_name": true, + "enforce_hostnames": false, + "allowed_uri_sans": "spiffe://*", + "key_type": "any", + "max_ttl": v.config.LeafCertTTL.String(), + "no_store": true, + "require_cn": false, }) if err != nil { return "", err @@ -174,8 +181,9 @@ func (v *VaultProvider) generateIntermediateCSR() (string, error) { } // Generate a new intermediate CSR for the root to sign. + uuid, _ := uuid.GenerateUUID() data, err := v.client.Logical().Write(v.config.IntermediatePKIPath+"intermediate/generate/internal", map[string]interface{}{ - "common_name": "Vault CA Intermediate Authority", + "common_name": fmt.Sprintf("%s.intermediate.ca.%s.%s.%s", uuid[:8], v.clusterID, v.dcName, v.domain), "key_type": v.config.PrivateKeyType, "key_bits": v.config.PrivateKeyBits, "uri_sans": spiffeID.URI().String(), diff --git a/agent/connect/ca/provider_vault_test.go b/agent/connect/ca/provider_vault_test.go index b0ddaa4112..3c73a7bc53 100644 --- a/agent/connect/ca/provider_vault_test.go +++ b/agent/connect/ca/provider_vault_test.go @@ -48,7 +48,7 @@ func testVaultClusterWithConfig(t *testing.T, isRoot bool, rawConf map[string]in require := require.New(t) provider := &VaultProvider{} - require.NoError(provider.Configure("asdf", isRoot, conf)) + require.NoError(provider.Configure("asdf", "dc1", "consul", isRoot, conf)) if isRoot { require.NoError(provider.GenerateRoot()) _, err := provider.GenerateIntermediate() @@ -120,7 +120,7 @@ func TestVaultCAProvider_Bootstrap(t *testing.T) { require.NoError(err) require.True(parsed.IsCA) require.Len(parsed.URIs, 1) - require.Equal(parsed.URIs[0].String(), fmt.Sprintf("spiffe://%s.consul", provider.clusterId)) + require.Equal(parsed.URIs[0].String(), fmt.Sprintf("spiffe://%s.consul", provider.clusterID)) } } @@ -149,7 +149,7 @@ func TestVaultCAProvider_SignLeaf(t *testing.T) { // Generate a leaf cert for the service. var firstSerial uint64 { - raw, _ := connect.TestCSR(t, spiffeService) + raw, _ := connect.TestCSR(t, spiffeService, "node1.foo.service.dc1.consul.") csr, err := connect.ParseCSR(raw) require.NoError(err) @@ -159,7 +159,7 @@ func TestVaultCAProvider_SignLeaf(t *testing.T) { parsed, err := connect.ParseCert(cert) require.NoError(err) - require.Equal(parsed.URIs[0], spiffeService.URI()) + require.Equal(spiffeService.URI(), parsed.URIs[0]) firstSerial = parsed.SerialNumber.Uint64() // Ensure the cert is valid now and expires within the correct limit. @@ -172,7 +172,7 @@ func TestVaultCAProvider_SignLeaf(t *testing.T) { // the serial number is unique. spiffeService.Service = "bar" { - raw, _ := connect.TestCSR(t, spiffeService) + raw, _ := connect.TestCSR(t, spiffeService, "node1.bar.service.dc1.consul.") csr, err := connect.ParseCSR(raw) require.NoError(err) @@ -182,7 +182,7 @@ func TestVaultCAProvider_SignLeaf(t *testing.T) { parsed, err := connect.ParseCert(cert) require.NoError(err) - require.Equal(parsed.URIs[0], spiffeService.URI()) + require.Equal(spiffeService.URI(), parsed.URIs[0]) require.NotEqual(firstSerial, parsed.SerialNumber.Uint64()) // Ensure the cert is valid now and expires within the correct limit. @@ -233,7 +233,7 @@ func TestVaultProvider_SignIntermediateConsul(t *testing.T) { conf := testConsulCAConfig() delegate := newMockDelegate(t, conf) provider2 := &ConsulProvider{Delegate: delegate} - require.NoError(provider2.Configure(conf.ClusterID, false, conf.Config)) + require.NoError(provider2.Configure(conf.ClusterID, "dc2", "consul", false, conf.Config)) testSignIntermediateCrossDC(t, provider1, provider2) } @@ -243,7 +243,7 @@ func TestVaultProvider_SignIntermediateConsul(t *testing.T) { conf := testConsulCAConfig() delegate := newMockDelegate(t, conf) provider1 := &ConsulProvider{Delegate: delegate} - require.NoError(provider1.Configure(conf.ClusterID, true, conf.Config)) + require.NoError(provider1.Configure(conf.ClusterID, "dc1", "consul", true, conf.Config)) require.NoError(provider1.GenerateRoot()) provider2, core, listener := testVaultClusterWithConfig(t, false, nil) diff --git a/agent/connect/csr.go b/agent/connect/csr.go index 61a73ed33a..deed31433d 100644 --- a/agent/connect/csr.go +++ b/agent/connect/csr.go @@ -13,8 +13,9 @@ import ( // CreateCSR returns a CSR to sign the given service along with the PEM-encoded // private key for this certificate. -func CreateCSR(uri CertURI, privateKey crypto.Signer, extensions ...pkix.Extension) (string, error) { +func CreateCSR(uri CertURI, commonName string, privateKey crypto.Signer, extensions ...pkix.Extension) (string, error) { template := &x509.CertificateRequest{ + Subject: pkix.Name{CommonName: commonName}, URIs: []*url.URL{uri.URI()}, SignatureAlgorithm: x509.ECDSAWithSHA256, ExtraExtensions: extensions, @@ -37,13 +38,13 @@ func CreateCSR(uri CertURI, privateKey crypto.Signer, extensions ...pkix.Extensi // CreateCSR returns a CA CSR to sign the given service along with the PEM-encoded // private key for this certificate. -func CreateCACSR(uri CertURI, privateKey crypto.Signer) (string, error) { +func CreateCACSR(uri CertURI, commonName string, privateKey crypto.Signer) (string, error) { ext, err := CreateCAExtension() if err != nil { return "", err } - return CreateCSR(uri, privateKey, ext) + return CreateCSR(uri, commonName, privateKey, ext) } // CreateCAExtension creates a pkix.Extension for the x509 Basic Constraints diff --git a/agent/connect/testing_ca.go b/agent/connect/testing_ca.go index a1deeeedbf..3af0b929b3 100644 --- a/agent/connect/testing_ca.go +++ b/agent/connect/testing_ca.go @@ -227,8 +227,9 @@ func TestLeaf(t testing.T, service string, root *structs.CARoot) (string, string // TestCSR returns a CSR to sign the given service along with the PEM-encoded // private key for this certificate. -func TestCSR(t testing.T, uri CertURI) (string, string) { +func TestCSR(t testing.T, uri CertURI, commonName string) (string, string) { template := &x509.CertificateRequest{ + Subject: pkix.Name{CommonName: commonName}, URIs: []*url.URL{uri.URI()}, SignatureAlgorithm: x509.ECDSAWithSHA256, }