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

@@ -886,9 +886,9 @@ func (c *ServerCommand) InitListeners(config *server.Config, disableClustering b
} }
if reloadFunc != nil { if reloadFunc != nil {
relSlice := (*c.reloadFuncs)["listener|"+lnConfig.Type] relSlice := (*c.reloadFuncs)[fmt.Sprintf("listener|%s", lnConfig.Type)]
relSlice = append(relSlice, reloadFunc) relSlice = append(relSlice, reloadFunc)
(*c.reloadFuncs)["listener|"+lnConfig.Type] = relSlice (*c.reloadFuncs)[fmt.Sprintf("listener|%s", lnConfig.Type)] = relSlice
} }
if !disableClustering && lnConfig.Type == "tcp" { if !disableClustering && lnConfig.Type == "tcp" {

View File

@@ -793,7 +793,7 @@ func testConfig_Sanitized(t *testing.T) {
"address": "127.0.0.1:443", "address": "127.0.0.1:443",
"chroot_namespace": "admin/", "chroot_namespace": "admin/",
}, },
"type": "tcp", "type": configutil.TCP,
}, },
}, },
"log_format": "", "log_format": "",
@@ -890,6 +890,15 @@ listener "tcp" {
redact_addresses = true redact_addresses = true
redact_cluster_name = true redact_cluster_name = true
redact_version = true redact_version = true
}
listener "unix" {
address = "/var/run/vault.sock"
socket_mode = "644"
socket_user = "1000"
socket_group = "1000"
redact_addresses = true
redact_cluster_name = true
redact_version = true
}`)) }`))
config := Config{ config := Config{
@@ -903,16 +912,14 @@ listener "tcp" {
config.Listeners = listeners config.Listeners = listeners
// Track which types of listener were found. // Track which types of listener were found.
for _, l := range config.Listeners { for _, l := range config.Listeners {
config.found(l.Type, l.Type) config.found(l.Type.String(), l.Type.String())
} }
if len(config.Listeners) == 0 { require.Len(t, config.Listeners, 2)
t.Fatalf("expected at least one listener in the config") tcpListener := config.Listeners[0]
} require.Equal(t, configutil.TCP, tcpListener.Type)
listener := config.Listeners[0] unixListner := config.Listeners[1]
if listener.Type != "tcp" { require.Equal(t, configutil.Unix, unixListner.Type)
t.Fatalf("expected tcp listener in the config")
}
expected := &Config{ expected := &Config{
SharedConfig: &configutil.SharedConfig{ SharedConfig: &configutil.SharedConfig{
@@ -946,6 +953,16 @@ listener "tcp" {
RedactClusterName: true, RedactClusterName: true,
RedactVersion: true, RedactVersion: true,
}, },
{
Type: "unix",
Address: "/var/run/vault.sock",
SocketMode: "644",
SocketUser: "1000",
SocketGroup: "1000",
RedactAddresses: false,
RedactClusterName: false,
RedactVersion: false,
},
}, },
}, },
} }

View File

@@ -22,7 +22,7 @@ import (
type ListenerFactory func(*configutil.Listener, io.Writer, cli.Ui) (net.Listener, map[string]string, reloadutil.ReloadFunc, error) type ListenerFactory func(*configutil.Listener, io.Writer, cli.Ui) (net.Listener, map[string]string, reloadutil.ReloadFunc, error)
// BuiltinListeners is the list of built-in listener types. // BuiltinListeners is the list of built-in listener types.
var BuiltinListeners = map[string]ListenerFactory{ var BuiltinListeners = map[configutil.ListenerType]ListenerFactory{
"tcp": tcpListenerFactory, "tcp": tcpListenerFactory,
"unix": unixListenerFactory, "unix": unixListenerFactory,
} }

View File

@@ -141,7 +141,7 @@ func ParseConfig(d string) (*SharedConfig, error) {
// Track which types of listener were found. // Track which types of listener were found.
for _, l := range result.Listeners { 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" "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 { type ListenerTelemetry struct {
UnusedKeys UnusedKeyMap `hcl:",unusedKeyPositions"` UnusedKeys UnusedKeyMap `hcl:",unusedKeyPositions"`
UnauthenticatedMetricsAccess bool `hcl:"-"` UnauthenticatedMetricsAccess bool `hcl:"-"`
@@ -45,7 +53,7 @@ type Listener struct {
UnusedKeys UnusedKeyMap `hcl:",unusedKeyPositions"` UnusedKeys UnusedKeyMap `hcl:",unusedKeyPositions"`
RawConfig map[string]interface{} RawConfig map[string]interface{}
Type string Type ListenerType
Purpose []string `hcl:"-"` Purpose []string `hcl:"-"`
PurposeRaw interface{} `hcl:"purpose"` PurposeRaw interface{} `hcl:"purpose"`
Role string `hcl:"role"` Role string `hcl:"role"`
@@ -254,6 +262,16 @@ func parseListener(item *ast.ObjectItem) (*Listener, error) {
return l, nil 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. // parseChrootNamespace attempts to parse the raw listener chroot namespace settings.
// The state of the listener will be modified, raw data will be cleared upon // The state of the listener will be modified, raw data will be cleared upon
// successful parsing. // successful parsing.
@@ -286,20 +304,21 @@ func (l *Listener) parseType(fallback string) error {
} }
// Use type if available, otherwise fall back. // Use type if available, otherwise fall back.
result := l.Type rawType := l.Type
if result == "" { if rawType == "" {
result = fallback rawType = ListenerType(fallback)
} }
result = strings.ToLower(result)
parsedType := rawType.Normalize()
// Sanity check the values // Sanity check the values
switch result { switch parsedType {
case "tcp", "unix": case TCP, Unix:
default: 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 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 // The state of the listener will be modified, raw data will be cleared upon
// successful parsing. // successful parsing.
func (l *Listener) parseHTTPHeaderSettings() error { 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 // if CustomResponseHeadersRaw is nil, we still need to set the default headers
customHeadersMap, err := ParseCustomResponseHeaders(l.CustomResponseHeadersRaw) customHeadersMap, err := ParseCustomResponseHeaders(l.CustomResponseHeadersRaw)
if err != nil { 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 // The state of the listener will be modified, raw data will be cleared upon
// successful parsing. // successful parsing.
func (l *Listener) parseRedactionSettings() error { 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 var err error
if l.RedactAddressesRaw != nil { if l.RedactAddressesRaw != nil {

View File

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