mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-02 19:47:54 +00:00
Initial Diagnose Command for TLS and Listener [VAULT-1896, VAULT-1899] (#11249)
* sanity checks for tls config in diagnose * backup * backup * backup * added necessary tests * remove comment * remove parallels causing test flakiness * comments * small fix * separate out config hcl test case into new hcl file * newline * addressed comments * addressed comments * addressed comments * addressed comments * addressed comments * reload funcs should be allowed to be nil
This commit is contained in:
@@ -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"
|
||||
)
|
||||
@@ -20,6 +24,12 @@ type OperatorDiagnoseCommand struct {
|
||||
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?
|
||||
@@ -127,6 +137,8 @@ func (c *OperatorDiagnoseCommand) RunWithParsedFlags() int {
|
||||
|
||||
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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,84 +1568,14 @@ 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))
|
||||
if status != 0 {
|
||||
c.UI.Output("Error parsing listener configuration.")
|
||||
c.UI.Error(errMsg.Error())
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we close all listeners from this point on
|
||||
listenerCloseFunc := func() {
|
||||
for _, ln := range lns {
|
||||
|
||||
47
command/server/test-fixtures/config_diagnose_ok.hcl
Normal file
47
command/server/test-fixtures/config_diagnose_ok.hcl
Normal file
@@ -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
|
||||
@@ -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" {
|
||||
|
||||
48
command/server/test-fixtures/tls_config_ok.hcl
Normal file
48
command/server/test-fixtures/tls_config_ok.hcl
Normal file
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
|
||||
1
vault/diagnose/test-fixtures/fakecert.pem
Normal file
1
vault/diagnose/test-fixtures/fakecert.pem
Normal file
@@ -0,0 +1 @@
|
||||
This is a fake cert!
|
||||
20
vault/diagnose/test-fixtures/trailingdatacert.pem
Normal file
20
vault/diagnose/test-fixtures/trailingdatacert.pem
Normal file
@@ -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-----
|
||||
58
vault/diagnose/tls.go
Normal file
58
vault/diagnose/tls.go
Normal file
@@ -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")
|
||||
}
|
||||
146
vault/diagnose/tls_test.go
Normal file
146
vault/diagnose/tls_test.go
Normal file
@@ -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) {}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user