Listeners: Redaction only for TCP (#23592)

* redaction should only work for TCP listeners, also fix bug that allowed custom response headers for unix listeners

* fix failing test

* updates from PR feedback
This commit is contained in:
Peter Wilson
2023-10-11 17:38:05 +01:00
committed by GitHub
parent 525bf2f894
commit 813c786032
6 changed files with 113 additions and 24 deletions

View File

@@ -141,7 +141,7 @@ func ParseConfig(d string) (*SharedConfig, error) {
// Track which types of listener were found.
for _, l := range result.Listeners {
result.found(l.Type, l.Type)
result.found(l.Type.String(), l.Type.String())
}
}

View File

@@ -22,6 +22,14 @@ import (
"github.com/hashicorp/vault/helper/namespace"
)
const (
TCP ListenerType = "tcp"
Unix ListenerType = "unix"
)
// ListenerType represents the supported types of listener.
type ListenerType string
type ListenerTelemetry struct {
UnusedKeys UnusedKeyMap `hcl:",unusedKeyPositions"`
UnauthenticatedMetricsAccess bool `hcl:"-"`
@@ -45,7 +53,7 @@ type Listener struct {
UnusedKeys UnusedKeyMap `hcl:",unusedKeyPositions"`
RawConfig map[string]interface{}
Type string
Type ListenerType
Purpose []string `hcl:"-"`
PurposeRaw interface{} `hcl:"purpose"`
Role string `hcl:"role"`
@@ -254,6 +262,16 @@ func parseListener(item *ast.ObjectItem) (*Listener, error) {
return l, nil
}
// Normalize returns the lower case string version of a listener type.
func (t ListenerType) Normalize() ListenerType {
return ListenerType(strings.ToLower(string(t)))
}
// String returns the string version of a listener type.
func (t ListenerType) String() string {
return string(t.Normalize())
}
// parseChrootNamespace attempts to parse the raw listener chroot namespace settings.
// The state of the listener will be modified, raw data will be cleared upon
// successful parsing.
@@ -286,20 +304,21 @@ func (l *Listener) parseType(fallback string) error {
}
// Use type if available, otherwise fall back.
result := l.Type
if result == "" {
result = fallback
rawType := l.Type
if rawType == "" {
rawType = ListenerType(fallback)
}
result = strings.ToLower(result)
parsedType := rawType.Normalize()
// Sanity check the values
switch result {
case "tcp", "unix":
switch parsedType {
case TCP, Unix:
default:
return fmt.Errorf("unsupported listener type %q", result)
return fmt.Errorf("unsupported listener type %q", parsedType)
}
l.Type = result
l.Type = parsedType
return nil
}
@@ -396,6 +415,13 @@ func (l *Listener) parseTLSSettings() error {
// The state of the listener will be modified, raw data will be cleared upon
// successful parsing.
func (l *Listener) parseHTTPHeaderSettings() error {
// Custom response headers are only supported by TCP listeners.
// Clear raw data and return early if it was something else.
if l.Type != TCP {
l.CustomResponseHeadersRaw = nil
return nil
}
// if CustomResponseHeadersRaw is nil, we still need to set the default headers
customHeadersMap, err := ParseCustomResponseHeaders(l.CustomResponseHeadersRaw)
if err != nil {
@@ -604,6 +630,16 @@ func (l *Listener) parseCORSSettings() error {
// The state of the listener will be modified, raw data will be cleared upon
// successful parsing.
func (l *Listener) parseRedactionSettings() error {
// Redaction is only supported on TCP listeners.
// Clear raw data and return early if it was something else.
if l.Type != TCP {
l.RedactAddressesRaw = nil
l.RedactClusterNameRaw = nil
l.RedactVersionRaw = nil
return nil
}
var err error
if l.RedactAddressesRaw != nil {

View File

@@ -159,7 +159,7 @@ func TestListener_parseType(t *testing.T) {
tc := tc
t.Run(name, func(t *testing.T) {
t.Parallel()
l := &Listener{Type: tc.inputType}
l := &Listener{Type: ListenerType(tc.inputType)}
err := l.parseType(tc.inputFallback)
switch {
case tc.isErrorExpected:
@@ -167,7 +167,7 @@ func TestListener_parseType(t *testing.T) {
require.ErrorContains(t, err, tc.errorMessage)
default:
require.NoError(t, err)
require.Equal(t, tc.expectedValue, l.Type)
require.Equal(t, tc.expectedValue, l.Type.String())
}
})
}
@@ -861,16 +861,19 @@ func TestListener_parseCORSSettings(t *testing.T) {
// assign the relevant value on the SharedConfig struct.
func TestListener_parseHTTPHeaderSettings(t *testing.T) {
tests := map[string]struct {
listenerType ListenerType
rawCustomResponseHeaders []map[string]any
expectedNumCustomResponseHeaders int
isErrorExpected bool
errorMessage string
}{
"nil": {
listenerType: TCP,
isErrorExpected: false,
expectedNumCustomResponseHeaders: 1, // default: Strict-Transport-Security
},
"custom-headers-bad": {
listenerType: TCP,
rawCustomResponseHeaders: []map[string]any{
{"juan": false},
},
@@ -878,6 +881,7 @@ func TestListener_parseHTTPHeaderSettings(t *testing.T) {
errorMessage: "failed to parse custom_response_headers",
},
"custom-headers-good": {
listenerType: TCP,
rawCustomResponseHeaders: []map[string]any{
{
"2xx": []map[string]any{
@@ -888,6 +892,18 @@ func TestListener_parseHTTPHeaderSettings(t *testing.T) {
expectedNumCustomResponseHeaders: 2,
isErrorExpected: false,
},
"unix-no-headers": {
listenerType: Unix,
rawCustomResponseHeaders: []map[string]any{
{
"2xx": []map[string]any{
{"X-Custom-Header": []any{"Custom Header Value 1", "Custom Header Value 2"}},
},
},
},
expectedNumCustomResponseHeaders: 0,
isErrorExpected: false,
},
}
for name, tc := range tests {
@@ -898,6 +914,7 @@ func TestListener_parseHTTPHeaderSettings(t *testing.T) {
// Configure listener with raw values
l := &Listener{
Type: tc.listenerType,
CustomResponseHeadersRaw: tc.rawCustomResponseHeaders,
}
@@ -978,6 +995,7 @@ func TestListener_parseChrootNamespaceSettings(t *testing.T) {
// assign the relevant value on the SharedConfig struct.
func TestListener_parseRedactionSettings(t *testing.T) {
tests := map[string]struct {
listenerType ListenerType
rawRedactAddresses any
expectedRedactAddresses bool
rawRedactClusterName any
@@ -988,41 +1006,58 @@ func TestListener_parseRedactionSettings(t *testing.T) {
errorMessage string
}{
"missing": {
listenerType: TCP,
isErrorExpected: false,
expectedRedactAddresses: false,
expectedRedactClusterName: false,
expectedRedactVersion: false,
},
"redact-addresses-bad": {
listenerType: TCP,
rawRedactAddresses: "juan",
isErrorExpected: true,
errorMessage: "invalid value for redact_addresses",
},
"redact-addresses-good": {
listenerType: TCP,
rawRedactAddresses: "true",
expectedRedactAddresses: true,
isErrorExpected: false,
},
"redact-cluster-name-bad": {
listenerType: TCP,
rawRedactClusterName: "juan",
isErrorExpected: true,
errorMessage: "invalid value for redact_cluster_name",
},
"redact-cluster-name-good": {
listenerType: TCP,
rawRedactClusterName: "true",
expectedRedactClusterName: true,
isErrorExpected: false,
},
"redact-version-bad": {
listenerType: TCP,
rawRedactVersion: "juan",
isErrorExpected: true,
errorMessage: "invalid value for redact_version",
},
"redact-version-good": {
listenerType: TCP,
rawRedactVersion: "true",
expectedRedactVersion: true,
isErrorExpected: false,
},
"redact-unix-na": {
listenerType: Unix,
rawRedactAddresses: "true",
expectedRedactAddresses: false,
rawRedactClusterName: "true",
expectedRedactClusterName: false,
rawRedactVersion: "true",
expectedRedactVersion: false,
isErrorExpected: false,
},
}
for name, tc := range tests {
@@ -1033,6 +1068,7 @@ func TestListener_parseRedactionSettings(t *testing.T) {
// Configure listener with raw values
l := &Listener{
Type: tc.listenerType,
RedactAddressesRaw: tc.rawRedactAddresses,
RedactClusterNameRaw: tc.rawRedactClusterName,
RedactVersionRaw: tc.rawRedactVersion,