add subject names to issued certs

This commit is contained in:
tradel 2019-08-27 14:15:10 -07:00
parent 1c9b271731
commit 82544b64e5
6 changed files with 88 additions and 68 deletions

View file

@ -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 |

View file

@ -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.

View file

@ -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(),

View file

@ -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)

View file

@ -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

View file

@ -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,
}