mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-02 03:27:54 +00:00
Config: Listener parsing clean-up (#23502)
* Updated test for go-sockaddr template * Rename test * Tried to break up listener config parsing and add tests
This commit is contained in:
@@ -893,12 +893,19 @@ listener "tcp" {
|
|||||||
}
|
}
|
||||||
list, _ := obj.Node.(*ast.ObjectList)
|
list, _ := obj.Node.(*ast.ObjectList)
|
||||||
objList := list.Filter("listener")
|
objList := list.Filter("listener")
|
||||||
configutil.ParseListeners(config.SharedConfig, objList)
|
listeners, err := configutil.ParseListeners(objList)
|
||||||
listeners := config.Listeners
|
require.NoError(t, err)
|
||||||
if len(listeners) == 0 {
|
// Update the shared config
|
||||||
|
config.Listeners = listeners
|
||||||
|
// Track which types of listener were found.
|
||||||
|
for _, l := range config.Listeners {
|
||||||
|
config.found(l.Type, l.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(config.Listeners) == 0 {
|
||||||
t.Fatalf("expected at least one listener in the config")
|
t.Fatalf("expected at least one listener in the config")
|
||||||
}
|
}
|
||||||
listener := listeners[0]
|
listener := config.Listeners[0]
|
||||||
if listener.Type != "tcp" {
|
if listener.Type != "tcp" {
|
||||||
t.Fatalf("expected tcp listener in the config")
|
t.Fatalf("expected tcp listener in the config")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,9 +122,17 @@ func ParseConfig(d string) (*SharedConfig, error) {
|
|||||||
|
|
||||||
if o := list.Filter("listener"); len(o.Items) > 0 {
|
if o := list.Filter("listener"); len(o.Items) > 0 {
|
||||||
result.found("listener", "Listener")
|
result.found("listener", "Listener")
|
||||||
if err := ParseListeners(&result, o); err != nil {
|
listeners, err := ParseListeners(o)
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error parsing 'listener': %w", err)
|
return nil, fmt.Errorf("error parsing 'listener': %w", err)
|
||||||
}
|
}
|
||||||
|
// Update the shared config
|
||||||
|
result.Listeners = listeners
|
||||||
|
|
||||||
|
// Track which types of listener were found.
|
||||||
|
for _, l := range result.Listeners {
|
||||||
|
result.found(l.Type, l.Type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if o := list.Filter("user_lockout"); len(o.Items) > 0 {
|
if o := list.Filter("user_lockout"); len(o.Items) > 0 {
|
||||||
|
|||||||
@@ -144,268 +144,413 @@ func (l *Listener) Validate(path string) []ConfigError {
|
|||||||
return append(results, ValidateUnusedFields(l.Profiling.UnusedKeys, path)...)
|
return append(results, ValidateUnusedFields(l.Profiling.UnusedKeys, path)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseListeners(result *SharedConfig, list *ast.ObjectList) error {
|
// ParseListeners attempts to parse the AST list of objects into listeners.
|
||||||
var err error
|
func ParseListeners(list *ast.ObjectList) ([]*Listener, error) {
|
||||||
result.Listeners = make([]*Listener, 0, len(list.Items))
|
listeners := make([]*Listener, len(list.Items))
|
||||||
|
|
||||||
for i, item := range list.Items {
|
for i, item := range list.Items {
|
||||||
var l Listener
|
l, err := parseListener(item)
|
||||||
if err := hcl.DecodeObject(&l, item.Val); err != nil {
|
if err != nil {
|
||||||
return multierror.Prefix(err, fmt.Sprintf("listeners.%d:", i))
|
return nil, multierror.Prefix(err, fmt.Sprintf("listeners.%d:", i))
|
||||||
}
|
}
|
||||||
if rendered, err := ParseSingleIPTemplate(l.Address); err != nil {
|
listeners[i] = l
|
||||||
return multierror.Prefix(err, fmt.Sprintf("listeners.%d:", i))
|
|
||||||
} else {
|
|
||||||
l.Address = rendered
|
|
||||||
}
|
|
||||||
if rendered, err := ParseSingleIPTemplate(l.ClusterAddress); err != nil {
|
|
||||||
return multierror.Prefix(err, fmt.Sprintf("listeners.%d:", i))
|
|
||||||
} else {
|
|
||||||
l.ClusterAddress = rendered
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hacky way, for now, to get the values we want for sanitizing
|
return listeners, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseListener attempts to parse the AST object into a listener.
|
||||||
|
func parseListener(item *ast.ObjectItem) (*Listener, error) {
|
||||||
|
var l *Listener
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Decode the current item
|
||||||
|
if err = hcl.DecodeObject(&l, item.Val); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse and update address if required.
|
||||||
|
if l.Address, err = ParseSingleIPTemplate(l.Address); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse and update cluster address if required.
|
||||||
|
if l.ClusterAddress, err = ParseSingleIPTemplate(l.ClusterAddress); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the values for sanitizing
|
||||||
var m map[string]interface{}
|
var m map[string]interface{}
|
||||||
if err := hcl.DecodeObject(&m, item.Val); err != nil {
|
if err := hcl.DecodeObject(&m, item.Val); err != nil {
|
||||||
return multierror.Prefix(err, fmt.Sprintf("listeners.%d:", i))
|
return nil, err
|
||||||
}
|
}
|
||||||
l.RawConfig = m
|
l.RawConfig = m
|
||||||
|
|
||||||
// Base values
|
// Parse type, but supply a fallback if type wasn't set.
|
||||||
{
|
var fallbackType string
|
||||||
|
if len(item.Keys) == 1 {
|
||||||
|
fallbackType = strings.ToLower(item.Keys[0].Token.Value().(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = l.parseType(fallbackType); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse out each set off settings for the listener.
|
||||||
|
for _, parser := range []func() error{
|
||||||
|
l.parseRequestSettings,
|
||||||
|
l.parseTLSSettings,
|
||||||
|
l.parseHTTPTimeoutSettings,
|
||||||
|
l.parseProxySettings,
|
||||||
|
l.parseForwardedForSettings,
|
||||||
|
l.parseTelemetrySettings,
|
||||||
|
l.parseProfilingSettings,
|
||||||
|
l.parseInFlightRequestSettings,
|
||||||
|
l.parseCORSSettings,
|
||||||
|
l.parseHTTPHeaderSettings,
|
||||||
|
l.parseChrootNamespaceSettings,
|
||||||
|
} {
|
||||||
|
err := parser()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func (l *Listener) parseChrootNamespaceSettings() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// If a valid ChrootNamespace value exists, then canonicalize the namespace value
|
||||||
|
if l.ChrootNamespaceRaw != nil {
|
||||||
|
l.ChrootNamespace, err = parseutil.ParseString(l.ChrootNamespaceRaw)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid value for chroot_namespace: %w", err)
|
||||||
|
}
|
||||||
|
l.ChrootNamespace = namespace.Canonicalize(l.ChrootNamespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.ChrootNamespaceRaw = nil
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseType attempts to sanitize and validate the type set on the listener.
|
||||||
|
// If the listener has no type set, the fallback value will be used.
|
||||||
|
// The state of the listener will be modified.
|
||||||
|
func (l *Listener) parseType(fallback string) error {
|
||||||
switch {
|
switch {
|
||||||
case l.Type != "":
|
case l.Type != "":
|
||||||
case len(item.Keys) == 1:
|
case fallback != "":
|
||||||
l.Type = strings.ToLower(item.Keys[0].Token.Value().(string))
|
|
||||||
default:
|
default:
|
||||||
return multierror.Prefix(errors.New("listener type must be specified"), fmt.Sprintf("listeners.%d:", i))
|
return errors.New("listener type must be specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Type = strings.ToLower(l.Type)
|
// Use type if available, otherwise fall back.
|
||||||
switch l.Type {
|
result := l.Type
|
||||||
|
if result == "" {
|
||||||
|
result = fallback
|
||||||
|
}
|
||||||
|
result = strings.ToLower(result)
|
||||||
|
|
||||||
|
// Sanity check the values
|
||||||
|
switch result {
|
||||||
case "tcp", "unix":
|
case "tcp", "unix":
|
||||||
result.found(l.Type, l.Type)
|
|
||||||
default:
|
default:
|
||||||
return multierror.Prefix(fmt.Errorf("unsupported listener type %q", l.Type), fmt.Sprintf("listeners.%d:", i))
|
return fmt.Errorf("unsupported listener type %q", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.PurposeRaw != nil {
|
l.Type = result
|
||||||
if l.Purpose, err = parseutil.ParseCommaStringSlice(l.PurposeRaw); err != nil {
|
|
||||||
return multierror.Prefix(fmt.Errorf("unable to parse 'purpose' in listener type %q: %w", l.Type, err), fmt.Sprintf("listeners.%d:", i))
|
|
||||||
}
|
|
||||||
for i, v := range l.Purpose {
|
|
||||||
l.Purpose[i] = strings.ToLower(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.PurposeRaw = nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch l.Role {
|
// parseRequestSettings attempts to parse the raw listener request settings.
|
||||||
case "default", "metrics_only", "":
|
// The state of the listener will be modified, raw data will be cleared upon
|
||||||
result.found(l.Type, l.Type)
|
// successful parsing.
|
||||||
default:
|
func (l *Listener) parseRequestSettings() error {
|
||||||
return multierror.Prefix(fmt.Errorf("unsupported listener role %q", l.Role), fmt.Sprintf("listeners.%d:", i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Request Parameters
|
|
||||||
{
|
|
||||||
if l.MaxRequestSizeRaw != nil {
|
if l.MaxRequestSizeRaw != nil {
|
||||||
if l.MaxRequestSize, err = parseutil.ParseInt(l.MaxRequestSizeRaw); err != nil {
|
maxRequestSize, err := parseutil.ParseInt(l.MaxRequestSizeRaw)
|
||||||
return multierror.Prefix(fmt.Errorf("error parsing max_request_size: %w", err), fmt.Sprintf("listeners.%d", i))
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing max_request_size: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
l.MaxRequestSizeRaw = nil
|
l.MaxRequestSize = maxRequestSize
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.MaxRequestDurationRaw != nil {
|
if l.MaxRequestDurationRaw != nil {
|
||||||
if l.MaxRequestDuration, err = parseutil.ParseDurationSecond(l.MaxRequestDurationRaw); err != nil {
|
maxRequestDuration, err := parseutil.ParseDurationSecond(l.MaxRequestDurationRaw)
|
||||||
return multierror.Prefix(fmt.Errorf("error parsing max_request_duration: %w", err), fmt.Sprintf("listeners.%d", i))
|
if err != nil {
|
||||||
}
|
return fmt.Errorf("error parsing max_request_duration: %w", err)
|
||||||
if l.MaxRequestDuration < 0 {
|
|
||||||
return multierror.Prefix(errors.New("max_request_duration cannot be negative"), fmt.Sprintf("listeners.%d", i))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
l.MaxRequestDurationRaw = nil
|
if maxRequestDuration < 0 {
|
||||||
|
return errors.New("max_request_duration cannot be negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
l.MaxRequestDuration = maxRequestDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.RequireRequestHeaderRaw != nil {
|
if l.RequireRequestHeaderRaw != nil {
|
||||||
if l.RequireRequestHeader, err = parseutil.ParseBool(l.RequireRequestHeaderRaw); err != nil {
|
requireRequestHeader, err := parseutil.ParseBool(l.RequireRequestHeaderRaw)
|
||||||
return multierror.Prefix(fmt.Errorf("invalid value for require_request_header: %w", err), fmt.Sprintf("listeners.%d", i))
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid value for require_request_header: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
l.RequireRequestHeader = requireRequestHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear raw values after successful parsing.
|
||||||
|
l.MaxRequestSizeRaw = nil
|
||||||
|
l.MaxRequestDurationRaw = nil
|
||||||
l.RequireRequestHeaderRaw = nil
|
l.RequireRequestHeaderRaw = nil
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TLS Parameters
|
return nil
|
||||||
{
|
}
|
||||||
|
|
||||||
|
// parseTLSSettings attempts to parse the raw listener TLS settings.
|
||||||
|
// The state of the listener will be modified, raw data will be cleared upon
|
||||||
|
// successful parsing.
|
||||||
|
func (l *Listener) parseTLSSettings() error {
|
||||||
if l.TLSDisableRaw != nil {
|
if l.TLSDisableRaw != nil {
|
||||||
if l.TLSDisable, err = parseutil.ParseBool(l.TLSDisableRaw); err != nil {
|
tlsDisable, err := parseutil.ParseBool(l.TLSDisableRaw)
|
||||||
return multierror.Prefix(fmt.Errorf("invalid value for tls_disable: %w", err), fmt.Sprintf("listeners.%d", i))
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid value for tls_disable: %w", err)
|
||||||
}
|
}
|
||||||
|
l.TLSDisable = tlsDisable
|
||||||
l.TLSDisableRaw = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.TLSCipherSuitesRaw != "" {
|
if l.TLSCipherSuitesRaw != "" {
|
||||||
if l.TLSCipherSuites, err = tlsutil.ParseCiphers(l.TLSCipherSuitesRaw); err != nil {
|
tlsCipherSuites, err := tlsutil.ParseCiphers(l.TLSCipherSuitesRaw)
|
||||||
return multierror.Prefix(fmt.Errorf("invalid value for tls_cipher_suites: %w", err), fmt.Sprintf("listeners.%d", i))
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid value for tls_cipher_suites: %w", err)
|
||||||
}
|
}
|
||||||
|
l.TLSCipherSuites = tlsCipherSuites
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.TLSRequireAndVerifyClientCertRaw != nil {
|
if l.TLSRequireAndVerifyClientCertRaw != nil {
|
||||||
if l.TLSRequireAndVerifyClientCert, err = parseutil.ParseBool(l.TLSRequireAndVerifyClientCertRaw); err != nil {
|
tlsRequireAndVerifyClientCert, err := parseutil.ParseBool(l.TLSRequireAndVerifyClientCertRaw)
|
||||||
return multierror.Prefix(fmt.Errorf("invalid value for tls_require_and_verify_client_cert: %w", err), fmt.Sprintf("listeners.%d", i))
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid value for tls_require_and_verify_client_cert: %w", err)
|
||||||
}
|
}
|
||||||
|
l.TLSRequireAndVerifyClientCert = tlsRequireAndVerifyClientCert
|
||||||
l.TLSRequireAndVerifyClientCertRaw = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.TLSDisableClientCertsRaw != nil {
|
if l.TLSDisableClientCertsRaw != nil {
|
||||||
if l.TLSDisableClientCerts, err = parseutil.ParseBool(l.TLSDisableClientCertsRaw); err != nil {
|
tlsDisableClientCerts, err := parseutil.ParseBool(l.TLSDisableClientCertsRaw)
|
||||||
return multierror.Prefix(fmt.Errorf("invalid value for tls_disable_client_certs: %w", err), fmt.Sprintf("listeners.%d", i))
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid value for tls_disable_client_certs: %w", err)
|
||||||
|
}
|
||||||
|
l.TLSDisableClientCerts = tlsDisableClientCerts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear raw values after successful parsing.
|
||||||
|
l.TLSDisableRaw = nil
|
||||||
|
l.TLSCipherSuitesRaw = ""
|
||||||
|
l.TLSRequireAndVerifyClientCertRaw = nil
|
||||||
l.TLSDisableClientCertsRaw = nil
|
l.TLSDisableClientCertsRaw = nil
|
||||||
}
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseHTTPHeaderSettings attempts to parse the raw listener HTTP header settings.
|
||||||
|
// The state of the listener will be modified, raw data will be cleared upon
|
||||||
|
// successful parsing.
|
||||||
|
func (l *Listener) parseHTTPHeaderSettings() error {
|
||||||
|
// if CustomResponseHeadersRaw is nil, we still need to set the default headers
|
||||||
|
customHeadersMap, err := ParseCustomResponseHeaders(l.CustomResponseHeadersRaw)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse custom_response_headers: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP timeouts
|
l.CustomResponseHeaders = customHeadersMap
|
||||||
{
|
l.CustomResponseHeadersRaw = nil
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseHTTPTimeoutSettings attempts to parse the raw listener HTTP timeout settings.
|
||||||
|
// The state of the listener will be modified, raw data will be cleared upon
|
||||||
|
// successful parsing.
|
||||||
|
func (l *Listener) parseHTTPTimeoutSettings() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
if l.HTTPReadTimeoutRaw != nil {
|
if l.HTTPReadTimeoutRaw != nil {
|
||||||
if l.HTTPReadTimeout, err = parseutil.ParseDurationSecond(l.HTTPReadTimeoutRaw); err != nil {
|
if l.HTTPReadTimeout, err = parseutil.ParseDurationSecond(l.HTTPReadTimeoutRaw); err != nil {
|
||||||
return multierror.Prefix(fmt.Errorf("error parsing http_read_timeout: %w", err), fmt.Sprintf("listeners.%d", i))
|
return fmt.Errorf("error parsing http_read_timeout: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
l.HTTPReadTimeoutRaw = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.HTTPReadHeaderTimeoutRaw != nil {
|
if l.HTTPReadHeaderTimeoutRaw != nil {
|
||||||
if l.HTTPReadHeaderTimeout, err = parseutil.ParseDurationSecond(l.HTTPReadHeaderTimeoutRaw); err != nil {
|
if l.HTTPReadHeaderTimeout, err = parseutil.ParseDurationSecond(l.HTTPReadHeaderTimeoutRaw); err != nil {
|
||||||
return multierror.Prefix(fmt.Errorf("error parsing http_read_header_timeout: %w", err), fmt.Sprintf("listeners.%d", i))
|
return fmt.Errorf("error parsing http_read_header_timeout: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
l.HTTPReadHeaderTimeoutRaw = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.HTTPWriteTimeoutRaw != nil {
|
if l.HTTPWriteTimeoutRaw != nil {
|
||||||
if l.HTTPWriteTimeout, err = parseutil.ParseDurationSecond(l.HTTPWriteTimeoutRaw); err != nil {
|
if l.HTTPWriteTimeout, err = parseutil.ParseDurationSecond(l.HTTPWriteTimeoutRaw); err != nil {
|
||||||
return multierror.Prefix(fmt.Errorf("error parsing http_write_timeout: %w", err), fmt.Sprintf("listeners.%d", i))
|
return fmt.Errorf("error parsing http_write_timeout: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
l.HTTPWriteTimeoutRaw = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.HTTPIdleTimeoutRaw != nil {
|
if l.HTTPIdleTimeoutRaw != nil {
|
||||||
if l.HTTPIdleTimeout, err = parseutil.ParseDurationSecond(l.HTTPIdleTimeoutRaw); err != nil {
|
if l.HTTPIdleTimeout, err = parseutil.ParseDurationSecond(l.HTTPIdleTimeoutRaw); err != nil {
|
||||||
return multierror.Prefix(fmt.Errorf("error parsing http_idle_timeout: %w", err), fmt.Sprintf("listeners.%d", i))
|
return fmt.Errorf("error parsing http_idle_timeout: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear raw values after successful parsing.
|
||||||
|
l.HTTPReadTimeoutRaw = nil
|
||||||
|
l.HTTPReadHeaderTimeoutRaw = nil
|
||||||
|
l.HTTPWriteTimeoutRaw = nil
|
||||||
l.HTTPIdleTimeoutRaw = nil
|
l.HTTPIdleTimeoutRaw = nil
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Proxy Protocol config
|
return nil
|
||||||
{
|
}
|
||||||
|
|
||||||
|
// parseProxySettings attempts to parse the raw listener proxy settings.
|
||||||
|
// The state of the listener will be modified, raw data will be cleared upon
|
||||||
|
// successful parsing.
|
||||||
|
func (l *Listener) parseProxySettings() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
if l.ProxyProtocolAuthorizedAddrsRaw != nil {
|
if l.ProxyProtocolAuthorizedAddrsRaw != nil {
|
||||||
if l.ProxyProtocolAuthorizedAddrs, err = parseutil.ParseAddrs(l.ProxyProtocolAuthorizedAddrsRaw); err != nil {
|
l.ProxyProtocolAuthorizedAddrs, err = parseutil.ParseAddrs(l.ProxyProtocolAuthorizedAddrsRaw)
|
||||||
return multierror.Prefix(fmt.Errorf("error parsing proxy_protocol_authorized_addrs: %w", err), fmt.Sprintf("listeners.%d", i))
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing proxy_protocol_authorized_addrs: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validation/sanity check on allowed settings for behavior.
|
||||||
switch l.ProxyProtocolBehavior {
|
switch l.ProxyProtocolBehavior {
|
||||||
case "allow_authorized", "deny_authorized":
|
case "allow_authorized", "deny_authorized", "use_always", "":
|
||||||
if len(l.ProxyProtocolAuthorizedAddrs) == 0 {
|
// Ignore these cases, they're all valid values.
|
||||||
return multierror.Prefix(errors.New("proxy_protocol_behavior set to allow or deny only authorized addresses but no proxy_protocol_authorized_addrs value"), fmt.Sprintf("listeners.%d", i))
|
// In the case of 'allow_authorized' and 'deny_authorized', we don't need
|
||||||
}
|
// to check how many addresses we have in ProxyProtocolAuthorizedAddrs
|
||||||
|
// as parseutil.ParseAddrs returns "one or more addresses" (or an error)
|
||||||
|
// so we'd have returned earlier.
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported value supplied for proxy_protocol_behavior: %q", l.ProxyProtocolBehavior)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear raw values after successful parsing.
|
||||||
l.ProxyProtocolAuthorizedAddrsRaw = nil
|
l.ProxyProtocolAuthorizedAddrsRaw = nil
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// X-Forwarded-For config
|
return nil
|
||||||
{
|
}
|
||||||
|
|
||||||
|
// parseForwardedForSettings attempts to parse the raw listener x-forwarded-for settings.
|
||||||
|
// The state of the listener will be modified, raw data will be cleared upon
|
||||||
|
// successful parsing.
|
||||||
|
func (l *Listener) parseForwardedForSettings() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
if l.XForwardedForAuthorizedAddrsRaw != nil {
|
if l.XForwardedForAuthorizedAddrsRaw != nil {
|
||||||
if l.XForwardedForAuthorizedAddrs, err = parseutil.ParseAddrs(l.XForwardedForAuthorizedAddrsRaw); err != nil {
|
if l.XForwardedForAuthorizedAddrs, err = parseutil.ParseAddrs(l.XForwardedForAuthorizedAddrsRaw); err != nil {
|
||||||
return multierror.Prefix(fmt.Errorf("error parsing x_forwarded_for_authorized_addrs: %w", err), fmt.Sprintf("listeners.%d", i))
|
return fmt.Errorf("error parsing x_forwarded_for_authorized_addrs: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
l.XForwardedForAuthorizedAddrsRaw = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.XForwardedForHopSkipsRaw != nil {
|
if l.XForwardedForHopSkipsRaw != nil {
|
||||||
if l.XForwardedForHopSkips, err = parseutil.ParseInt(l.XForwardedForHopSkipsRaw); err != nil {
|
if l.XForwardedForHopSkips, err = parseutil.ParseInt(l.XForwardedForHopSkipsRaw); err != nil {
|
||||||
return multierror.Prefix(fmt.Errorf("error parsing x_forwarded_for_hop_skips: %w", err), fmt.Sprintf("listeners.%d", i))
|
return fmt.Errorf("error parsing x_forwarded_for_hop_skips: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.XForwardedForHopSkips < 0 {
|
if l.XForwardedForHopSkips < 0 {
|
||||||
return multierror.Prefix(fmt.Errorf("x_forwarded_for_hop_skips cannot be negative but set to %d", l.XForwardedForHopSkips), fmt.Sprintf("listeners.%d", i))
|
return fmt.Errorf("x_forwarded_for_hop_skips cannot be negative but set to %d", l.XForwardedForHopSkips)
|
||||||
}
|
}
|
||||||
|
|
||||||
l.XForwardedForHopSkipsRaw = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.XForwardedForRejectNotAuthorizedRaw != nil {
|
if l.XForwardedForRejectNotAuthorizedRaw != nil {
|
||||||
if l.XForwardedForRejectNotAuthorized, err = parseutil.ParseBool(l.XForwardedForRejectNotAuthorizedRaw); err != nil {
|
if l.XForwardedForRejectNotAuthorized, err = parseutil.ParseBool(l.XForwardedForRejectNotAuthorizedRaw); err != nil {
|
||||||
return multierror.Prefix(fmt.Errorf("invalid value for x_forwarded_for_reject_not_authorized: %w", err), fmt.Sprintf("listeners.%d", i))
|
return fmt.Errorf("invalid value for x_forwarded_for_reject_not_authorized: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
l.XForwardedForRejectNotAuthorizedRaw = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.XForwardedForRejectNotPresentRaw != nil {
|
if l.XForwardedForRejectNotPresentRaw != nil {
|
||||||
if l.XForwardedForRejectNotPresent, err = parseutil.ParseBool(l.XForwardedForRejectNotPresentRaw); err != nil {
|
if l.XForwardedForRejectNotPresent, err = parseutil.ParseBool(l.XForwardedForRejectNotPresentRaw); err != nil {
|
||||||
return multierror.Prefix(fmt.Errorf("invalid value for x_forwarded_for_reject_not_present: %w", err), fmt.Sprintf("listeners.%d", i))
|
return fmt.Errorf("invalid value for x_forwarded_for_reject_not_present: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear raw values after successful parsing.
|
||||||
|
l.XForwardedForAuthorizedAddrsRaw = nil
|
||||||
|
l.XForwardedForHopSkipsRaw = nil
|
||||||
|
l.XForwardedForRejectNotAuthorizedRaw = nil
|
||||||
l.XForwardedForRejectNotPresentRaw = nil
|
l.XForwardedForRejectNotPresentRaw = nil
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Telemetry
|
return nil
|
||||||
{
|
}
|
||||||
|
|
||||||
|
// parseTelemetrySettings attempts to parse the raw listener telemetry settings.
|
||||||
|
// The state of the listener will be modified, raw data will be cleared upon
|
||||||
|
// successful parsing.
|
||||||
|
func (l *Listener) parseTelemetrySettings() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
if l.Telemetry.UnauthenticatedMetricsAccessRaw != nil {
|
if l.Telemetry.UnauthenticatedMetricsAccessRaw != nil {
|
||||||
if l.Telemetry.UnauthenticatedMetricsAccess, err = parseutil.ParseBool(l.Telemetry.UnauthenticatedMetricsAccessRaw); err != nil {
|
if l.Telemetry.UnauthenticatedMetricsAccess, err = parseutil.ParseBool(l.Telemetry.UnauthenticatedMetricsAccessRaw); err != nil {
|
||||||
return multierror.Prefix(fmt.Errorf("invalid value for telemetry.unauthenticated_metrics_access: %w", err), fmt.Sprintf("listeners.%d", i))
|
return fmt.Errorf("invalid value for telemetry.unauthenticated_metrics_access: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Telemetry.UnauthenticatedMetricsAccessRaw = nil
|
l.Telemetry.UnauthenticatedMetricsAccessRaw = nil
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Profiling
|
return nil
|
||||||
{
|
}
|
||||||
|
|
||||||
|
// parseProfilingSettings attempts to parse the raw listener profiling settings.
|
||||||
|
// The state of the listener will be modified, raw data will be cleared upon
|
||||||
|
// successful parsing.
|
||||||
|
func (l *Listener) parseProfilingSettings() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
if l.Profiling.UnauthenticatedPProfAccessRaw != nil {
|
if l.Profiling.UnauthenticatedPProfAccessRaw != nil {
|
||||||
if l.Profiling.UnauthenticatedPProfAccess, err = parseutil.ParseBool(l.Profiling.UnauthenticatedPProfAccessRaw); err != nil {
|
if l.Profiling.UnauthenticatedPProfAccess, err = parseutil.ParseBool(l.Profiling.UnauthenticatedPProfAccessRaw); err != nil {
|
||||||
return multierror.Prefix(fmt.Errorf("invalid value for profiling.unauthenticated_pprof_access: %w", err), fmt.Sprintf("listeners.%d", i))
|
return fmt.Errorf("invalid value for profiling.unauthenticated_pprof_access: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Profiling.UnauthenticatedPProfAccessRaw = nil
|
l.Profiling.UnauthenticatedPProfAccessRaw = nil
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// InFlight Request logging
|
return nil
|
||||||
{
|
}
|
||||||
|
|
||||||
|
// parseProfilingSettings attempts to parse the raw listener in-flight request logging settings.
|
||||||
|
// The state of the listener will be modified, raw data will be cleared upon
|
||||||
|
// successful parsing.
|
||||||
|
func (l *Listener) parseInFlightRequestSettings() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
if l.InFlightRequestLogging.UnauthenticatedInFlightAccessRaw != nil {
|
if l.InFlightRequestLogging.UnauthenticatedInFlightAccessRaw != nil {
|
||||||
if l.InFlightRequestLogging.UnauthenticatedInFlightAccess, err = parseutil.ParseBool(l.InFlightRequestLogging.UnauthenticatedInFlightAccessRaw); err != nil {
|
if l.InFlightRequestLogging.UnauthenticatedInFlightAccess, err = parseutil.ParseBool(l.InFlightRequestLogging.UnauthenticatedInFlightAccessRaw); err != nil {
|
||||||
return multierror.Prefix(fmt.Errorf("invalid value for inflight_requests_logging.unauthenticated_in_flight_requests_access: %w", err), fmt.Sprintf("listeners.%d", i))
|
return fmt.Errorf("invalid value for inflight_requests_logging.unauthenticated_in_flight_requests_access: %w", err)
|
||||||
}
|
|
||||||
|
|
||||||
l.InFlightRequestLogging.UnauthenticatedInFlightAccessRaw = ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CORS
|
l.InFlightRequestLogging.UnauthenticatedInFlightAccessRaw = nil
|
||||||
{
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseCORSSettings attempts to parse the raw listener CORS settings.
|
||||||
|
// The state of the listener will be modified, raw data will be cleared upon
|
||||||
|
// successful parsing.
|
||||||
|
func (l *Listener) parseCORSSettings() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
if l.CorsEnabledRaw != nil {
|
if l.CorsEnabledRaw != nil {
|
||||||
if l.CorsEnabled, err = parseutil.ParseBool(l.CorsEnabledRaw); err != nil {
|
if l.CorsEnabled, err = parseutil.ParseBool(l.CorsEnabledRaw); err != nil {
|
||||||
return multierror.Prefix(fmt.Errorf("invalid value for cors_enabled: %w", err), fmt.Sprintf("listeners.%d", i))
|
return fmt.Errorf("invalid value for cors_enabled: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
l.CorsEnabledRaw = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if strutil.StrListContains(l.CorsAllowedOrigins, "*") && len(l.CorsAllowedOrigins) > 1 {
|
if strutil.StrListContains(l.CorsAllowedOrigins, "*") && len(l.CorsAllowedOrigins) > 1 {
|
||||||
return multierror.Prefix(errors.New("cors_allowed_origins must only contain a wildcard or only non-wildcard values"), fmt.Sprintf("listeners.%d", i))
|
return errors.New("cors_allowed_origins must only contain a wildcard or only non-wildcard values")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(l.CorsAllowedHeadersRaw) > 0 {
|
if len(l.CorsAllowedHeadersRaw) > 0 {
|
||||||
@@ -413,35 +558,9 @@ func ParseListeners(result *SharedConfig, list *ast.ObjectList) error {
|
|||||||
l.CorsAllowedHeaders = append(l.CorsAllowedHeaders, textproto.CanonicalMIMEHeaderKey(header))
|
l.CorsAllowedHeaders = append(l.CorsAllowedHeaders, textproto.CanonicalMIMEHeaderKey(header))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP Headers
|
l.CorsEnabledRaw = nil
|
||||||
{
|
l.CorsAllowedHeadersRaw = nil
|
||||||
// if CustomResponseHeadersRaw is nil, we still need to set the default headers
|
|
||||||
customHeadersMap, err := ParseCustomResponseHeaders(l.CustomResponseHeadersRaw)
|
|
||||||
if err != nil {
|
|
||||||
return multierror.Prefix(fmt.Errorf("failed to parse custom_response_headers: %w", err), fmt.Sprintf("listeners.%d", i))
|
|
||||||
}
|
|
||||||
l.CustomResponseHeaders = customHeadersMap
|
|
||||||
l.CustomResponseHeadersRaw = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Listeners = append(result.Listeners, &l)
|
|
||||||
|
|
||||||
// Chroot Namespace
|
|
||||||
{
|
|
||||||
// If a valid ChrootNamespace value exists, then canonicalize the namespace value
|
|
||||||
if l.ChrootNamespaceRaw != nil {
|
|
||||||
if l.ChrootNamespace, err = parseutil.ParseString(l.ChrootNamespaceRaw); err != nil {
|
|
||||||
return multierror.Prefix(fmt.Errorf("invalid value for chroot_namespace: %w", err), fmt.Sprintf("listeners.%d", i))
|
|
||||||
} else {
|
|
||||||
l.ChrootNamespace = namespace.Canonicalize(l.ChrootNamespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.ChrootNamespaceRaw = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,49 +4,971 @@
|
|||||||
package configutil
|
package configutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"crypto/tls"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseSingleIPTemplate(t *testing.T) {
|
// TestListener_ParseSingleIPTemplate exercises the ParseSingleIPTemplate function to
|
||||||
type args struct {
|
// ensure that we only attempt to parse templates when the input contains a
|
||||||
ipTmpl string
|
// template placeholder (see: go-sockaddr/template).
|
||||||
}
|
func TestListener_ParseSingleIPTemplate(t *testing.T) {
|
||||||
tests := []struct {
|
tests := map[string]struct {
|
||||||
name string
|
|
||||||
arg string
|
arg string
|
||||||
want string
|
want string
|
||||||
wantErr assert.ErrorAssertionFunc
|
isErrorExpected bool
|
||||||
|
errorMessage string
|
||||||
}{
|
}{
|
||||||
{
|
"test https addr": {
|
||||||
name: "test https addr",
|
|
||||||
arg: "https://vaultproject.io:8200",
|
arg: "https://vaultproject.io:8200",
|
||||||
want: "https://vaultproject.io:8200",
|
want: "https://vaultproject.io:8200",
|
||||||
wantErr: assert.NoError,
|
isErrorExpected: false,
|
||||||
},
|
},
|
||||||
{
|
"test invalid template func": {
|
||||||
name: "test invalid template func",
|
arg: "{{ FooBar }}",
|
||||||
arg: "{{FooBar}}",
|
|
||||||
want: "",
|
want: "",
|
||||||
wantErr: assert.Error,
|
isErrorExpected: true,
|
||||||
|
errorMessage: "unable to parse address template",
|
||||||
},
|
},
|
||||||
{
|
"test partial template": {
|
||||||
name: "test partial template",
|
|
||||||
arg: "{{FooBar",
|
arg: "{{FooBar",
|
||||||
want: "{{FooBar",
|
want: "{{FooBar",
|
||||||
wantErr: assert.NoError,
|
isErrorExpected: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for name, tc := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
name := name
|
||||||
got, err := ParseSingleIPTemplate(tt.arg)
|
tc := tc
|
||||||
if !tt.wantErr(t, err, fmt.Sprintf("ParseSingleIPTemplate(%v)", tt.arg)) {
|
t.Run(name, func(t *testing.T) {
|
||||||
return
|
got, err := ParseSingleIPTemplate(tc.arg)
|
||||||
|
|
||||||
|
if tc.isErrorExpected {
|
||||||
|
require.Error(t, err)
|
||||||
|
require.ErrorContains(t, err, tc.errorMessage)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equalf(t, tt.want, got, "ParseSingleIPTemplate(%v)", tt.arg)
|
require.Equal(t, tc.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestListener_parseType exercises the listener receiver parseType.
|
||||||
|
// We check various inputs to ensure we can parse the values as expected and
|
||||||
|
// assign the relevant value on the SharedConfig struct.
|
||||||
|
func TestListener_parseType(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := map[string]struct {
|
||||||
|
inputType string
|
||||||
|
inputFallback string
|
||||||
|
expectedValue string
|
||||||
|
isErrorExpected bool
|
||||||
|
errorMessage string
|
||||||
|
}{
|
||||||
|
"empty-all": {
|
||||||
|
inputType: "",
|
||||||
|
inputFallback: "",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "listener type must be specified",
|
||||||
|
},
|
||||||
|
"bad-type": {
|
||||||
|
inputType: "foo",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "unsupported listener type",
|
||||||
|
},
|
||||||
|
"bad-fallback": {
|
||||||
|
inputType: "",
|
||||||
|
inputFallback: "foo",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "unsupported listener type",
|
||||||
|
},
|
||||||
|
"tcp-type-lower": {
|
||||||
|
inputType: "tcp",
|
||||||
|
expectedValue: "tcp",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"tcp-type-upper": {
|
||||||
|
inputType: "TCP",
|
||||||
|
expectedValue: "tcp",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"tcp-type-mixed": {
|
||||||
|
inputType: "tCp",
|
||||||
|
expectedValue: "tcp",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"tcp-fallback-lower": {
|
||||||
|
inputType: "",
|
||||||
|
inputFallback: "tcp",
|
||||||
|
expectedValue: "tcp",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"tcp-fallback-upper": {
|
||||||
|
inputType: "",
|
||||||
|
inputFallback: "TCP",
|
||||||
|
expectedValue: "tcp",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"tcp-fallback-mixed": {
|
||||||
|
inputType: "",
|
||||||
|
inputFallback: "tCp",
|
||||||
|
expectedValue: "tcp",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"unix-type-lower": {
|
||||||
|
inputType: "unix",
|
||||||
|
expectedValue: "unix",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"unix-type-upper": {
|
||||||
|
inputType: "UNIX",
|
||||||
|
expectedValue: "unix",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"unix-type-mixed": {
|
||||||
|
inputType: "uNiX",
|
||||||
|
expectedValue: "unix",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"unix-fallback-lower": {
|
||||||
|
inputType: "",
|
||||||
|
inputFallback: "unix",
|
||||||
|
expectedValue: "unix",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"unix-fallback-upper": {
|
||||||
|
inputType: "",
|
||||||
|
inputFallback: "UNIX",
|
||||||
|
expectedValue: "unix",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"unix-fallback-mixed": {
|
||||||
|
inputType: "",
|
||||||
|
inputFallback: "uNiX",
|
||||||
|
expectedValue: "unix",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tests {
|
||||||
|
name := name
|
||||||
|
tc := tc
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
l := &Listener{Type: tc.inputType}
|
||||||
|
err := l.parseType(tc.inputFallback)
|
||||||
|
switch {
|
||||||
|
case tc.isErrorExpected:
|
||||||
|
require.Error(t, err)
|
||||||
|
require.ErrorContains(t, err, tc.errorMessage)
|
||||||
|
default:
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.expectedValue, l.Type)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestListener_parseRequestSettings exercises the listener receiver parseRequestSettings.
|
||||||
|
// We check various inputs to ensure we can parse the values as expected and
|
||||||
|
// assign the relevant value on the SharedConfig struct.
|
||||||
|
func TestListener_parseRequestSettings(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := map[string]struct {
|
||||||
|
rawMaxRequestSize any
|
||||||
|
expectedMaxRequestSize int64
|
||||||
|
rawMaxRequestDuration any
|
||||||
|
expectedDuration time.Duration
|
||||||
|
rawRequireRequestHeader any
|
||||||
|
expectedRequireRequestHeader bool
|
||||||
|
isErrorExpected bool
|
||||||
|
errorMessage string
|
||||||
|
}{
|
||||||
|
"nil": {
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"max-request-size-bad": {
|
||||||
|
rawMaxRequestSize: "juan",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "error parsing max_request_size",
|
||||||
|
},
|
||||||
|
"max-request-size-good": {
|
||||||
|
rawMaxRequestSize: "5",
|
||||||
|
expectedMaxRequestSize: 5,
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"max-request-duration-bad": {
|
||||||
|
rawMaxRequestDuration: "juan",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "error parsing max_request_duration",
|
||||||
|
},
|
||||||
|
"max-request-duration-good": {
|
||||||
|
rawMaxRequestDuration: "30s",
|
||||||
|
expectedDuration: 30 * time.Second,
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"require-request-header-bad": {
|
||||||
|
rawRequireRequestHeader: "juan",
|
||||||
|
expectedRequireRequestHeader: false,
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "invalid value for require_request_header",
|
||||||
|
},
|
||||||
|
"require-request-header-good": {
|
||||||
|
rawRequireRequestHeader: "true",
|
||||||
|
expectedRequireRequestHeader: true,
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tests {
|
||||||
|
name := name
|
||||||
|
tc := tc
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Configure listener with raw values
|
||||||
|
l := &Listener{
|
||||||
|
MaxRequestSizeRaw: tc.rawMaxRequestSize,
|
||||||
|
MaxRequestDurationRaw: tc.rawMaxRequestDuration,
|
||||||
|
RequireRequestHeaderRaw: tc.rawRequireRequestHeader,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.parseRequestSettings()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case tc.isErrorExpected:
|
||||||
|
require.Error(t, err)
|
||||||
|
require.ErrorContains(t, err, tc.errorMessage)
|
||||||
|
default:
|
||||||
|
// Assert we got the relevant values.
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.expectedMaxRequestSize, l.MaxRequestSize)
|
||||||
|
require.Equal(t, tc.expectedDuration, l.MaxRequestDuration)
|
||||||
|
require.Equal(t, tc.expectedRequireRequestHeader, l.RequireRequestHeader)
|
||||||
|
|
||||||
|
// Ensure the state was modified for the raw values.
|
||||||
|
require.Nil(t, l.MaxRequestSizeRaw)
|
||||||
|
require.Nil(t, l.MaxRequestDurationRaw)
|
||||||
|
require.Nil(t, l.RequireRequestHeaderRaw)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestListener_parseTLSSettings exercises the listener receiver parseTLSSettings.
|
||||||
|
// We check various inputs to ensure we can parse the values as expected and
|
||||||
|
// assign the relevant value on the SharedConfig struct.
|
||||||
|
func TestListener_parseTLSSettings(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := map[string]struct {
|
||||||
|
rawTLSDisable any
|
||||||
|
expectedTLSDisable bool
|
||||||
|
rawTLSCipherSuites string
|
||||||
|
expectedTLSCipherSuites []uint16
|
||||||
|
rawTLSRequireAndVerifyClientCert any
|
||||||
|
expectedTLSRequireAndVerifyClientCert bool
|
||||||
|
rawTLSDisableClientCerts any
|
||||||
|
expectedTLSDisableClientCerts bool
|
||||||
|
isErrorExpected bool
|
||||||
|
errorMessage string
|
||||||
|
}{
|
||||||
|
"nil": {
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"tls-disable-bad": {
|
||||||
|
rawTLSDisable: "juan",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "invalid value for tls_disable",
|
||||||
|
},
|
||||||
|
"tls-disable-good": {
|
||||||
|
rawTLSDisable: "true",
|
||||||
|
expectedTLSDisable: true,
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"tls-cipher-suites-bad": {
|
||||||
|
rawTLSCipherSuites: "juan",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "invalid value for tls_cipher_suites",
|
||||||
|
},
|
||||||
|
"tls-cipher-suites-good": {
|
||||||
|
rawTLSCipherSuites: "TLS_RSA_WITH_RC4_128_SHA",
|
||||||
|
expectedTLSCipherSuites: []uint16{tls.TLS_RSA_WITH_RC4_128_SHA},
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"tls-require-and-verify-client-cert-bad": {
|
||||||
|
rawTLSRequireAndVerifyClientCert: "juan",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "invalid value for tls_require_and_verify_client_cert",
|
||||||
|
},
|
||||||
|
"tls-require-and-verify-client-cert-good": {
|
||||||
|
rawTLSRequireAndVerifyClientCert: "true",
|
||||||
|
expectedTLSRequireAndVerifyClientCert: true,
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"tls-disable-client-certs-bad": {
|
||||||
|
rawTLSDisableClientCerts: "juan",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "invalid value for tls_disable_client_certs",
|
||||||
|
},
|
||||||
|
"tls-disable-client-certs-good": {
|
||||||
|
rawTLSDisableClientCerts: "true",
|
||||||
|
expectedTLSDisableClientCerts: true,
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tests {
|
||||||
|
name := name
|
||||||
|
tc := tc
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Configure listener with raw values
|
||||||
|
l := &Listener{
|
||||||
|
TLSDisableRaw: tc.rawTLSDisable,
|
||||||
|
TLSCipherSuitesRaw: tc.rawTLSCipherSuites,
|
||||||
|
TLSRequireAndVerifyClientCertRaw: tc.rawTLSRequireAndVerifyClientCert,
|
||||||
|
TLSDisableClientCertsRaw: tc.rawTLSDisableClientCerts,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.parseTLSSettings()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case tc.isErrorExpected:
|
||||||
|
require.Error(t, err)
|
||||||
|
require.ErrorContains(t, err, tc.errorMessage)
|
||||||
|
default:
|
||||||
|
// Assert we got the relevant values.
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.expectedTLSDisable, l.TLSDisable)
|
||||||
|
require.Equal(t, tc.expectedTLSCipherSuites, l.TLSCipherSuites)
|
||||||
|
require.Equal(t, tc.expectedTLSRequireAndVerifyClientCert, l.TLSRequireAndVerifyClientCert)
|
||||||
|
require.Equal(t, tc.expectedTLSDisableClientCerts, l.TLSDisableClientCerts)
|
||||||
|
|
||||||
|
// Ensure the state was modified for the raw values.
|
||||||
|
require.Nil(t, l.TLSDisableRaw)
|
||||||
|
require.Empty(t, l.TLSCipherSuitesRaw)
|
||||||
|
require.Nil(t, l.TLSRequireAndVerifyClientCertRaw)
|
||||||
|
require.Nil(t, l.TLSDisableClientCertsRaw)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestListener_parseHTTPTimeoutSettings exercises the listener receiver parseHTTPTimeoutSettings.
|
||||||
|
// We check various inputs to ensure we can parse the values as expected and
|
||||||
|
// assign the relevant value on the SharedConfig struct.
|
||||||
|
func TestListener_parseHTTPTimeoutSettings(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
rawHTTPReadTimeout any
|
||||||
|
expectedHTTPReadTimeout time.Duration
|
||||||
|
rawHTTPReadHeaderTimeout any
|
||||||
|
expectedHTTPReadHeaderTimeout time.Duration
|
||||||
|
rawHTTPWriteTimeout any
|
||||||
|
expectedHTTPWriteTimeout time.Duration
|
||||||
|
rawHTTPIdleTimeout any
|
||||||
|
expectedHTTPIdleTimeout time.Duration
|
||||||
|
isErrorExpected bool
|
||||||
|
errorMessage string
|
||||||
|
}{
|
||||||
|
"nil": {
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"read-timeout-bad": {
|
||||||
|
rawHTTPReadTimeout: "juan",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "error parsing http_read_timeout",
|
||||||
|
},
|
||||||
|
"read-timeout-good": {
|
||||||
|
rawHTTPReadTimeout: "30s",
|
||||||
|
expectedHTTPReadTimeout: 30 * time.Second,
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"read-header-timeout-bad": {
|
||||||
|
rawHTTPReadHeaderTimeout: "juan",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "error parsing http_read_header_timeout",
|
||||||
|
},
|
||||||
|
"read-header-timeout-good": {
|
||||||
|
rawHTTPReadHeaderTimeout: "30s",
|
||||||
|
expectedHTTPReadHeaderTimeout: 30 * time.Second,
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"write-timeout-bad": {
|
||||||
|
rawHTTPWriteTimeout: "juan",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "error parsing http_write_timeout",
|
||||||
|
},
|
||||||
|
"write-timeout-good": {
|
||||||
|
rawHTTPWriteTimeout: "30s",
|
||||||
|
expectedHTTPWriteTimeout: 30 * time.Second,
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"idle-timeout-bad": {
|
||||||
|
rawHTTPIdleTimeout: "juan",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "error parsing http_idle_timeout",
|
||||||
|
},
|
||||||
|
"idle-timeout-good": {
|
||||||
|
rawHTTPIdleTimeout: "30s",
|
||||||
|
expectedHTTPIdleTimeout: 30 * time.Second,
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tests {
|
||||||
|
name := name
|
||||||
|
tc := tc
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Configure listener with raw values
|
||||||
|
l := &Listener{
|
||||||
|
HTTPReadTimeoutRaw: tc.rawHTTPReadTimeout,
|
||||||
|
HTTPReadHeaderTimeoutRaw: tc.rawHTTPReadHeaderTimeout,
|
||||||
|
HTTPWriteTimeoutRaw: tc.rawHTTPWriteTimeout,
|
||||||
|
HTTPIdleTimeoutRaw: tc.rawHTTPIdleTimeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.parseHTTPTimeoutSettings()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case tc.isErrorExpected:
|
||||||
|
require.Error(t, err)
|
||||||
|
require.ErrorContains(t, err, tc.errorMessage)
|
||||||
|
default:
|
||||||
|
// Assert we got the relevant values.
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.expectedHTTPReadTimeout, l.HTTPReadTimeout)
|
||||||
|
require.Equal(t, tc.expectedHTTPReadHeaderTimeout, l.HTTPReadHeaderTimeout)
|
||||||
|
require.Equal(t, tc.expectedHTTPWriteTimeout, l.HTTPWriteTimeout)
|
||||||
|
require.Equal(t, tc.expectedHTTPIdleTimeout, l.HTTPIdleTimeout)
|
||||||
|
|
||||||
|
// Ensure the state was modified for the raw values.
|
||||||
|
require.Nil(t, l.HTTPReadTimeoutRaw)
|
||||||
|
require.Nil(t, l.HTTPReadHeaderTimeoutRaw)
|
||||||
|
require.Nil(t, l.HTTPWriteTimeoutRaw)
|
||||||
|
require.Nil(t, l.HTTPIdleTimeoutRaw)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestListener_parseProxySettings exercises the listener receiver parseProxySettings.
|
||||||
|
// We check various inputs to ensure we can parse the values as expected and
|
||||||
|
// assign the relevant value on the SharedConfig struct.
|
||||||
|
func TestListener_parseProxySettings(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
rawProxyProtocolAuthorizedAddrs any
|
||||||
|
expectedNumAddrs int
|
||||||
|
proxyBehavior string
|
||||||
|
isErrorExpected bool
|
||||||
|
errorMessage string
|
||||||
|
}{
|
||||||
|
"nil": {
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"bad-addrs": {
|
||||||
|
rawProxyProtocolAuthorizedAddrs: "juan",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "error parsing proxy_protocol_authorized_addrs",
|
||||||
|
},
|
||||||
|
"good-addrs": {
|
||||||
|
rawProxyProtocolAuthorizedAddrs: "10.0.0.1,10.0.2.1",
|
||||||
|
expectedNumAddrs: 2,
|
||||||
|
proxyBehavior: "",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"behavior-bad": {
|
||||||
|
rawProxyProtocolAuthorizedAddrs: "10.0.0.1,10.0.2.1",
|
||||||
|
proxyBehavior: "juan",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "unsupported value supplied for proxy_protocol_behavior",
|
||||||
|
},
|
||||||
|
"behavior-use-always": {
|
||||||
|
rawProxyProtocolAuthorizedAddrs: "10.0.0.1,10.0.2.1",
|
||||||
|
expectedNumAddrs: 2,
|
||||||
|
proxyBehavior: "use_always",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"behavior-empty": {
|
||||||
|
rawProxyProtocolAuthorizedAddrs: "10.0.0.1,10.0.2.1",
|
||||||
|
expectedNumAddrs: 2,
|
||||||
|
proxyBehavior: "",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"behavior-allow": {
|
||||||
|
rawProxyProtocolAuthorizedAddrs: "10.0.0.1,10.0.2.1",
|
||||||
|
expectedNumAddrs: 2,
|
||||||
|
proxyBehavior: "allow_authorized",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"behavior-deny": {
|
||||||
|
rawProxyProtocolAuthorizedAddrs: "10.0.0.1,10.0.2.1",
|
||||||
|
expectedNumAddrs: 2,
|
||||||
|
proxyBehavior: "deny_authorized",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tests {
|
||||||
|
name := name
|
||||||
|
tc := tc
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Configure listener with raw values
|
||||||
|
l := &Listener{
|
||||||
|
ProxyProtocolAuthorizedAddrsRaw: tc.rawProxyProtocolAuthorizedAddrs,
|
||||||
|
ProxyProtocolBehavior: tc.proxyBehavior,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.parseProxySettings()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case tc.isErrorExpected:
|
||||||
|
require.Error(t, err)
|
||||||
|
require.ErrorContains(t, err, tc.errorMessage)
|
||||||
|
default:
|
||||||
|
// Assert we got the relevant values.
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, l.ProxyProtocolAuthorizedAddrs, tc.expectedNumAddrs)
|
||||||
|
|
||||||
|
// Ensure the state was modified for the raw values.
|
||||||
|
require.Nil(t, l.ProxyProtocolAuthorizedAddrsRaw)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestListener_parseForwardedForSettings exercises the listener receiver parseForwardedForSettings.
|
||||||
|
// We check various inputs to ensure we can parse the values as expected and
|
||||||
|
// assign the relevant value on the SharedConfig struct.
|
||||||
|
func TestListener_parseForwardedForSettings(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
rawAuthorizedAddrs any
|
||||||
|
expectedNumAddrs int
|
||||||
|
rawHopSkips any
|
||||||
|
expectedHopSkips int64
|
||||||
|
rawRejectNotAuthorized any
|
||||||
|
expectedRejectNotAuthorized bool
|
||||||
|
rawRejectNotPresent any
|
||||||
|
expectedRejectNotPresent bool
|
||||||
|
isErrorExpected bool
|
||||||
|
errorMessage string
|
||||||
|
}{
|
||||||
|
"nil": {
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"authorized-addrs-bad": {
|
||||||
|
rawAuthorizedAddrs: "juan",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "error parsing x_forwarded_for_authorized_addrs",
|
||||||
|
},
|
||||||
|
"authorized-addrs-good": {
|
||||||
|
rawAuthorizedAddrs: "10.0.0.1,10.0.2.1",
|
||||||
|
expectedNumAddrs: 2,
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tests {
|
||||||
|
name := name
|
||||||
|
tc := tc
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Configure listener with raw values
|
||||||
|
l := &Listener{
|
||||||
|
XForwardedForAuthorizedAddrsRaw: tc.rawAuthorizedAddrs,
|
||||||
|
XForwardedForHopSkipsRaw: tc.rawHopSkips,
|
||||||
|
XForwardedForRejectNotAuthorizedRaw: tc.rawRejectNotAuthorized,
|
||||||
|
XForwardedForRejectNotPresentRaw: tc.rawRejectNotPresent,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.parseForwardedForSettings()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case tc.isErrorExpected:
|
||||||
|
require.Error(t, err)
|
||||||
|
require.ErrorContains(t, err, tc.errorMessage)
|
||||||
|
default:
|
||||||
|
// Assert we got the relevant values.
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Len(t, l.XForwardedForAuthorizedAddrs, tc.expectedNumAddrs)
|
||||||
|
require.Equal(t, tc.expectedHopSkips, l.XForwardedForHopSkips)
|
||||||
|
require.Equal(t, tc.expectedRejectNotAuthorized, l.XForwardedForRejectNotAuthorized)
|
||||||
|
require.Equal(t, tc.expectedRejectNotPresent, l.XForwardedForRejectNotPresent)
|
||||||
|
|
||||||
|
// Ensure the state was modified for the raw values.
|
||||||
|
require.Nil(t, l.XForwardedForAuthorizedAddrsRaw)
|
||||||
|
require.Nil(t, l.XForwardedForHopSkipsRaw)
|
||||||
|
require.Nil(t, l.XForwardedForRejectNotAuthorizedRaw)
|
||||||
|
require.Nil(t, l.XForwardedForRejectNotPresentRaw)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestListener_parseTelemetrySettings exercises the listener receiver parseTelemetrySettings.
|
||||||
|
// We check various inputs to ensure we can parse the values as expected and
|
||||||
|
// assign the relevant value on the SharedConfig struct.
|
||||||
|
func TestListener_parseTelemetrySettings(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
rawUnauthenticatedMetricsAccess any
|
||||||
|
expectedUnauthenticatedMetricsAccess bool
|
||||||
|
isErrorExpected bool
|
||||||
|
errorMessage string
|
||||||
|
}{
|
||||||
|
"nil": {
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"unauth-bad": {
|
||||||
|
rawUnauthenticatedMetricsAccess: "juan",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "invalid value for telemetry.unauthenticated_metrics_access",
|
||||||
|
},
|
||||||
|
"unauth-good": {
|
||||||
|
rawUnauthenticatedMetricsAccess: "true",
|
||||||
|
expectedUnauthenticatedMetricsAccess: true,
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tests {
|
||||||
|
name := name
|
||||||
|
tc := tc
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Configure listener with raw values
|
||||||
|
l := &Listener{
|
||||||
|
Telemetry: ListenerTelemetry{
|
||||||
|
UnauthenticatedMetricsAccessRaw: tc.rawUnauthenticatedMetricsAccess,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.parseTelemetrySettings()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case tc.isErrorExpected:
|
||||||
|
require.Error(t, err)
|
||||||
|
require.ErrorContains(t, err, tc.errorMessage)
|
||||||
|
default:
|
||||||
|
// Assert we got the relevant values.
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.expectedUnauthenticatedMetricsAccess, l.Telemetry.UnauthenticatedMetricsAccess)
|
||||||
|
|
||||||
|
// Ensure the state was modified for the raw values.
|
||||||
|
require.Nil(t, l.Telemetry.UnauthenticatedMetricsAccessRaw)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestListener_parseProfilingSettings exercises the listener receiver parseProfilingSettings.
|
||||||
|
// We check various inputs to ensure we can parse the values as expected and
|
||||||
|
// assign the relevant value on the SharedConfig struct.
|
||||||
|
func TestListener_parseProfilingSettings(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
rawUnauthenticatedPProfAccess any
|
||||||
|
expectedUnauthenticatedPProfAccess bool
|
||||||
|
isErrorExpected bool
|
||||||
|
errorMessage string
|
||||||
|
}{
|
||||||
|
"nil": {
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"bad": {
|
||||||
|
rawUnauthenticatedPProfAccess: "juan",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "invalid value for profiling.unauthenticated_pprof_access",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tests {
|
||||||
|
name := name
|
||||||
|
tc := tc
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Configure listener with raw values
|
||||||
|
l := &Listener{
|
||||||
|
Profiling: ListenerProfiling{
|
||||||
|
UnauthenticatedPProfAccessRaw: tc.rawUnauthenticatedPProfAccess,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.parseProfilingSettings()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case tc.isErrorExpected:
|
||||||
|
require.Error(t, err)
|
||||||
|
require.ErrorContains(t, err, tc.errorMessage)
|
||||||
|
default:
|
||||||
|
// Assert we got the relevant values.
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.expectedUnauthenticatedPProfAccess, l.Profiling.UnauthenticatedPProfAccess)
|
||||||
|
|
||||||
|
// Ensure the state was modified for the raw values.
|
||||||
|
require.Nil(t, l.Profiling.UnauthenticatedPProfAccessRaw)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestListener_parseInFlightRequestSettings exercises the listener receiver parseInFlightRequestSettings.
|
||||||
|
// We check various inputs to ensure we can parse the values as expected and
|
||||||
|
// assign the relevant value on the SharedConfig struct.
|
||||||
|
func TestListener_parseInFlightRequestSettings(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
rawUnauthenticatedInFlightAccess any
|
||||||
|
expectedUnauthenticatedInFlightAccess bool
|
||||||
|
isErrorExpected bool
|
||||||
|
errorMessage string
|
||||||
|
}{
|
||||||
|
"nil": {
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"bad": {
|
||||||
|
rawUnauthenticatedInFlightAccess: "juan",
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "invalid value for inflight_requests_logging.unauthenticated_in_flight_requests_access",
|
||||||
|
},
|
||||||
|
"good": {
|
||||||
|
rawUnauthenticatedInFlightAccess: "true",
|
||||||
|
expectedUnauthenticatedInFlightAccess: true,
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tests {
|
||||||
|
name := name
|
||||||
|
tc := tc
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Configure listener with raw values
|
||||||
|
l := &Listener{
|
||||||
|
InFlightRequestLogging: ListenerInFlightRequestLogging{
|
||||||
|
UnauthenticatedInFlightAccessRaw: tc.rawUnauthenticatedInFlightAccess,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.parseInFlightRequestSettings()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case tc.isErrorExpected:
|
||||||
|
require.Error(t, err)
|
||||||
|
require.ErrorContains(t, err, tc.errorMessage)
|
||||||
|
default:
|
||||||
|
// Assert we got the relevant values.
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.expectedUnauthenticatedInFlightAccess, l.InFlightRequestLogging.UnauthenticatedInFlightAccess)
|
||||||
|
|
||||||
|
// Ensure the state was modified for the raw values.
|
||||||
|
require.Nil(t, l.InFlightRequestLogging.UnauthenticatedInFlightAccessRaw)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestListener_parseCORSSettings exercises the listener receiver parseCORSSettings.
|
||||||
|
// We check various inputs to ensure we can parse the values as expected and
|
||||||
|
// assign the relevant value on the SharedConfig struct.
|
||||||
|
func TestListener_parseCORSSettings(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
rawCorsEnabled any
|
||||||
|
rawCorsAllowedHeaders []string
|
||||||
|
corsAllowedOrigins []string
|
||||||
|
expectedCorsEnabled bool
|
||||||
|
expectedNumCorsAllowedHeaders int
|
||||||
|
isErrorExpected bool
|
||||||
|
errorMessage string
|
||||||
|
}{
|
||||||
|
"nil": {
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"cors-enabled-bad": {
|
||||||
|
rawCorsEnabled: "juan",
|
||||||
|
expectedCorsEnabled: false,
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "invalid value for cors_enabled",
|
||||||
|
},
|
||||||
|
"cors-enabled-good": {
|
||||||
|
rawCorsEnabled: "true",
|
||||||
|
expectedCorsEnabled: true,
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"cors-allowed-origins-single-wildcard": {
|
||||||
|
corsAllowedOrigins: []string{"*"},
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"cors-allowed-origins-multi-wildcard": {
|
||||||
|
corsAllowedOrigins: []string{"*", "hashicorp.com"},
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "cors_allowed_origins must only contain a wildcard or only non-wildcard values",
|
||||||
|
},
|
||||||
|
"cors-allowed-headers-anything": {
|
||||||
|
rawCorsAllowedHeaders: []string{"foo", "bar"},
|
||||||
|
expectedNumCorsAllowedHeaders: 2,
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tests {
|
||||||
|
name := name
|
||||||
|
tc := tc
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Configure listener with raw values
|
||||||
|
l := &Listener{
|
||||||
|
CorsEnabledRaw: tc.rawCorsEnabled,
|
||||||
|
CorsAllowedHeadersRaw: tc.rawCorsAllowedHeaders,
|
||||||
|
CorsAllowedOrigins: tc.corsAllowedOrigins,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.parseCORSSettings()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case tc.isErrorExpected:
|
||||||
|
require.Error(t, err)
|
||||||
|
require.ErrorContains(t, err, tc.errorMessage)
|
||||||
|
default:
|
||||||
|
// Assert we got the relevant values.
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.expectedCorsEnabled, l.CorsEnabled)
|
||||||
|
require.Len(t, l.CorsAllowedHeaders, tc.expectedNumCorsAllowedHeaders)
|
||||||
|
|
||||||
|
// Ensure the state was modified for the raw values.
|
||||||
|
require.Nil(t, l.CorsEnabledRaw)
|
||||||
|
require.Nil(t, l.CorsAllowedHeadersRaw)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestListener_parseHTTPHeaderSettings exercises the listener receiver parseHTTPHeaderSettings.
|
||||||
|
// We check various inputs to ensure we can parse the values as expected and
|
||||||
|
// assign the relevant value on the SharedConfig struct.
|
||||||
|
func TestListener_parseHTTPHeaderSettings(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
rawCustomResponseHeaders []map[string]any
|
||||||
|
expectedNumCustomResponseHeaders int
|
||||||
|
isErrorExpected bool
|
||||||
|
errorMessage string
|
||||||
|
}{
|
||||||
|
"nil": {
|
||||||
|
isErrorExpected: false,
|
||||||
|
expectedNumCustomResponseHeaders: 1, // default: Strict-Transport-Security
|
||||||
|
},
|
||||||
|
"custom-headers-bad": {
|
||||||
|
rawCustomResponseHeaders: []map[string]any{
|
||||||
|
{"juan": false},
|
||||||
|
},
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "failed to parse custom_response_headers",
|
||||||
|
},
|
||||||
|
"custom-headers-good": {
|
||||||
|
rawCustomResponseHeaders: []map[string]any{
|
||||||
|
{
|
||||||
|
"2xx": []map[string]any{
|
||||||
|
{"X-Custom-Header": []any{"Custom Header Value 1", "Custom Header Value 2"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedNumCustomResponseHeaders: 2,
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tests {
|
||||||
|
name := name
|
||||||
|
tc := tc
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Configure listener with raw values
|
||||||
|
l := &Listener{
|
||||||
|
CustomResponseHeadersRaw: tc.rawCustomResponseHeaders,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.parseHTTPHeaderSettings()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case tc.isErrorExpected:
|
||||||
|
require.Error(t, err)
|
||||||
|
require.ErrorContains(t, err, tc.errorMessage)
|
||||||
|
default:
|
||||||
|
// Assert we got the relevant values.
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, l.CustomResponseHeaders, tc.expectedNumCustomResponseHeaders)
|
||||||
|
|
||||||
|
// Ensure the state was modified for the raw values.
|
||||||
|
require.Nil(t, l.CustomResponseHeadersRaw)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestListener_parseChrootNamespaceSettings exercises the listener receiver parseChrootNamespaceSettings.
|
||||||
|
// We check various inputs to ensure we can parse the values as expected and
|
||||||
|
// assign the relevant value on the SharedConfig struct.
|
||||||
|
func TestListener_parseChrootNamespaceSettings(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
rawChrootNamespace any
|
||||||
|
expectedChrootNamespace string
|
||||||
|
isErrorExpected bool
|
||||||
|
errorMessage string
|
||||||
|
}{
|
||||||
|
"nil": {
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
"bad": {
|
||||||
|
rawChrootNamespace: &Listener{}, // Unsure how we'd ever see this really.
|
||||||
|
isErrorExpected: true,
|
||||||
|
errorMessage: "invalid value for chroot_namespace",
|
||||||
|
},
|
||||||
|
"good": {
|
||||||
|
rawChrootNamespace: "juan",
|
||||||
|
expectedChrootNamespace: "juan/",
|
||||||
|
isErrorExpected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tests {
|
||||||
|
name := name
|
||||||
|
tc := tc
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Configure listener with raw values
|
||||||
|
l := &Listener{
|
||||||
|
ChrootNamespaceRaw: tc.rawChrootNamespace,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := l.parseChrootNamespaceSettings()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case tc.isErrorExpected:
|
||||||
|
require.Error(t, err)
|
||||||
|
require.ErrorContains(t, err, tc.errorMessage)
|
||||||
|
default:
|
||||||
|
// Assert we got the relevant values.
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.expectedChrootNamespace, l.ChrootNamespace)
|
||||||
|
|
||||||
|
// Ensure the state was modified for the raw values.
|
||||||
|
require.Nil(t, l.ChrootNamespaceRaw)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user