connect/ca: fix vault provider URI SANs and test

This commit is contained in:
Kyle Havlovitz 2018-06-14 10:56:17 -07:00 committed by Jack Pearkes
parent 1a8ac686b2
commit 226a59215d
4 changed files with 46 additions and 41 deletions

View File

@ -9,6 +9,7 @@ import (
"net/http" "net/http"
"strings" "strings"
"github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
vaultapi "github.com/hashicorp/vault/api" vaultapi "github.com/hashicorp/vault/api"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
@ -29,24 +30,22 @@ type VaultProvider struct {
// backends mounted and initialized. If the root backend is not set up already, // backends mounted and initialized. If the root backend is not set up already,
// it will be mounted/generated as needed, but any existing state will not be // it will be mounted/generated as needed, but any existing state will not be
// overwritten. // overwritten.
func NewVaultProvider(rawConfig map[string]interface{}, clusterId string, client *vaultapi.Client) (*VaultProvider, error) { func NewVaultProvider(rawConfig map[string]interface{}, clusterId string) (*VaultProvider, error) {
conf, err := ParseVaultCAConfig(rawConfig) conf, err := ParseVaultCAConfig(rawConfig)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// todo(kyhavlov): figure out the right way to pass the TLS config // todo(kyhavlov): figure out the right way to pass the TLS config
if client == nil { clientConf := &vaultapi.Config{
clientConf := &vaultapi.Config{ Address: conf.Address,
Address: conf.Address,
}
client, err = vaultapi.NewClient(clientConf)
if err != nil {
return nil, err
}
client.SetToken(conf.Token)
} }
client, err := vaultapi.NewClient(clientConf)
if err != nil {
return nil, err
}
client.SetToken(conf.Token)
provider := &VaultProvider{ provider := &VaultProvider{
config: conf, config: conf,
@ -72,8 +71,9 @@ func NewVaultProvider(rawConfig map[string]interface{}, clusterId string, client
fallthrough fallthrough
case ErrBackendNotInitialized: case ErrBackendNotInitialized:
spiffeID := connect.SpiffeIDSigning{ClusterID: clusterId, Domain: "consul"}
_, err := client.Logical().Write(conf.RootPKIPath+"root/generate/internal", map[string]interface{}{ _, err := client.Logical().Write(conf.RootPKIPath+"root/generate/internal", map[string]interface{}{
"alt_names": fmt.Sprintf("URI:spiffe://%s.consul", clusterId), "uri_sans": spiffeID.URI().String(),
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -160,10 +160,12 @@ func (v *VaultProvider) GenerateIntermediate() (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
spiffeID := connect.SpiffeIDSigning{ClusterID: v.clusterId, Domain: "consul"}
if role == nil { if role == nil {
_, err := v.client.Logical().Write(rolePath, map[string]interface{}{ _, err := v.client.Logical().Write(rolePath, map[string]interface{}{
"allowed_domains": fmt.Sprintf("%s.consul", v.clusterId), "allowed_domains": spiffeID.Host(),
"max_ttl": "72h", "allowed_uri_sans": "spiffe://*",
"max_ttl": "72h",
}) })
if err != nil { if err != nil {
return "", err return "", err
@ -173,7 +175,7 @@ func (v *VaultProvider) GenerateIntermediate() (string, error) {
// Generate a new intermediate CSR for the root to sign. // Generate a new intermediate CSR for the root to sign.
csr, err := v.client.Logical().Write(v.config.IntermediatePKIPath+"intermediate/generate/internal", map[string]interface{}{ csr, err := v.client.Logical().Write(v.config.IntermediatePKIPath+"intermediate/generate/internal", map[string]interface{}{
"common_name": "Vault CA Intermediate Authority", "common_name": "Vault CA Intermediate Authority",
"alt_names": fmt.Sprintf("URI:spiffe://%s.consul", v.clusterId), "uri_sans": spiffeID.URI().String(),
}) })
if err != nil { if err != nil {
return "", err return "", err

View File

@ -1,52 +1,51 @@
package ca package ca
import ( import (
"fmt"
"io/ioutil" "io/ioutil"
"net"
"testing" "testing"
"github.com/hashicorp/consul/agent/connect" "github.com/hashicorp/consul/agent/connect"
vaultapi "github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/builtin/logical/pki" "github.com/hashicorp/vault/builtin/logical/pki"
vaulthttp "github.com/hashicorp/vault/http" vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/vault" "github.com/hashicorp/vault/vault"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func testVaultCluster(t *testing.T) (*VaultProvider, *vault.TestCluster) { func testVaultCluster(t *testing.T) (*VaultProvider, *vault.Core, net.Listener) {
coreConfig := &vault.CoreConfig{ if err := vault.AddTestLogicalBackend("pki", pki.Factory); err != nil {
LogicalBackends: map[string]logical.Factory{ t.Fatal(err)
"pki": pki.Factory,
},
} }
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{ core, _, token := vault.TestCoreUnsealedRaw(t)
HandlerFunc: vaulthttp.Handler,
NumCores: 1,
})
cluster.Start()
client := cluster.Cores[0].Client ln, addr := vaulthttp.TestServer(t, core)
provider, err := NewVaultProvider(map[string]interface{}{ provider, err := NewVaultProvider(map[string]interface{}{
"Address": client.Address(), "Address": addr,
"Token": cluster.RootToken, "Token": token,
"RootPKIPath": "pki-root/", "RootPKIPath": "pki-root/",
"IntermediatePKIPath": "pki-intermediate/", "IntermediatePKIPath": "pki-intermediate/",
}, "asdf", client) }, "asdf")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
return provider, cluster return provider, core, ln
} }
func TestVaultCAProvider_Bootstrap(t *testing.T) { func TestVaultCAProvider_Bootstrap(t *testing.T) {
t.Parallel() t.Parallel()
require := require.New(t) require := require.New(t)
provider, vaultCluster := testVaultCluster(t) provider, core, listener := testVaultCluster(t)
defer vaultCluster.Cleanup() defer core.Shutdown()
client := vaultCluster.Cores[0].Client defer listener.Close()
client, err := vaultapi.NewClient(&vaultapi.Config{
Address: "http://" + listener.Addr().String(),
})
require.NoError(err)
client.SetToken(provider.config.Token)
cases := []struct { cases := []struct {
certFunc func() (string, error) certFunc func() (string, error)
@ -66,16 +65,16 @@ func TestVaultCAProvider_Bootstrap(t *testing.T) {
for _, tc := range cases { for _, tc := range cases {
cert, err := tc.certFunc() cert, err := tc.certFunc()
require.NoError(err) require.NoError(err)
req := client.NewRequest("GET", "v1/"+tc.backendPath+"ca/pem") req := client.NewRequest("GET", "/v1/"+tc.backendPath+"ca/pem")
resp, err := client.RawRequest(req) resp, err := client.RawRequest(req)
require.NoError(err) require.NoError(err)
bytes, err := ioutil.ReadAll(resp.Body) bytes, err := ioutil.ReadAll(resp.Body)
require.NoError(err) require.NoError(err)
require.Equal(cert, string(bytes)) require.Equal(cert, string(bytes))
// Should be a valid cert // Should be a valid CA cert
parsed, err := connect.ParseCert(cert) parsed, err := connect.ParseCert(cert)
require.NoError(err) require.NoError(err)
require.Equal(parsed.URIs[0].String(), fmt.Sprintf("spiffe://%s.consul", provider.clusterId)) require.True(parsed.IsCA)
} }
} }

View File

@ -83,11 +83,15 @@ func fixupConfig(conf *structs.CAConfiguration) {
if raw, ok := v.([]uint8); ok { if raw, ok := v.([]uint8); ok {
conf.Config[k] = ca.Uint8ToString(raw) conf.Config[k] = ca.Uint8ToString(raw)
} }
// todo(kyhavlov): should we be hiding this and the vault token? switch conf.Provider {
if conf.Provider == structs.ConsulCAProvider { case structs.ConsulCAProvider:
if v, ok := conf.Config["PrivateKey"]; ok && v != "" { if v, ok := conf.Config["PrivateKey"]; ok && v != "" {
conf.Config["PrivateKey"] = "hidden" conf.Config["PrivateKey"] = "hidden"
} }
case structs.VaultCAProvider:
if v, ok := conf.Config["Token"]; ok && v != "" {
conf.Config["Token"] = "hidden"
}
} }
} }
} }

View File

@ -512,7 +512,7 @@ func (s *Server) createCAProvider(conf *structs.CAConfiguration) (ca.Provider, e
case structs.ConsulCAProvider: case structs.ConsulCAProvider:
return ca.NewConsulProvider(conf.Config, &consulCADelegate{s}) return ca.NewConsulProvider(conf.Config, &consulCADelegate{s})
case structs.VaultCAProvider: case structs.VaultCAProvider:
return ca.NewVaultProvider(conf.Config, conf.ClusterID, nil) return ca.NewVaultProvider(conf.Config, conf.ClusterID)
default: default:
return nil, fmt.Errorf("unknown CA provider %q", conf.Provider) return nil, fmt.Errorf("unknown CA provider %q", conf.Provider)
} }