diff --git a/command/operator_diagnose.go b/command/operator_diagnose.go index c81fc57020..8368d6ef6e 100644 --- a/command/operator_diagnose.go +++ b/command/operator_diagnose.go @@ -2,9 +2,13 @@ package command import ( "strings" + "sync" log "github.com/hashicorp/go-hclog" + "github.com/hashicorp/vault/internalshared/listenerutil" + "github.com/hashicorp/vault/internalshared/reloadutil" "github.com/hashicorp/vault/sdk/version" + "github.com/hashicorp/vault/vault/diagnose" "github.com/mitchellh/cli" "github.com/posener/complete" ) @@ -17,9 +21,15 @@ var _ cli.CommandAutocomplete = (*OperatorDiagnoseCommand)(nil) type OperatorDiagnoseCommand struct { *BaseCommand - flagDebug bool - flagSkips []string - flagConfigs []string + flagDebug bool + flagSkips []string + flagConfigs []string + cleanupGuard sync.Once + + reloadFuncsLock *sync.RWMutex + reloadFuncs *map[string][]reloadutil.ReloadFunc + startedCh chan struct{} // for tests + reloadedCh chan struct{} // for tests } func (c *OperatorDiagnoseCommand) Synopsis() string { @@ -110,7 +120,7 @@ func (c *OperatorDiagnoseCommand) RunWithParsedFlags() int { } c.UI.Output(version.GetVersion().FullVersionNumber(true)) - + rloadFuncs := make(map[string][]reloadutil.ReloadFunc) server := &ServerCommand{ // TODO: set up a different one? // In particular, a UI instance that won't output? @@ -125,8 +135,10 @@ func (c *OperatorDiagnoseCommand) RunWithParsedFlags() int { // TODO: other ServerCommand options? - logger: log.NewInterceptLogger(nil), - allLoggers: []log.Logger{}, + logger: log.NewInterceptLogger(nil), + allLoggers: []log.Logger{}, + reloadFuncs: &rloadFuncs, + reloadFuncsLock: new(sync.RWMutex), } phase := "Parse configuration" @@ -140,6 +152,61 @@ func (c *OperatorDiagnoseCommand) RunWithParsedFlags() int { return 1 } + // Check Listener Information + // TODO: Run Diagnose checks on the actual net.Listeners + + disableClustering := config.HAStorage.DisableClustering + infoKeys := make([]string, 0, 10) + info := make(map[string]string) + status, lns, _, errMsg := server.InitListeners(config, disableClustering, &infoKeys, &info) + + if status != 0 { + c.UI.Output("Error parsing listener configuration.") + c.UI.Error(errMsg.Error()) + return 1 + } + + // Make sure we close all listeners from this point on + listenerCloseFunc := func() { + for _, ln := range lns { + ln.Listener.Close() + } + } + + defer c.cleanupGuard.Do(listenerCloseFunc) + + sanitizedListeners := make([]listenerutil.Listener, 0, len(config.Listeners)) + for _, ln := range lns { + if ln.Config.TLSDisable { + c.UI.Warn("WARNING! TLS is disabled in a Listener config stanza.") + continue + } + if ln.Config.TLSDisableClientCerts { + c.UI.Warn("WARNING! TLS for a listener is turned on without requiring client certs.") + } + + // Check ciphersuite and load ca/cert/key files + // TODO: TLSConfig returns a reloadFunc and a TLSConfig. We can use this to + // perform an active probe. + _, _, err := listenerutil.TLSConfig(ln.Config, make(map[string]string), c.UI) + if err != nil { + c.UI.Output("Error creating TLS Configuration out of config file: ") + c.UI.Output(err.Error()) + return 1 + } + + sanitizedListeners = append(sanitizedListeners, listenerutil.Listener{ + Listener: ln.Listener, + Config: ln.Config, + }) + } + err = diagnose.ListenerChecks(sanitizedListeners) + if err != nil { + c.UI.Output("Diagnose caught configuration errors: ") + c.UI.Output(err.Error()) + return 1 + } + // Errors in these items could stop Vault from starting but are not yet covered: // TODO: logging configuration // TODO: SetupTelemetry diff --git a/command/operator_diagnose_test.go b/command/operator_diagnose_test.go index d153d40701..b0b78deba8 100644 --- a/command/operator_diagnose_test.go +++ b/command/operator_diagnose_test.go @@ -22,7 +22,6 @@ func testOperatorDiagnoseCommand(tb testing.TB) (*cli.MockUi, *OperatorDiagnoseC func TestOperatorDiagnoseCommand_Run(t *testing.T) { t.Parallel() - cases := []struct { name string args []string @@ -32,7 +31,7 @@ func TestOperatorDiagnoseCommand_Run(t *testing.T) { { "diagnose_ok", []string{ - "-config", "./server/test-fixtures/config.hcl", + "-config", "./server/test-fixtures/config_diagnose_ok.hcl", }, []string{"Parse configuration\n\x1b[F\x1b[32m[ ok ]\x1b[0m Parse configuration\n[ ] Access storage\n\x1b[F\x1b[32m[ ok ]\x1b[0m Access storage\n"}, 0, @@ -45,6 +44,14 @@ func TestOperatorDiagnoseCommand_Run(t *testing.T) { []string{"Parse configuration\n\x1b[F\x1b[32m[ ok ]\x1b[0m Parse configuration\n[ ] Access storage\n\x1b[F\x1b[31m[failed]\x1b[0m Access storage\nA storage backend must be specified\n"}, 1, }, + { + "diagnose_listener_config_ok", + []string{ + "-config", "./server/test-fixtures/tls_config_ok.hcl", + }, + []string{"Parse configuration\n\x1b[F\x1b[32m[ ok ]\x1b[0m Parse configuration\n[ ] Access storage\n\x1b[F\x1b[32m[ ok ]\x1b[0m Access storage\n"}, + 0, + }, } t.Run("validations", func(t *testing.T) { diff --git a/command/server.go b/command/server.go index 4d8a9da3d6..784a53e9ab 100644 --- a/command/server.go +++ b/command/server.go @@ -871,6 +871,92 @@ func (c *ServerCommand) setupStorage(config *server.Config) (physical.Backend, e return backend, nil } +// InitListeners returns a response code, error message, Listeners, and a TCP Address list. +func (c *ServerCommand) InitListeners(config *server.Config, disableClustering bool, infoKeys *[]string, info *map[string]string) (int, []listenerutil.Listener, []*net.TCPAddr, error) { + clusterAddrs := []*net.TCPAddr{} + + // Initialize the listeners + lns := make([]listenerutil.Listener, 0, len(config.Listeners)) + + c.reloadFuncsLock.Lock() + + defer c.reloadFuncsLock.Unlock() + + var errMsg error + for i, lnConfig := range config.Listeners { + ln, props, reloadFunc, err := server.NewListener(lnConfig, c.gatedWriter, c.UI) + if err != nil { + errMsg = fmt.Errorf("Error initializing listener of type %s: %s", lnConfig.Type, err) + return 1, nil, nil, errMsg + } + + if reloadFunc != nil { + relSlice := (*c.reloadFuncs)["listener|"+lnConfig.Type] + relSlice = append(relSlice, reloadFunc) + (*c.reloadFuncs)["listener|"+lnConfig.Type] = relSlice + } + + if !disableClustering && lnConfig.Type == "tcp" { + addr := lnConfig.ClusterAddress + if addr != "" { + tcpAddr, err := net.ResolveTCPAddr("tcp", lnConfig.ClusterAddress) + if err != nil { + errMsg = fmt.Errorf("Error resolving cluster_address: %s", err) + return 1, nil, nil, errMsg + } + clusterAddrs = append(clusterAddrs, tcpAddr) + } else { + tcpAddr, ok := ln.Addr().(*net.TCPAddr) + if !ok { + errMsg = fmt.Errorf("Failed to parse tcp listener") + return 1, nil, nil, errMsg + } + clusterAddr := &net.TCPAddr{ + IP: tcpAddr.IP, + Port: tcpAddr.Port + 1, + } + clusterAddrs = append(clusterAddrs, clusterAddr) + addr = clusterAddr.String() + } + props["cluster address"] = addr + } + + if lnConfig.MaxRequestSize == 0 { + lnConfig.MaxRequestSize = vaulthttp.DefaultMaxRequestSize + } + props["max_request_size"] = fmt.Sprintf("%d", lnConfig.MaxRequestSize) + + if lnConfig.MaxRequestDuration == 0 { + lnConfig.MaxRequestDuration = vault.DefaultMaxRequestDuration + } + props["max_request_duration"] = lnConfig.MaxRequestDuration.String() + + lns = append(lns, listenerutil.Listener{ + Listener: ln, + Config: lnConfig, + }) + + // Store the listener props for output later + key := fmt.Sprintf("listener %d", i+1) + propsList := make([]string, 0, len(props)) + for k, v := range props { + propsList = append(propsList, fmt.Sprintf( + "%s: %q", k, v)) + } + sort.Strings(propsList) + *infoKeys = append(*infoKeys, key) + (*info)[key] = fmt.Sprintf( + "%s (%s)", lnConfig.Type, strings.Join(propsList, ", ")) + + } + if !disableClustering { + if c.logger.IsDebug() { + c.logger.Debug("cluster listener addresses synthesized", "cluster_addresses", clusterAddrs) + } + } + return 0, lns, clusterAddrs, nil +} + func (c *ServerCommand) Run(args []string) int { f := c.Flags() @@ -1482,82 +1568,12 @@ CLUSTER_SYNTHESIS_COMPLETE: } } - clusterAddrs := []*net.TCPAddr{} + status, lns, clusterAddrs, errMsg := c.InitListeners(config, disableClustering, &infoKeys, &info) - // Initialize the listeners - lns := make([]listenerutil.Listener, 0, len(config.Listeners)) - c.reloadFuncsLock.Lock() - for i, lnConfig := range config.Listeners { - ln, props, reloadFunc, err := server.NewListener(lnConfig, c.gatedWriter, c.UI) - if err != nil { - c.UI.Error(fmt.Sprintf("Error initializing listener of type %s: %s", lnConfig.Type, err)) - return 1 - } - - if reloadFunc != nil { - relSlice := (*c.reloadFuncs)["listener|"+lnConfig.Type] - relSlice = append(relSlice, reloadFunc) - (*c.reloadFuncs)["listener|"+lnConfig.Type] = relSlice - } - - if !disableClustering && lnConfig.Type == "tcp" { - addr := lnConfig.ClusterAddress - if addr != "" { - tcpAddr, err := net.ResolveTCPAddr("tcp", lnConfig.ClusterAddress) - if err != nil { - c.UI.Error(fmt.Sprintf("Error resolving cluster_address: %s", err)) - return 1 - } - clusterAddrs = append(clusterAddrs, tcpAddr) - } else { - tcpAddr, ok := ln.Addr().(*net.TCPAddr) - if !ok { - c.UI.Error("Failed to parse tcp listener") - return 1 - } - clusterAddr := &net.TCPAddr{ - IP: tcpAddr.IP, - Port: tcpAddr.Port + 1, - } - clusterAddrs = append(clusterAddrs, clusterAddr) - addr = clusterAddr.String() - } - props["cluster address"] = addr - } - - if lnConfig.MaxRequestSize == 0 { - lnConfig.MaxRequestSize = vaulthttp.DefaultMaxRequestSize - } - props["max_request_size"] = fmt.Sprintf("%d", lnConfig.MaxRequestSize) - - if lnConfig.MaxRequestDuration == 0 { - lnConfig.MaxRequestDuration = vault.DefaultMaxRequestDuration - } - props["max_request_duration"] = fmt.Sprintf("%s", lnConfig.MaxRequestDuration.String()) - - lns = append(lns, listenerutil.Listener{ - Listener: ln, - Config: lnConfig, - }) - - // Store the listener props for output later - key := fmt.Sprintf("listener %d", i+1) - propsList := make([]string, 0, len(props)) - for k, v := range props { - propsList = append(propsList, fmt.Sprintf( - "%s: %q", k, v)) - } - sort.Strings(propsList) - infoKeys = append(infoKeys, key) - info[key] = fmt.Sprintf( - "%s (%s)", lnConfig.Type, strings.Join(propsList, ", ")) - - } - c.reloadFuncsLock.Unlock() - if !disableClustering { - if c.logger.IsDebug() { - c.logger.Debug("cluster listener addresses synthesized", "cluster_addresses", clusterAddrs) - } + if status != 0 { + c.UI.Output("Error parsing listener configuration.") + c.UI.Error(errMsg.Error()) + return 1 } // Make sure we close all listeners from this point on diff --git a/command/server/test-fixtures/config_diagnose_ok.hcl b/command/server/test-fixtures/config_diagnose_ok.hcl new file mode 100644 index 0000000000..053ad635df --- /dev/null +++ b/command/server/test-fixtures/config_diagnose_ok.hcl @@ -0,0 +1,47 @@ +disable_cache = true +disable_mlock = true + +ui = true + +listener "tcp" { + address = "127.0.0.1:1024" + tls_disable = true +} + +backend "consul" { + foo = "bar" + advertise_addr = "foo" +} + +ha_backend "consul" { + bar = "baz" + advertise_addr = "snafu" + disable_clustering = "true" +} + +service_registration "consul" { + foo = "bar" +} + +telemetry { + statsd_address = "bar" + usage_gauge_period = "5m" + maximum_gauge_cardinality = 100 + + statsite_address = "foo" + dogstatsd_addr = "127.0.0.1:7254" + dogstatsd_tags = ["tag_1:val_1", "tag_2:val_2"] + metrics_prefix = "myprefix" +} + +sentinel { + additional_enabled_modules = [] +} + +max_lease_ttl = "10h" +default_lease_ttl = "10h" +cluster_name = "testcluster" +pid_file = "./pidfile" +raw_storage_endpoint = true +disable_sealwrap = true +disable_printable_check = true diff --git a/command/server/test-fixtures/nostore_config.hcl b/command/server/test-fixtures/nostore_config.hcl index ad86e3dcee..bdfa051abe 100644 --- a/command/server/test-fixtures/nostore_config.hcl +++ b/command/server/test-fixtures/nostore_config.hcl @@ -4,8 +4,8 @@ disable_mlock = true ui = true listener "tcp" { - address = "127.0.0.1:443" - allow_stuff = true + address = "127.0.0.1:1024" + tls_disable = true } ha_backend "consul" { diff --git a/command/server/test-fixtures/tls_config_ok.hcl b/command/server/test-fixtures/tls_config_ok.hcl new file mode 100644 index 0000000000..83c8d1159b --- /dev/null +++ b/command/server/test-fixtures/tls_config_ok.hcl @@ -0,0 +1,48 @@ +disable_cache = true +disable_mlock = true + +ui = true + +listener "tcp" { + address = "127.0.0.1:1025" + tls_cert_file = "./../api/test-fixtures/keys/cert.pem" + tls_key_file = "./../api/test-fixtures/keys/key.pem" +} + +backend "consul" { + foo = "bar" + advertise_addr = "foo" +} + +ha_backend "consul" { + bar = "baz" + advertise_addr = "snafu" + disable_clustering = "true" +} + +service_registration "consul" { + foo = "bar" +} + +telemetry { + statsd_address = "bar" + usage_gauge_period = "5m" + maximum_gauge_cardinality = 100 + + statsite_address = "foo" + dogstatsd_addr = "127.0.0.1:7254" + dogstatsd_tags = ["tag_1:val_1", "tag_2:val_2"] + metrics_prefix = "myprefix" +} + +sentinel { + additional_enabled_modules = [] +} + +max_lease_ttl = "10h" +default_lease_ttl = "10h" +cluster_name = "testcluster" +pid_file = "./pidfile" +raw_storage_endpoint = true +disable_sealwrap = true +disable_printable_check = true diff --git a/physical/raft/raft.go b/physical/raft/raft.go index 5d207ad070..2d667543ff 100644 --- a/physical/raft/raft.go +++ b/physical/raft/raft.go @@ -5,11 +5,6 @@ import ( "crypto/tls" "errors" "fmt" - "github.com/hashicorp/vault/sdk/helper/consts" - "github.com/hashicorp/vault/sdk/helper/jsonutil" - "github.com/hashicorp/vault/sdk/helper/tlsutil" - "github.com/hashicorp/vault/sdk/logical" - "github.com/hashicorp/vault/sdk/physical" "io" "io/ioutil" "os" @@ -18,6 +13,12 @@ import ( "sync" "time" + "github.com/hashicorp/vault/sdk/helper/consts" + "github.com/hashicorp/vault/sdk/helper/jsonutil" + "github.com/hashicorp/vault/sdk/helper/tlsutil" + "github.com/hashicorp/vault/sdk/logical" + "github.com/hashicorp/vault/sdk/physical" + "github.com/armon/go-metrics" "github.com/golang/protobuf/proto" "github.com/hashicorp/errwrap" diff --git a/vault/core.go b/vault/core.go index 74199979c4..72b5f767f8 100644 --- a/vault/core.go +++ b/vault/core.go @@ -2614,16 +2614,6 @@ func (c *Core) SetConfig(conf *server.Config) { c.logger.Debug("set config", "sanitized config", string(bz)) } -// SanitizedConfig returns a sanitized version of the current config. -// See server.Config.Sanitized for specific values omitted. -func (c *Core) SanitizedConfig() map[string]interface{} { - conf := c.rawConfig.Load() - if conf == nil { - return nil - } - return conf.(*server.Config).Sanitized() -} - // LogFormat returns the log format current in use. func (c *Core) LogFormat() string { conf := c.rawConfig.Load() diff --git a/vault/core_util.go b/vault/core_util.go index 57d76f0d0b..60123d11ef 100644 --- a/vault/core_util.go +++ b/vault/core_util.go @@ -5,6 +5,7 @@ package vault import ( "context" + "github.com/hashicorp/vault/command/server" "github.com/hashicorp/vault/helper/namespace" "github.com/hashicorp/vault/sdk/helper/license" "github.com/hashicorp/vault/sdk/logical" @@ -55,6 +56,26 @@ func (c *Core) setupReplicationResolverHandler() error { return nil } +// GetCoreConfigInternal returns the server configuration +// in struct format. +func (c *Core) GetCoreConfigInternal() *server.Config { + conf := c.rawConfig.Load() + if conf == nil { + return nil + } + return conf.(*server.Config) +} + +// SanitizedConfig returns a sanitized version of the current config. +// See server.Config.Sanitized for specific values omitted. +func (c *Core) SanitizedConfig() map[string]interface{} { + conf := c.rawConfig.Load() + if conf == nil { + return nil + } + return conf.(*server.Config).Sanitized() +} + func (c *Core) teardownReplicationResolverHandler() {} func createSecondaries(*Core, *CoreConfig) {} diff --git a/vault/diagnose/test-fixtures/fakecert.pem b/vault/diagnose/test-fixtures/fakecert.pem new file mode 100644 index 0000000000..413ec9491f --- /dev/null +++ b/vault/diagnose/test-fixtures/fakecert.pem @@ -0,0 +1 @@ +This is a fake cert! \ No newline at end of file diff --git a/vault/diagnose/test-fixtures/trailingdatacert.pem b/vault/diagnose/test-fixtures/trailingdatacert.pem new file mode 100644 index 0000000000..0aeeffe79e --- /dev/null +++ b/vault/diagnose/test-fixtures/trailingdatacert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDTDCCAjQCFBVkElzevM5tqOIoJxMSJnzPvc1MMA0GCSqGSIb3DQEBCwUAMGMx +CzAJBgNVBAYTAkZSMQ4wDAYDVQQIDAVQYXJpczEOMAwGA1UEBwwFUGFyaXMxDjAM +BgNVBAoMBVVidWR1MQ4wDAYDVQQLDAVVYnVkdTEUMBIGA1UEAwwLU0RBIFJPT1Qg +Q0EwHhcNMjAwNjExMTUzNTIyWhcNMzAwNjA5MTUzNTIyWjBiMQswCQYDVQQGEwJG +UjEOMAwGA1UECAwFUGFyaXMxDjAMBgNVBAcMBVBhcmlzMQ4wDAYDVQQKDAVVQnVk +dTEOMAwGA1UECwwFVWJ1ZHUxEzARBgNVBAMMClNEQSBDTElFTlQwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmDo5b/iLyNfColCwB15pvUDUkYzp5ungr +BCPb5H9Sw7ymcfsiTeBtgADXHyRli+YtfAPjC/55wkkgfBgKRKS8xG8zas0XXt/f +Wpnl2d8oYGXhGBZPdfaPP/fTANSTWIKVF7+My9LKrulJ+CTKNSnTWKsXFzZivN4H +a9aaBdP3s5CjVtJ456Egu5g2EfStBtNJwXzCjPSZyD69v1G0yWx6M8zhvY6+EV+T +uqycAsNfvcf9K0nR83RXkZnNnSNy6ptNg0fMpa5JrCHYirtGU5O7CSBCpA52fy9R +c12NYxl7qFSCTMC/SzZXjGUpIohxNmTzwCttT292A0yc3Bi8aPl/AgMBAAEwDQYJ +KoZIhvcNAQELBQADggEBAIvLrCOMedGTg4oSCYI+HdWB6E4uhG5UFxNnv3QEib0p +GV71S+wrU443ZAfZvxlHFKjkvZDoV8hXT8VIuWOxA9TvaLksoIHeOKdDiGntjVfL +aDMBnYXPXSDEqFDtAXjtUuIua74fkJ/guCJiX+t7fPctWZPo6J89XtgRZrZcnFIF +1RQZg6cMDoVb3aMUWKvKo6YDsoxiP6rM8xNHqxW788oLTyDeu9ceJkv31jIUYIgJ +t4i3bkqf54FAIMdKn/mXmprrqW0b4tvR0fCThD4pgXxVw/Egm3tro/z6/y+qLJ9U +oZa28h/VYGyoJnEOSl09nZhcfOwWUaXxl94n93dBwXIwDDAKBggrBgEFBQcDAg== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/vault/diagnose/tls.go b/vault/diagnose/tls.go new file mode 100644 index 0000000000..51272a409a --- /dev/null +++ b/vault/diagnose/tls.go @@ -0,0 +1,58 @@ +package diagnose + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + + "github.com/hashicorp/vault/internalshared/listenerutil" + "github.com/hashicorp/vault/vault" +) + +func ListenerChecks(listeners []listenerutil.Listener) error { + for _, listener := range listeners { + l := listener.Config + err := TLSFileChecks(l.TLSCertFile, l.TLSKeyFile) + if err != nil { + return err + } + } + return nil +} + +// TLSChecks contains manual error checks against the TLS configuration +func TLSFileChecks(certFilePath, keyFilePath string) error { + + // LoadX509KeyPair will check if the key/cert information can be loaded from files, + // if they exist with keys and certs of the same algorithm type, if there + // is an unknown algorithm type being used, and if the files have trailing + // data. + cert, err := tls.LoadX509KeyPair(certFilePath, keyFilePath) + if err != nil { + return err + } + + // LoadX509KeyPair has a nil leaf certificate because it does not retain the + // parsed form, so we have to manually create it ourselves. + + x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + return err + } + cert.Leaf = x509Cert + + // TODO: Check root as well via l.TLSClientCAFile + + // Check that certificate isn't expired and is of correct usage type + cert.Leaf.Verify(x509.VerifyOptions{ + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + }) + return nil +} + +// ServerListenerActiveProbe attempts to use TLS information to set up a TLS server with each listener +// and generate a successful request through to the server. +// TODO +func ServerListenerActiveProbe(core *vault.Core) error { + return fmt.Errorf("Method not implemented") +} diff --git a/vault/diagnose/tls_test.go b/vault/diagnose/tls_test.go new file mode 100644 index 0000000000..0e7aa4233e --- /dev/null +++ b/vault/diagnose/tls_test.go @@ -0,0 +1,146 @@ +package diagnose + +import ( + "testing" + + "github.com/hashicorp/vault/command/server" + "github.com/hashicorp/vault/internalshared/configutil" + "github.com/hashicorp/vault/internalshared/listenerutil" + "github.com/hashicorp/vault/vault" +) + +func setup(t *testing.T) *vault.Core { + serverConf := &server.Config{ + SharedConfig: &configutil.SharedConfig{ + Listeners: []*configutil.Listener{ + { + Type: "tcp", + Address: "127.0.0.1:443", + ClusterAddress: "127.0.0.1:8201", + TLSCertFile: "./certs/server.crt", + TLSKeyFile: "./certs/server.key", + TLSClientCAFile: "./certs/rootca.crt", + TLSMinVersion: "tls11", + TLSRequireAndVerifyClientCert: true, + TLSDisableClientCerts: true, + }, + { + Type: "tcp", + Address: "127.0.0.1:443", + ClusterAddress: "127.0.0.1:8201", + TLSCertFile: "./certs/server2.crt", + TLSKeyFile: "./certs/server2.key", + TLSClientCAFile: "./certs/rootca2.crt", + TLSMinVersion: "tls12", + TLSRequireAndVerifyClientCert: true, + TLSDisableClientCerts: false, + }, + { + Type: "tcp", + Address: "127.0.0.1:443", + ClusterAddress: "127.0.0.1:8201", + TLSCertFile: "./certs/server3.crt", + TLSKeyFile: "./certs/server3.key", + TLSClientCAFile: "./certs/rootca3.crt", + TLSMinVersion: "tls13", + TLSRequireAndVerifyClientCert: false, + TLSDisableClientCerts: true, + }, + }, + }, + } + conf := &vault.CoreConfig{ + RawConfig: serverConf, + } + core := vault.TestCoreWithConfig(t, conf) + return core +} + +func TestTLSValidCert(t *testing.T) { + listeners := []listenerutil.Listener{ + { + Config: &configutil.Listener{ + Type: "tcp", + Address: "127.0.0.1:443", + ClusterAddress: "127.0.0.1:8201", + TLSCertFile: "./../../api/test-fixtures/keys/cert.pem", + TLSKeyFile: "./../../api/test-fixtures/keys/key.pem", + TLSClientCAFile: "./../../api/test-fixtures/root/rootcacert.pem", + TLSMinVersion: "0", + TLSRequireAndVerifyClientCert: true, + TLSDisableClientCerts: false, + }, + }, + } + err := ListenerChecks(listeners) + if err != nil { + t.Errorf(err.Error()) + } +} + +func TestTLSFakeCert(t *testing.T) { + listeners := []listenerutil.Listener{ + { + Config: &configutil.Listener{ + Type: "tcp", + Address: "127.0.0.1:443", + ClusterAddress: "127.0.0.1:8201", + TLSCertFile: "./test-fixtures/fakecert.pem", + TLSKeyFile: "./../../api/test-fixtures/keys/key.pem", + TLSClientCAFile: "./../../api/test-fixtures/root/rootcacert.pem", + TLSMinVersion: "0", + TLSRequireAndVerifyClientCert: true, + TLSDisableClientCerts: false, + }, + }, + } + err := ListenerChecks(listeners) + if err == nil { + t.Errorf("TLS Config check on fake certificate should fail") + } + if err.Error() != "tls: failed to find any PEM data in certificate input" { + t.Errorf("Bad error message: %s", err.Error()) + } +} + +// TestTLSTrailingData uses a certificate from: +// https://github.com/golang/go/issues/40545 that contains +// an extra DER sequence, and makes sure a trailing data error +// is returned. +func TestTLSTrailingData(t *testing.T) { + listeners := []listenerutil.Listener{ + { + Config: &configutil.Listener{ + Type: "tcp", + Address: "127.0.0.1:443", + ClusterAddress: "127.0.0.1:8201", + TLSCertFile: "./test-fixtures/trailingdatacert.pem", + TLSKeyFile: "./../../api/test-fixtures/keys/key.pem", + TLSClientCAFile: "./../../api/test-fixtures/root/rootcacert.pem", + TLSMinVersion: "0", + TLSRequireAndVerifyClientCert: true, + TLSDisableClientCerts: false, + }, + }, + } + err := ListenerChecks(listeners) + if err == nil { + t.Errorf("TLS Config check on fake certificate should fail") + } + if err.Error() != "asn1: syntax error: trailing data" { + t.Errorf("Bad error message: %s", err.Error()) + } +} + +func TestTLSExpiredCert(t *testing.T) { +} + +func TestTLSMismatchedCryptographicInfo(t *testing.T) {} + +func TestTLSContradictoryFlags(t *testing.T) {} + +func TestTLSBadCipherSuite(t *testing.T) {} + +func TestTLSUnknownAlgorithm(t *testing.T) {} + +func TestTLSIncorrectUsageType(t *testing.T) {} diff --git a/vault/testing.go b/vault/testing.go index 7fdc4e271d..d69fec7c35 100644 --- a/vault/testing.go +++ b/vault/testing.go @@ -158,6 +158,7 @@ func TestCoreWithSealAndUI(t testing.T, opts *CoreConfig) *Core { conf.MetricsHelper = opts.MetricsHelper conf.MetricSink = opts.MetricSink conf.NumExpirationWorkers = numExpirationWorkersTest + conf.RawConfig = opts.RawConfig if opts.Logger != nil { conf.Logger = opts.Logger