connect/ca: add URI SAN support to the Vault provider

This commit is contained in:
Kyle Havlovitz 2018-06-14 14:20:10 -07:00 committed by Jack Pearkes
parent 7b0845ccde
commit a97c44c1ba
5 changed files with 89 additions and 4 deletions

View File

@ -113,6 +113,7 @@ func DevSource() Source {
connect = {
enabled = true
ca_provider = "consul"
}
performance = {
raft_multiplier = 1

View File

@ -163,8 +163,9 @@ func (v *VaultProvider) GenerateIntermediate() (string, error) {
spiffeID := connect.SpiffeIDSigning{ClusterID: v.clusterId, Domain: "consul"}
if role == nil {
_, err := v.client.Logical().Write(rolePath, map[string]interface{}{
"allowed_domains": spiffeID.Host(),
"allow_any_name": true,
"allowed_uri_sans": "spiffe://*",
"key_type": "any",
"max_ttl": "72h",
})
if err != nil {
@ -218,11 +219,10 @@ func (v *VaultProvider) Sign(csr *x509.CertificateRequest) (string, error) {
// Use the leaf cert role to sign a new cert for this CSR.
response, err := v.client.Logical().Write(v.config.IntermediatePKIPath+"sign/"+VaultCALeafCertRole, map[string]interface{}{
"csr": pemBuf.String(),
"common_name": csr.Subject.CommonName,
"csr": pemBuf.String(),
})
if err != nil {
return "", nil
return "", fmt.Errorf("error issuing cert: %v", err)
}
if response == nil || response.Data["certificate"] == "" || response.Data["issuing_ca"] == "" {
return "", fmt.Errorf("certificate info returned from Vault was blank")

View File

@ -1,9 +1,11 @@
package ca
import (
"fmt"
"io/ioutil"
"net"
"testing"
"time"
"github.com/hashicorp/consul/agent/connect"
vaultapi "github.com/hashicorp/vault/api"
@ -76,5 +78,73 @@ func TestVaultCAProvider_Bootstrap(t *testing.T) {
parsed, err := connect.ParseCert(cert)
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))
}
}
func TestVaultCAProvider_SignLeaf(t *testing.T) {
t.Parallel()
require := require.New(t)
provider, core, listener := testVaultCluster(t)
defer core.Shutdown()
defer listener.Close()
client, err := vaultapi.NewClient(&vaultapi.Config{
Address: "http://" + listener.Addr().String(),
})
require.NoError(err)
client.SetToken(provider.config.Token)
spiffeService := &connect.SpiffeIDService{
Host: "node1",
Namespace: "default",
Datacenter: "dc1",
Service: "foo",
}
// Generate a leaf cert for the service.
var firstSerial uint64
{
raw, _ := connect.TestCSR(t, spiffeService)
csr, err := connect.ParseCSR(raw)
require.NoError(err)
cert, err := provider.Sign(csr)
require.NoError(err)
parsed, err := connect.ParseCert(cert)
require.NoError(err)
require.Equal(parsed.URIs[0], spiffeService.URI())
require.Equal(parsed.Subject.CommonName, "foo")
firstSerial = parsed.SerialNumber.Uint64()
// Ensure the cert is valid now and expires within the correct limit.
require.True(parsed.NotAfter.Sub(time.Now()) < 3*24*time.Hour)
require.True(parsed.NotBefore.Before(time.Now()))
}
// Generate a new cert for another service and make sure
// the serial number is unique.
spiffeService.Service = "bar"
{
raw, _ := connect.TestCSR(t, spiffeService)
csr, err := connect.ParseCSR(raw)
require.NoError(err)
cert, err := provider.Sign(csr)
require.NoError(err)
parsed, err := connect.ParseCert(cert)
require.NoError(err)
require.Equal(parsed.URIs[0], spiffeService.URI())
require.Equal(parsed.Subject.CommonName, "bar")
require.NotEqual(firstSerial, parsed.SerialNumber.Uint64())
// Ensure the cert is valid now and expires within the correct limit.
require.True(parsed.NotAfter.Sub(time.Now()) < 3*24*time.Hour)
require.True(parsed.NotBefore.Before(time.Now()))
}
}

View File

@ -5,16 +5,24 @@ import (
"crypto"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"net/url"
)
// 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) (string, error) {
serviceId, ok := uri.(*SpiffeIDService)
if !ok {
return "", fmt.Errorf("SPIFFE ID in CSR must be a service ID")
}
template := &x509.CertificateRequest{
URIs: []*url.URL{uri.URI()},
SignatureAlgorithm: x509.ECDSAWithSHA256,
Subject: pkix.Name{CommonName: serviceId.Service},
}
// Create the CSR itself

View File

@ -201,9 +201,15 @@ 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) {
serviceId, ok := uri.(*SpiffeIDService)
if !ok {
t.Fatalf("SPIFFE ID in CSR must be a service ID")
}
template := &x509.CertificateRequest{
URIs: []*url.URL{uri.URI()},
SignatureAlgorithm: x509.ECDSAWithSHA256,
Subject: pkix.Name{CommonName: serviceId.Service},
}
// Create the private key we'll use