Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TLS Verification Bugfixes #11910

Merged
merged 8 commits into from
Jun 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 6 additions & 30 deletions command/operator_diagnose.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ func (c *OperatorDiagnoseCommand) offlineDiagnostics(ctx context.Context) error
SEALFAIL:
sealspan.End()
var coreConfig vault.CoreConfig
if err := diagnose.Test(ctx, "setup-core", func(ctx context.Context) error {
diagnose.Test(ctx, "setup-core", func(ctx context.Context) error {
var secureRandomReader io.Reader
// prepare a secure random reader for core
secureRandomReader, err = configutil.CreateSecureRandomReaderFunc(config.SharedConfig, barrierWrapper)
Expand All @@ -429,9 +429,7 @@ SEALFAIL:
}
coreConfig = createCoreConfig(server, config, *backend, configSR, barrierSeal, unwrapSeal, metricsHelper, metricSink, secureRandomReader)
return nil
}); err != nil {
diagnose.Error(ctx, err)
}
})

var disableClustering bool
diagnose.Test(ctx, "setup-ha-storage", func(ctx context.Context) error {
Expand Down Expand Up @@ -510,6 +508,9 @@ SEALFAIL:
info := make(map[string]string)
var listeners []listenerutil.Listener
var status int

diagnose.ListenerChecks(ctx, config.Listeners)

diagnose.Test(ctx, "create-listeners", func(ctx context.Context) error {
status, listeners, _, err = server.InitListeners(config, disableClustering, &infoKeys, &info)
if status != 0 {
Expand All @@ -527,32 +528,7 @@ SEALFAIL:
}
}

defer c.cleanupGuard.Do(listenerCloseFunc)

listenerTLSContext, listenerTLSSpan := diagnose.StartSpan(ctx, "check-listener-tls")
sanitizedListeners := make([]listenerutil.Listener, 0, len(config.Listeners))
for _, ln := range lns {
if ln.Config.TLSDisable {
diagnose.Warn(listenerTLSContext, "TLS is disabled in a Listener config stanza.")
continue
}
if ln.Config.TLSDisableClientCerts {
diagnose.Warn(listenerTLSContext, "TLS for a listener is turned on without requiring client certs.")

}
err = diagnose.TLSMutualExclusionCertCheck(ln.Config)
if err != nil {
diagnose.Warn(listenerTLSContext, fmt.Sprintf("TLSDisableClientCerts and TLSRequireAndVerifyClientCert should not both be set. %s", err))
}

sanitizedListeners = append(sanitizedListeners, listenerutil.Listener{
Listener: ln.Listener,
Config: ln.Config,
})
}
diagnose.ListenerChecks(listenerTLSContext, sanitizedListeners)

listenerTLSSpan.End()
c.cleanupGuard.Do(listenerCloseFunc)

return nil
})
Expand Down
64 changes: 55 additions & 9 deletions vault/diagnose/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package diagnose
import (
"context"
"errors"
"github.com/go-test/deep"
"os"
"reflect"
"strings"
"testing"

"github.com/go-test/deep"
)

const getMoreCoffee = "You'll find more coffee in the freezer door, or consider buying more for the office."
Expand All @@ -21,6 +22,26 @@ func TestDiagnoseOtelResults(t *testing.T) {
},
Advice: getMoreCoffee,
Children: []*Result{
{
Name: "prepare-kitchen",
Status: ErrorStatus,
Children: []*Result{
{
Name: "build-microwave",
Status: ErrorStatus,
Children: []*Result{
{
Name: "buy-parts",
Status: ErrorStatus,
Message: "no stores sell microwave parts, please buy a microwave instead.",
Warnings: []string{
"warning: you are about to try to build a microwave from scratch.",
},
},
},
},
},
},
{
Name: "warm-milk",
Status: OkStatus,
Expand Down Expand Up @@ -54,6 +75,7 @@ func TestDiagnoseOtelResults(t *testing.T) {

results := sess.Finalize(ctx)
results.ZeroTimes()

if !reflect.DeepEqual(results, expected) {
t.Fatalf("results mismatch: %s", strings.Join(deep.Equal(results, expected), "\n"))
}
Expand All @@ -63,27 +85,51 @@ func TestDiagnoseOtelResults(t *testing.T) {
const coffeeLeft = 3

func makeCoffee(ctx context.Context) error {

if coffeeLeft < 5 {
Warn(ctx, "coffee getting low")
Advise(ctx, getMoreCoffee)
}

err := Test(ctx, "warm-milk", warmMilk)
if err != nil {
return err
}
// To mimic listener TLS checks, we'll see if we can nest a Test and add errors in the function
Test(ctx, "prepare-kitchen", func(ctx context.Context) error {
return Test(ctx, "build-microwave", func(ctx context.Context) error {
buildMicrowave(ctx)
return nil
})
})

err = brewCoffee(ctx)
if err != nil {
return err
}
Test(ctx, "warm-milk", func(ctx context.Context) error {
return warmMilk(ctx)
})

brewCoffee(ctx)

SpotCheck(ctx, "pick-scone", pickScone)

Test(ctx, "dispose-grounds", Skippable("dispose-grounds", disposeGrounds))
return nil
}

// buildMicrowave will throw an error in the function itself to fail the span,
// but will return nil so the caller test doesn't necessarily throw an error.
// The intended behavior is that the superspan will detect the failed subspan
// and fail regardless. This happens when Fail is used to fail the span, but not
// when Error is used. See the comment in the function itself.
func buildMicrowave(ctx context.Context) error {
ctx, span := StartSpan(ctx, "buy-parts")

Fail(ctx, "no stores sell microwave parts, please buy a microwave instead.")

// The error line here does not actually yield an error in the output.
// TODO: Debug this. In the meantime, always use Fail over Error.
swayne275 marked this conversation as resolved.
Show resolved Hide resolved
// Error(ctx, errors.New("no stores sell microwave parts, please buy a microwave instead."))

Warn(ctx, "warning: you are about to try to build a microwave from scratch.")
span.End()
return nil
}

func warmMilk(ctx context.Context) error {
// Always succeeds
return nil
Expand Down
32 changes: 16 additions & 16 deletions vault/diagnose/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import (
"context"
"errors"
"fmt"
"go.opentelemetry.io/otel/attribute"
"io"
"sort"
"strings"
"sync"
"time"

"go.opentelemetry.io/otel/attribute"

wordwrap "github.com/mitchellh/go-wordwrap"
"go.opentelemetry.io/otel/codes"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
Expand Down Expand Up @@ -318,22 +319,20 @@ func (r *Result) StringWrapped(wrapLimit int) string {
func (r *Result) write(sb *strings.Builder, depth int, limit int) {
indent(sb, depth)
var prelude string
if len(r.Warnings) == 0 {
switch r.Status {
case OkStatus:
prelude = status_ok
case WarningStatus:
prelude = status_warn
case ErrorStatus:
prelude = status_failed
case SkippedStatus:
prelude = status_skipped
}
prelude = prelude + r.Name
switch r.Status {
case OkStatus:
prelude = status_ok
case WarningStatus:
prelude = status_warn
case ErrorStatus:
prelude = status_failed
case SkippedStatus:
prelude = status_skipped
}
prelude = prelude + r.Name

if r.Message != "" {
prelude = prelude + ": " + r.Message
}
if r.Message != "" {
prelude = prelude + ": " + r.Message
}
warnings := r.Warnings
if r.Message == "" && len(warnings) > 0 {
Expand All @@ -343,6 +342,7 @@ func (r *Result) write(sb *strings.Builder, depth int, limit int) {
warnings = warnings[1:]
}
}

writeWrapped(sb, prelude, depth+1, limit)
for _, w := range warnings {
sb.WriteRune('\n')
Expand Down
36 changes: 24 additions & 12 deletions vault/diagnose/tls_verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"time"

"github.com/hashicorp/vault/internalshared/configutil"
"github.com/hashicorp/vault/internalshared/listenerutil"
"github.com/hashicorp/vault/sdk/helper/tlsutil"
)

Expand All @@ -21,17 +20,32 @@ const maxVersionError = "'tls_max_version' value %q not supported, please specif

// ListenerChecks diagnoses warnings and the first encountered error for the listener
// configuration stanzas.
func ListenerChecks(ctx context.Context, listeners []listenerutil.Listener) ([]string, []error) {
func ListenerChecks(ctx context.Context, listeners []*configutil.Listener) ([]string, []error) {
testName := "check-listener-tls"
ctx, span := StartSpan(ctx, testName)
defer span.End()

// These aggregated warnings and errors are returned purely for testing purposes.
// The errors and warnings will report in this function itself.
var listenerWarnings []string
var listenerErrors []error

for _, listener := range listeners {
l := listener.Config
for _, l := range listeners {
listenerID := l.Address

if l.TLSDisable {
Warn(ctx, fmt.Sprintf("listener at address: %s has error: TLS is disabled in a Listener config stanza.", listenerID))
continue
}
if l.TLSDisableClientCerts {
Warn(ctx, fmt.Sprintf("listener at address: %s has error: TLS for a listener is turned on without requiring client certs.", listenerID))

}
status, warning := TLSMutualExclusionCertCheck(l)
if status == 1 {
Warn(ctx, warning)
}

// Perform the TLS version check for listeners.
if l.TLSMinVersion == "" {
l.TLSMinVersion = "tls12"
Expand All @@ -43,13 +57,13 @@ func ListenerChecks(ctx context.Context, listeners []listenerutil.Listener) ([]s
if !ok {
err := fmt.Errorf("listener at address: %s has error %s: ", listenerID, fmt.Sprintf(minVersionError, l.TLSMinVersion))
listenerErrors = append(listenerErrors, err)
Error(ctx, err)
Fail(ctx, err.Error())
}
_, ok = tlsutil.TLSLookup[l.TLSMaxVersion]
if !ok {
err := fmt.Errorf("listener at address: %s has error %s: ", listenerID, fmt.Sprintf(maxVersionError, l.TLSMaxVersion))
listenerErrors = append(listenerErrors, err)
Error(ctx, err)
Fail(ctx, err.Error())
}

// Perform checks on the TLS Cryptographic Information.
Expand All @@ -74,8 +88,7 @@ func outputError(ctx context.Context, newWarnings, listenerWarnings []string, ne
if newErr != nil {
errMsg := listenerID + ": " + newErr.Error()
listenerErrors = append(listenerErrors, fmt.Errorf(errMsg))
Error(ctx, fmt.Errorf(errMsg))

Fail(ctx, errMsg)
}
return listenerWarnings, listenerErrors
}
Expand Down Expand Up @@ -256,15 +269,14 @@ func NearExpiration(c *x509.Certificate) (bool, time.Duration) {
}

// TLSMutualExclusionCertCheck returns error if both TLSDisableClientCerts and TLSRequireAndVerifyClientCert are set
func TLSMutualExclusionCertCheck(l *configutil.Listener) error {
func TLSMutualExclusionCertCheck(l *configutil.Listener) (int, string) {

if l.TLSDisableClientCerts {
if l.TLSRequireAndVerifyClientCert {
return fmt.Errorf("the tls_disable_client_certs and tls_require_and_verify_client_cert fields in the " +
"listener stanza of the vault server config are mutually exclusive fields. Please ensure they are not both set to true.")
return 1, "the tls_disable_client_certs and tls_require_and_verify_client_cert fields in the listener stanza of the vault server config are mutually exclusive fields. Please ensure they are not both set to true."
}
}
return nil
return 0, ""
}

// TLSClientCAFileCheck Checks the validity of a client CA file
Expand Down
Loading