connect: Implement NeedsLogger interface for CA providers (#6556)
* add NeedsLogger to Provider interface * implements NeedsLogger in default provider * pass logger through to provider * test for proper operation of NeedsLogger * remove public testServer function * Switch test to actually assert on logging output rather than reflection. --amend * Ooops actually set the logger in all the places we need it - CA config set wasn't and causing segfault * Fix all the other places in tests where we set the logger * Add TODO comment
This commit is contained in:
parent
e100fda218
commit
19a3892f71
|
@ -2,6 +2,7 @@ package ca
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate mockery -name Provider -inpkg
|
//go:generate mockery -name Provider -inpkg
|
||||||
|
@ -74,3 +75,11 @@ type Provider interface {
|
||||||
// created for an intermediate CA.
|
// created for an intermediate CA.
|
||||||
Cleanup() error
|
Cleanup() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NeedsLogger is an optional interface that allows a CA provider to use the
|
||||||
|
// Consul logger to output diagnostic messages.
|
||||||
|
type NeedsLogger interface {
|
||||||
|
// SetLogger will pass a configured Logger to the provider.
|
||||||
|
// TODO(hclog) convert this to an hclog.Logger.
|
||||||
|
SetLogger(logger *log.Logger)
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -29,6 +30,7 @@ type ConsulProvider struct {
|
||||||
clusterID string
|
clusterID string
|
||||||
isRoot bool
|
isRoot bool
|
||||||
spiffeID *connect.SpiffeIDSigning
|
spiffeID *connect.SpiffeIDSigning
|
||||||
|
logger *log.Logger
|
||||||
|
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
@ -106,6 +108,9 @@ func (c *ConsulProvider) Configure(clusterID string, isRoot bool, rawConfig map[
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.logger.Printf("[DEBUG] consul CA provider configured ID=%s isRoot=%v",
|
||||||
|
c.id, c.isRoot)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,8 +551,8 @@ func (c *ConsulProvider) CrossSignCA(cert *x509.Certificate) (string, error) {
|
||||||
// getState returns the current provider state from the state delegate, and returns
|
// getState returns the current provider state from the state delegate, and returns
|
||||||
// ErrNotInitialized if no entry is found.
|
// ErrNotInitialized if no entry is found.
|
||||||
func (c *ConsulProvider) getState() (uint64, *structs.CAConsulProviderState, error) {
|
func (c *ConsulProvider) getState() (uint64, *structs.CAConsulProviderState, error) {
|
||||||
state := c.Delegate.State()
|
stateStore := c.Delegate.State()
|
||||||
idx, providerState, err := state.CAProviderState(c.id)
|
idx, providerState, err := stateStore.CAProviderState(c.id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
@ -576,8 +581,8 @@ func (c *ConsulProvider) incrementProviderIndex(providerState *structs.CAConsulP
|
||||||
|
|
||||||
// generateCA makes a new root CA using the current private key
|
// generateCA makes a new root CA using the current private key
|
||||||
func (c *ConsulProvider) generateCA(privateKey string, sn uint64) (string, error) {
|
func (c *ConsulProvider) generateCA(privateKey string, sn uint64) (string, error) {
|
||||||
state := c.Delegate.State()
|
stateStore := c.Delegate.State()
|
||||||
_, config, err := state.CAConfig(nil)
|
_, config, err := stateStore.CAConfig(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -631,3 +636,8 @@ func (c *ConsulProvider) generateCA(privateKey string, sn uint64) (string, error
|
||||||
|
|
||||||
return buf.String(), nil
|
return buf.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLogger implements the NeedsLogger interface so the provider can log important messages.
|
||||||
|
func (c *ConsulProvider) SetLogger(logger *log.Logger) {
|
||||||
|
c.logger = logger
|
||||||
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ func TestConsulCAProvider_Bootstrap(t *testing.T) {
|
||||||
conf := testConsulCAConfig()
|
conf := testConsulCAConfig()
|
||||||
delegate := newMockDelegate(t, conf)
|
delegate := newMockDelegate(t, conf)
|
||||||
|
|
||||||
provider := &ConsulProvider{Delegate: delegate}
|
provider := TestConsulProvider(t, delegate)
|
||||||
require.NoError(provider.Configure(conf.ClusterID, true, conf.Config))
|
require.NoError(provider.Configure(conf.ClusterID, true, conf.Config))
|
||||||
require.NoError(provider.GenerateRoot())
|
require.NoError(provider.GenerateRoot())
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ func TestConsulCAProvider_Bootstrap_WithCert(t *testing.T) {
|
||||||
}
|
}
|
||||||
delegate := newMockDelegate(t, conf)
|
delegate := newMockDelegate(t, conf)
|
||||||
|
|
||||||
provider := &ConsulProvider{Delegate: delegate}
|
provider := TestConsulProvider(t, delegate)
|
||||||
require.NoError(provider.Configure(conf.ClusterID, true, conf.Config))
|
require.NoError(provider.Configure(conf.ClusterID, true, conf.Config))
|
||||||
require.NoError(provider.GenerateRoot())
|
require.NoError(provider.GenerateRoot())
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ func TestConsulCAProvider_SignLeaf(t *testing.T) {
|
||||||
conf.Config["PrivateKeyBits"] = tc.KeyBits
|
conf.Config["PrivateKeyBits"] = tc.KeyBits
|
||||||
delegate := newMockDelegate(t, conf)
|
delegate := newMockDelegate(t, conf)
|
||||||
|
|
||||||
provider := &ConsulProvider{Delegate: delegate}
|
provider := TestConsulProvider(t, delegate)
|
||||||
require.NoError(provider.Configure(conf.ClusterID, true, conf.Config))
|
require.NoError(provider.Configure(conf.ClusterID, true, conf.Config))
|
||||||
require.NoError(provider.GenerateRoot())
|
require.NoError(provider.GenerateRoot())
|
||||||
|
|
||||||
|
@ -242,7 +242,7 @@ func TestConsulCAProvider_CrossSignCA(t *testing.T) {
|
||||||
|
|
||||||
conf1 := testConsulCAConfig()
|
conf1 := testConsulCAConfig()
|
||||||
delegate1 := newMockDelegate(t, conf1)
|
delegate1 := newMockDelegate(t, conf1)
|
||||||
provider1 := &ConsulProvider{Delegate: delegate1}
|
provider1 := TestConsulProvider(t, delegate1)
|
||||||
conf1.Config["PrivateKeyType"] = tc.SigningKeyType
|
conf1.Config["PrivateKeyType"] = tc.SigningKeyType
|
||||||
conf1.Config["PrivateKeyBits"] = tc.SigningKeyBits
|
conf1.Config["PrivateKeyBits"] = tc.SigningKeyBits
|
||||||
require.NoError(provider1.Configure(conf1.ClusterID, true, conf1.Config))
|
require.NoError(provider1.Configure(conf1.ClusterID, true, conf1.Config))
|
||||||
|
@ -251,7 +251,7 @@ func TestConsulCAProvider_CrossSignCA(t *testing.T) {
|
||||||
conf2 := testConsulCAConfig()
|
conf2 := testConsulCAConfig()
|
||||||
conf2.CreateIndex = 10
|
conf2.CreateIndex = 10
|
||||||
delegate2 := newMockDelegate(t, conf2)
|
delegate2 := newMockDelegate(t, conf2)
|
||||||
provider2 := &ConsulProvider{Delegate: delegate2}
|
provider2 := TestConsulProvider(t, delegate2)
|
||||||
conf2.Config["PrivateKeyType"] = tc.CSRKeyType
|
conf2.Config["PrivateKeyType"] = tc.CSRKeyType
|
||||||
conf2.Config["PrivateKeyBits"] = tc.CSRKeyBits
|
conf2.Config["PrivateKeyBits"] = tc.CSRKeyBits
|
||||||
require.NoError(provider2.Configure(conf2.ClusterID, true, conf2.Config))
|
require.NoError(provider2.Configure(conf2.ClusterID, true, conf2.Config))
|
||||||
|
@ -360,7 +360,7 @@ func TestConsulProvider_SignIntermediate(t *testing.T) {
|
||||||
|
|
||||||
conf1 := testConsulCAConfig()
|
conf1 := testConsulCAConfig()
|
||||||
delegate1 := newMockDelegate(t, conf1)
|
delegate1 := newMockDelegate(t, conf1)
|
||||||
provider1 := &ConsulProvider{Delegate: delegate1}
|
provider1 := TestConsulProvider(t, delegate1)
|
||||||
conf1.Config["PrivateKeyType"] = tc.SigningKeyType
|
conf1.Config["PrivateKeyType"] = tc.SigningKeyType
|
||||||
conf1.Config["PrivateKeyBits"] = tc.SigningKeyBits
|
conf1.Config["PrivateKeyBits"] = tc.SigningKeyBits
|
||||||
require.NoError(provider1.Configure(conf1.ClusterID, true, conf1.Config))
|
require.NoError(provider1.Configure(conf1.ClusterID, true, conf1.Config))
|
||||||
|
@ -369,7 +369,7 @@ func TestConsulProvider_SignIntermediate(t *testing.T) {
|
||||||
conf2 := testConsulCAConfig()
|
conf2 := testConsulCAConfig()
|
||||||
conf2.CreateIndex = 10
|
conf2.CreateIndex = 10
|
||||||
delegate2 := newMockDelegate(t, conf2)
|
delegate2 := newMockDelegate(t, conf2)
|
||||||
provider2 := &ConsulProvider{Delegate: delegate2}
|
provider2 := TestConsulProvider(t, delegate2)
|
||||||
conf2.Config["PrivateKeyType"] = tc.CSRKeyType
|
conf2.Config["PrivateKeyType"] = tc.CSRKeyType
|
||||||
conf2.Config["PrivateKeyBits"] = tc.CSRKeyBits
|
conf2.Config["PrivateKeyBits"] = tc.CSRKeyBits
|
||||||
require.NoError(provider2.Configure(conf2.ClusterID, false, conf2.Config))
|
require.NoError(provider2.Configure(conf2.ClusterID, false, conf2.Config))
|
||||||
|
@ -451,7 +451,7 @@ func TestConsulCAProvider_MigrateOldID(t *testing.T) {
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.NotNil(providerState)
|
require.NotNil(providerState)
|
||||||
|
|
||||||
provider := &ConsulProvider{Delegate: delegate}
|
provider := TestConsulProvider(t, delegate)
|
||||||
require.NoError(provider.Configure(conf.ClusterID, true, conf.Config))
|
require.NoError(provider.Configure(conf.ClusterID, true, conf.Config))
|
||||||
require.NoError(provider.GenerateRoot())
|
require.NoError(provider.GenerateRoot())
|
||||||
|
|
||||||
|
|
|
@ -288,7 +288,7 @@ func TestVaultProvider_SignIntermediateConsul(t *testing.T) {
|
||||||
|
|
||||||
conf := testConsulCAConfig()
|
conf := testConsulCAConfig()
|
||||||
delegate := newMockDelegate(t, conf)
|
delegate := newMockDelegate(t, conf)
|
||||||
provider2 := &ConsulProvider{Delegate: delegate}
|
provider2 := TestConsulProvider(t, delegate)
|
||||||
require.NoError(t, provider2.Configure(conf.ClusterID, false, conf.Config))
|
require.NoError(t, provider2.Configure(conf.ClusterID, false, conf.Config))
|
||||||
|
|
||||||
testSignIntermediateCrossDC(t, provider1, provider2)
|
testSignIntermediateCrossDC(t, provider1, provider2)
|
||||||
|
@ -298,7 +298,7 @@ func TestVaultProvider_SignIntermediateConsul(t *testing.T) {
|
||||||
t.Run("pri=consul,sec=vault", func(t *testing.T) {
|
t.Run("pri=consul,sec=vault", func(t *testing.T) {
|
||||||
conf := testConsulCAConfig()
|
conf := testConsulCAConfig()
|
||||||
delegate := newMockDelegate(t, conf)
|
delegate := newMockDelegate(t, conf)
|
||||||
provider1 := &ConsulProvider{Delegate: delegate}
|
provider1 := TestConsulProvider(t, delegate)
|
||||||
require.NoError(t, provider1.Configure(conf.ClusterID, true, conf.Config))
|
require.NoError(t, provider1.Configure(conf.ClusterID, true, conf.Config))
|
||||||
require.NoError(t, provider1.GenerateRoot())
|
require.NoError(t, provider1.GenerateRoot())
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,11 @@ package ca
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/connect"
|
"github.com/hashicorp/consul/agent/connect"
|
||||||
|
"github.com/mitchellh/go-testing-interface"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KeyTestCases is a list of the important CA key types that we should test
|
// KeyTestCases is a list of the important CA key types that we should test
|
||||||
|
@ -61,3 +64,12 @@ func CASigningKeyTypeCases() []CASigningKeyTypes {
|
||||||
}
|
}
|
||||||
return cases
|
return cases
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestConsulProvider creates a new ConsulProvider, taking care to stub out it's
|
||||||
|
// Logger so that logging calls don't panic. If logging output is important
|
||||||
|
// SetLogger can be called again with another logger to capture logs.
|
||||||
|
func TestConsulProvider(t testing.T, d ConsulProviderStateDelegate) *ConsulProvider {
|
||||||
|
provider := &ConsulProvider{Delegate: d}
|
||||||
|
provider.SetLogger(log.New(ioutil.Discard, "", 0))
|
||||||
|
return provider
|
||||||
|
}
|
||||||
|
|
|
@ -102,14 +102,22 @@ func parseCARoot(pemValue, provider, clusterID string) (*structs.CARoot, error)
|
||||||
|
|
||||||
// createProvider returns a connect CA provider from the given config.
|
// createProvider returns a connect CA provider from the given config.
|
||||||
func (s *Server) createCAProvider(conf *structs.CAConfiguration) (ca.Provider, error) {
|
func (s *Server) createCAProvider(conf *structs.CAConfiguration) (ca.Provider, error) {
|
||||||
|
var p ca.Provider
|
||||||
switch conf.Provider {
|
switch conf.Provider {
|
||||||
case structs.ConsulCAProvider:
|
case structs.ConsulCAProvider:
|
||||||
return &ca.ConsulProvider{Delegate: &consulCADelegate{s}}, nil
|
p = &ca.ConsulProvider{Delegate: &consulCADelegate{s}}
|
||||||
case structs.VaultCAProvider:
|
case structs.VaultCAProvider:
|
||||||
return &ca.VaultProvider{}, nil
|
p = &ca.VaultProvider{}
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown CA provider %q", conf.Provider)
|
return nil, fmt.Errorf("unknown CA provider %q", conf.Provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the provider implements NeedsLogger, we give it our logger.
|
||||||
|
if needsLogger, ok := p.(ca.NeedsLogger); ok {
|
||||||
|
needsLogger.SetLogger(s.logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) getCAProvider() (ca.Provider, *structs.CARoot) {
|
func (s *Server) getCAProvider() (ca.Provider, *structs.CARoot) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package consul
|
package consul
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
@ -10,6 +11,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/agent/connect/ca"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/connect"
|
"github.com/hashicorp/consul/agent/connect"
|
||||||
"github.com/hashicorp/consul/agent/metadata"
|
"github.com/hashicorp/consul/agent/metadata"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
@ -1119,3 +1122,36 @@ func TestServer_RPC_RateLimit(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServer_CALogging(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
dir1, conf1 := testServerConfig(t)
|
||||||
|
|
||||||
|
// Setup dummy logger to catch output
|
||||||
|
var buf bytes.Buffer
|
||||||
|
logger := log.New(&buf, "", log.LstdFlags)
|
||||||
|
|
||||||
|
c, err := tlsutil.NewConfigurator(conf1.ToTLSUtilConfig(), nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
s1, err := NewServerLogger(conf1, logger, new(token.Store), c)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir1)
|
||||||
|
defer s1.Shutdown()
|
||||||
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
||||||
|
|
||||||
|
if _, ok := s1.caProvider.(ca.NeedsLogger); !ok {
|
||||||
|
t.Fatalf("provider does not implement NeedsLogger")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait til CA root is setup
|
||||||
|
retry.Run(t, func(r *retry.R) {
|
||||||
|
var out structs.IndexedCARoots
|
||||||
|
r.Check(s1.RPC("ConnectCA.Roots", structs.DCSpecificRequest{
|
||||||
|
Datacenter: conf1.Datacenter,
|
||||||
|
}, &out))
|
||||||
|
})
|
||||||
|
|
||||||
|
require.Contains(t, buf.String(), "consul CA provider configured")
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue