mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-30 18:17:55 +00:00 
			
		
		
		
	Add Ability to Disable Replication Status Endpoints in Listener Configuration (#23547)
* CI: Pre-emptively delete logs dir after cache restore in test-collect-reports (#23600) * Fix OktaNumberChallenge (#23565) * remove arg * changelog * exclude changelog in verifying doc/ui PRs (#23601) * Audit: eventlogger sink node reopen on SIGHUP (#23598) * ensure nodes are asked to reload audit files on SIGHUP * added changelog * Capture errors emitted from all nodes during proccessing of audit pipelines (#23582) * Update security-scan.yml * 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 * fix panic when unlocking unlocked user (#23611) * VAULT-18307: update rotation period for aws static roles on update (#23528) * add disable_replication_status_endpoints tcp listener config parameter * add wrapping handler for disabled replication status endpoints setting * adapt disable_replication_status_endpoints configuration parsing code to refactored parsing code * refactor configuration parsing code to facilitate testing * fix a panic when parsing configuration * update refactored configuration parsing code * fix merge corruption * add changelog file * document new TCP listener configuration parameter * make sure disable_replication_status_endpoints only has effect on TCP listeners * use active voice for explanation of disable_replication_status_endpoints * fix minor merge issue --------- Co-authored-by: Kuba Wieczorek <kuba.wieczorek@hashicorp.com> Co-authored-by: Angel Garbarino <Monkeychip@users.noreply.github.com> Co-authored-by: Hamid Ghaf <83242695+hghaf099@users.noreply.github.com> Co-authored-by: Peter Wilson <peter.wilson@hashicorp.com> Co-authored-by: Mark Collao <106274486+mcollao-hc@users.noreply.github.com> Co-authored-by: davidadeleon <56207066+davidadeleon@users.noreply.github.com> Co-authored-by: kpcraig <3031348+kpcraig@users.noreply.github.com>
This commit is contained in:
		
							
								
								
									
										3
									
								
								changelog/23547.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								changelog/23547.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| ```release-note:feature | ||||
| config/listener: allow per-listener configuration setting to disable replication status endpoints. | ||||
| ``` | ||||
| @@ -237,20 +237,27 @@ func handler(props *vault.HandlerProperties) http.Handler { | ||||
| 		additionalRoutes(mux, core) | ||||
| 	} | ||||
|  | ||||
| 	// Wrap the handler in another handler to trigger all help paths. | ||||
| 	helpWrappedHandler := wrapHelpHandler(mux, core) | ||||
| 	corsWrappedHandler := wrapCORSHandler(helpWrappedHandler, core) | ||||
| 	quotaWrappedHandler := rateLimitQuotaWrapping(corsWrappedHandler, core) | ||||
| 	genericWrappedHandler := genericWrapping(core, quotaWrappedHandler, props) | ||||
| 	// Build up a chain of wrapping handlers. | ||||
| 	wrappedHandler := wrapHelpHandler(mux, core) | ||||
| 	wrappedHandler = wrapCORSHandler(wrappedHandler, core) | ||||
| 	wrappedHandler = rateLimitQuotaWrapping(wrappedHandler, core) | ||||
| 	wrappedHandler = genericWrapping(core, wrappedHandler, props) | ||||
|  | ||||
| 	// Wrap the handler with PrintablePathCheckHandler to check for non-printable | ||||
| 	// characters in the request path. | ||||
| 	printablePathCheckHandler := genericWrappedHandler | ||||
| 	// Add an extra wrapping handler if the DisablePrintableCheck listener | ||||
| 	// setting isn't true that checks for non-printable characters in the | ||||
| 	// request path. | ||||
| 	if !props.DisablePrintableCheck { | ||||
| 		printablePathCheckHandler = cleanhttp.PrintablePathCheckHandler(genericWrappedHandler, nil) | ||||
| 		wrappedHandler = cleanhttp.PrintablePathCheckHandler(wrappedHandler, nil) | ||||
| 	} | ||||
|  | ||||
| 	return printablePathCheckHandler | ||||
| 	// Add an extra wrapping handler if the DisableReplicationStatusEndpoints | ||||
| 	// setting is true that will create a new request with a context that has | ||||
| 	// a value indicating that the replication status endpoints are disabled. | ||||
| 	if props.ListenerConfig != nil && props.ListenerConfig.DisableReplicationStatusEndpoints { | ||||
| 		wrappedHandler = disableReplicationStatusEndpointWrapping(wrappedHandler) | ||||
| 	} | ||||
|  | ||||
| 	return wrappedHandler | ||||
| } | ||||
|  | ||||
| type copyResponseWriter struct { | ||||
|   | ||||
| @@ -133,6 +133,14 @@ func rateLimitQuotaWrapping(handler http.Handler, core *vault.Core) http.Handler | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func disableReplicationStatusEndpointWrapping(h http.Handler) http.Handler { | ||||
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		request := r.WithContext(context.WithValue(r.Context(), "disable_replication_status_endpoints", true)) | ||||
|  | ||||
| 		h.ServeHTTP(w, request) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func parseRemoteIPAddress(r *http.Request) string { | ||||
| 	ip, _, err := net.SplitHostPort(r.RemoteAddr) | ||||
| 	if err != nil { | ||||
|   | ||||
| @@ -139,6 +139,10 @@ type Listener struct { | ||||
| 	RedactClusterName    bool `hcl:"-"` | ||||
| 	RedactVersionRaw     any  `hcl:"redact_version"` | ||||
| 	RedactVersion        bool `hcl:"-"` | ||||
|  | ||||
| 	// DisableReplicationStatusEndpoint disables the unauthenticated replication status endpoints | ||||
| 	DisableReplicationStatusEndpointsRaw interface{} `hcl:"disable_replication_status_endpoints"` | ||||
| 	DisableReplicationStatusEndpoints    bool        `hcl:"-"` | ||||
| } | ||||
|  | ||||
| // AgentAPI allows users to select which parts of the Agent API they want enabled. | ||||
| @@ -252,6 +256,7 @@ func parseListener(item *ast.ObjectItem) (*Listener, error) { | ||||
| 		l.parseHTTPHeaderSettings, | ||||
| 		l.parseChrootNamespaceSettings, | ||||
| 		l.parseRedactionSettings, | ||||
| 		l.parseDisableReplicationStatusEndpointSettings, | ||||
| 	} { | ||||
| 		err := parser() | ||||
| 		if err != nil { | ||||
| @@ -272,22 +277,114 @@ func (t ListenerType) String() string { | ||||
| 	return string(t.Normalize()) | ||||
| } | ||||
|  | ||||
| // parseAndClearBool parses a raw setting as a bool configuration parameter. If | ||||
| // the raw value is successfully parsed, the parsedSetting argument is set to it | ||||
| // and the rawSetting argument is cleared. Otherwise, the rawSetting argument is | ||||
| // left unchanged and an error is returned. | ||||
| func parseAndClearBool(rawSetting *interface{}, parsedSetting *bool) error { | ||||
| 	var err error | ||||
|  | ||||
| 	if *rawSetting != nil { | ||||
| 		*parsedSetting, err = parseutil.ParseBool(*rawSetting) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		*rawSetting = nil | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // parseAndClearString parses a raw setting as a string configuration parameter. | ||||
| // If the raw value is successfully parsed, the parsedSetting argument is set to | ||||
| // it and the rawSetting argument is cleared. Otherwise, the rawSetting argument | ||||
| // is left unchanged and an error is returned. | ||||
| func parseAndClearString(rawSetting *interface{}, parsedSetting *string) error { | ||||
| 	var err error | ||||
|  | ||||
| 	if *rawSetting != nil { | ||||
| 		*parsedSetting, err = parseutil.ParseString(*rawSetting) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		*rawSetting = nil | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // parseAndClearInt parses a raw setting as an integer configuration parameter. | ||||
| // If the raw value is successfully parsed, the parsedSetting argument is set to | ||||
| // it and the rawSetting argument is cleared. Otherwise, the rawSetting argument | ||||
| // is left unchanged and an error is returned. | ||||
| func parseAndClearInt(rawSetting *interface{}, parsedSetting *int64) error { | ||||
| 	var err error | ||||
|  | ||||
| 	if *rawSetting != nil { | ||||
| 		*parsedSetting, err = parseutil.ParseInt(*rawSetting) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		*rawSetting = nil | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // parseAndClearDurationSecond parses a raw setting as a time duration | ||||
| // configuration parameter. If the raw value is successfully parsed, the | ||||
| // parsedSetting argument is set to it and the rawSetting argument is cleared. | ||||
| // Otherwise, the rawSetting argument is left unchanged and an error is | ||||
| // returned. | ||||
| func parseAndClearDurationSecond(rawSetting *interface{}, parsedSetting *time.Duration) error { | ||||
| 	var err error | ||||
|  | ||||
| 	if *rawSetting != nil { | ||||
| 		*parsedSetting, err = parseutil.ParseDurationSecond(*rawSetting) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		*rawSetting = nil | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // parseDisableReplicationStatusEndpointSettings attempts to parse the raw | ||||
| // disable_replication_status_endpoints setting. The receiving Listener's | ||||
| // DisableReplicationStatusEndpoints field will be set with the successfully | ||||
| // parsed value. | ||||
| func (l *Listener) parseDisableReplicationStatusEndpointSettings() error { | ||||
| 	if l.Type != TCP { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if err := parseAndClearBool(&l.DisableReplicationStatusEndpointsRaw, &l.DisableReplicationStatusEndpoints); err != nil { | ||||
| 		return fmt.Errorf("invalid value for disable_replication_status_endpoints: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return 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 | ||||
| 	var ( | ||||
| 		err     error | ||||
| 		setting string | ||||
| 	) | ||||
|  | ||||
| 	// If a valid ChrootNamespace value exists, then canonicalize the namespace value | ||||
| 	if l.ChrootNamespaceRaw != nil { | ||||
| 		l.ChrootNamespace, err = parseutil.ParseString(l.ChrootNamespaceRaw) | ||||
| 	err = parseAndClearString(&l.ChrootNamespaceRaw, &setting) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("invalid value for chroot_namespace: %w", err) | ||||
| 	} | ||||
| 		l.ChrootNamespace = namespace.Canonicalize(l.ChrootNamespace) | ||||
| 	} | ||||
|  | ||||
| 	l.ChrootNamespaceRaw = nil | ||||
| 	l.ChrootNamespace = namespace.Canonicalize(setting) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @@ -327,15 +424,10 @@ func (l *Listener) parseType(fallback string) error { | ||||
| // The state of the listener will be modified, raw data will be cleared upon | ||||
| // successful parsing. | ||||
| func (l *Listener) parseRequestSettings() error { | ||||
| 	if l.MaxRequestSizeRaw != nil { | ||||
| 		maxRequestSize, err := parseutil.ParseInt(l.MaxRequestSizeRaw) | ||||
| 		if err != nil { | ||||
| 	if err := parseAndClearInt(&l.MaxRequestSizeRaw, &l.MaxRequestSize); err != nil { | ||||
| 		return fmt.Errorf("error parsing max_request_size: %w", err) | ||||
| 	} | ||||
|  | ||||
| 		l.MaxRequestSize = maxRequestSize | ||||
| 	} | ||||
|  | ||||
| 	if l.MaxRequestDurationRaw != nil { | ||||
| 		maxRequestDuration, err := parseutil.ParseDurationSecond(l.MaxRequestDurationRaw) | ||||
| 		if err != nil { | ||||
| @@ -347,22 +439,13 @@ func (l *Listener) parseRequestSettings() error { | ||||
| 		} | ||||
|  | ||||
| 		l.MaxRequestDuration = maxRequestDuration | ||||
| 		l.MaxRequestDurationRaw = nil | ||||
| 	} | ||||
|  | ||||
| 	if l.RequireRequestHeaderRaw != nil { | ||||
| 		requireRequestHeader, err := parseutil.ParseBool(l.RequireRequestHeaderRaw) | ||||
| 		if err != nil { | ||||
| 	if err := parseAndClearBool(&l.RequireRequestHeaderRaw, &l.RequireRequestHeader); 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 | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @@ -370,13 +453,9 @@ func (l *Listener) parseRequestSettings() error { | ||||
| // 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 { | ||||
| 		tlsDisable, err := parseutil.ParseBool(l.TLSDisableRaw) | ||||
| 		if err != nil { | ||||
| 	if err := parseAndClearBool(&l.TLSDisableRaw, &l.TLSDisable); err != nil { | ||||
| 		return fmt.Errorf("invalid value for tls_disable: %w", err) | ||||
| 	} | ||||
| 		l.TLSDisable = tlsDisable | ||||
| 	} | ||||
|  | ||||
| 	if l.TLSCipherSuitesRaw != "" { | ||||
| 		tlsCipherSuites, err := tlsutil.ParseCiphers(l.TLSCipherSuitesRaw) | ||||
| @@ -386,27 +465,16 @@ func (l *Listener) parseTLSSettings() error { | ||||
| 		l.TLSCipherSuites = tlsCipherSuites | ||||
| 	} | ||||
|  | ||||
| 	if l.TLSRequireAndVerifyClientCertRaw != nil { | ||||
| 		tlsRequireAndVerifyClientCert, err := parseutil.ParseBool(l.TLSRequireAndVerifyClientCertRaw) | ||||
| 		if err != nil { | ||||
| 	if err := parseAndClearBool(&l.TLSRequireAndVerifyClientCertRaw, &l.TLSRequireAndVerifyClientCert); err != nil { | ||||
| 		return fmt.Errorf("invalid value for tls_require_and_verify_client_cert: %w", err) | ||||
| 	} | ||||
| 		l.TLSRequireAndVerifyClientCert = tlsRequireAndVerifyClientCert | ||||
| 	} | ||||
|  | ||||
| 	if l.TLSDisableClientCertsRaw != nil { | ||||
| 		tlsDisableClientCerts, err := parseutil.ParseBool(l.TLSDisableClientCertsRaw) | ||||
| 		if err != nil { | ||||
| 	if err := parseAndClearBool(&l.TLSDisableClientCertsRaw, &l.TLSDisableClientCerts); 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 | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @@ -438,37 +506,21 @@ func (l *Listener) parseHTTPHeaderSettings() error { | ||||
| // 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.HTTPReadTimeout, err = parseutil.ParseDurationSecond(l.HTTPReadTimeoutRaw); err != nil { | ||||
| 	if err := parseAndClearDurationSecond(&l.HTTPReadTimeoutRaw, &l.HTTPReadTimeout); err != nil { | ||||
| 		return fmt.Errorf("error parsing http_read_timeout: %w", err) | ||||
| 	} | ||||
| 	} | ||||
|  | ||||
| 	if l.HTTPReadHeaderTimeoutRaw != nil { | ||||
| 		if l.HTTPReadHeaderTimeout, err = parseutil.ParseDurationSecond(l.HTTPReadHeaderTimeoutRaw); err != nil { | ||||
| 	if err := parseAndClearDurationSecond(&l.HTTPReadHeaderTimeoutRaw, &l.HTTPReadHeaderTimeout); err != nil { | ||||
| 		return fmt.Errorf("error parsing http_read_header_timeout: %w", err) | ||||
| 	} | ||||
| 	} | ||||
|  | ||||
| 	if l.HTTPWriteTimeoutRaw != nil { | ||||
| 		if l.HTTPWriteTimeout, err = parseutil.ParseDurationSecond(l.HTTPWriteTimeoutRaw); err != nil { | ||||
| 	if err := parseAndClearDurationSecond(&l.HTTPWriteTimeoutRaw, &l.HTTPWriteTimeout); err != nil { | ||||
| 		return fmt.Errorf("error parsing http_write_timeout: %w", err) | ||||
| 	} | ||||
| 	} | ||||
|  | ||||
| 	if l.HTTPIdleTimeoutRaw != nil { | ||||
| 		if l.HTTPIdleTimeout, err = parseutil.ParseDurationSecond(l.HTTPIdleTimeoutRaw); err != nil { | ||||
| 	if err := parseAndClearDurationSecond(&l.HTTPIdleTimeoutRaw, &l.HTTPIdleTimeout); err != nil { | ||||
| 		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 | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @@ -524,25 +576,20 @@ func (l *Listener) parseForwardedForSettings() error { | ||||
| 		if l.XForwardedForHopSkips < 0 { | ||||
| 			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.XForwardedForRejectNotAuthorized, err = parseutil.ParseBool(l.XForwardedForRejectNotAuthorizedRaw); err != nil { | ||||
| 	if err := parseAndClearBool(&l.XForwardedForRejectNotAuthorizedRaw, &l.XForwardedForRejectNotAuthorized); err != nil { | ||||
| 		return fmt.Errorf("invalid value for x_forwarded_for_reject_not_authorized: %w", err) | ||||
| 	} | ||||
| 	} | ||||
|  | ||||
| 	if l.XForwardedForRejectNotPresentRaw != nil { | ||||
| 		if l.XForwardedForRejectNotPresent, err = parseutil.ParseBool(l.XForwardedForRejectNotPresentRaw); err != nil { | ||||
| 	if err := parseAndClearBool(&l.XForwardedForRejectNotPresentRaw, &l.XForwardedForRejectNotPresent); err != nil { | ||||
| 		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 | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @@ -551,15 +598,9 @@ func (l *Listener) parseForwardedForSettings() error { | ||||
| // 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.UnauthenticatedMetricsAccess, err = parseutil.ParseBool(l.Telemetry.UnauthenticatedMetricsAccessRaw); err != nil { | ||||
| 	if err := parseAndClearBool(&l.Telemetry.UnauthenticatedMetricsAccessRaw, &l.Telemetry.UnauthenticatedMetricsAccess); err != nil { | ||||
| 		return fmt.Errorf("invalid value for telemetry.unauthenticated_metrics_access: %w", err) | ||||
| 	} | ||||
| 	} | ||||
|  | ||||
| 	l.Telemetry.UnauthenticatedMetricsAccessRaw = nil | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @@ -568,15 +609,9 @@ func (l *Listener) parseTelemetrySettings() error { | ||||
| // 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.UnauthenticatedPProfAccess, err = parseutil.ParseBool(l.Profiling.UnauthenticatedPProfAccessRaw); err != nil { | ||||
| 	if err := parseAndClearBool(&l.Profiling.UnauthenticatedPProfAccessRaw, &l.Profiling.UnauthenticatedPProfAccess); err != nil { | ||||
| 		return fmt.Errorf("invalid value for profiling.unauthenticated_pprof_access: %w", err) | ||||
| 	} | ||||
| 	} | ||||
|  | ||||
| 	l.Profiling.UnauthenticatedPProfAccessRaw = nil | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @@ -585,15 +620,9 @@ func (l *Listener) parseProfilingSettings() error { | ||||
| // 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.UnauthenticatedInFlightAccess, err = parseutil.ParseBool(l.InFlightRequestLogging.UnauthenticatedInFlightAccessRaw); err != nil { | ||||
| 	if err := parseAndClearBool(&l.InFlightRequestLogging.UnauthenticatedInFlightAccessRaw, &l.InFlightRequestLogging.UnauthenticatedInFlightAccess); err != nil { | ||||
| 		return fmt.Errorf("invalid value for inflight_requests_logging.unauthenticated_in_flight_requests_access: %w", err) | ||||
| 	} | ||||
| 	} | ||||
|  | ||||
| 	l.InFlightRequestLogging.UnauthenticatedInFlightAccessRaw = nil | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @@ -602,13 +631,9 @@ func (l *Listener) parseInFlightRequestSettings() error { | ||||
| // 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.CorsEnabled, err = parseutil.ParseBool(l.CorsEnabledRaw); err != nil { | ||||
| 	if err := parseAndClearBool(&l.CorsEnabledRaw, &l.CorsEnabled); err != nil { | ||||
| 		return fmt.Errorf("invalid value for cors_enabled: %w", err) | ||||
| 	} | ||||
| 	} | ||||
|  | ||||
| 	if strutil.StrListContains(l.CorsAllowedOrigins, "*") && len(l.CorsAllowedOrigins) > 1 { | ||||
| 		return errors.New("cors_allowed_origins must only contain a wildcard or only non-wildcard values") | ||||
| @@ -620,7 +645,6 @@ func (l *Listener) parseCORSSettings() error { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	l.CorsEnabledRaw = nil | ||||
| 	l.CorsAllowedHeadersRaw = nil | ||||
|  | ||||
| 	return nil | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import ( | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| ) | ||||
|  | ||||
| @@ -1095,3 +1096,269 @@ func TestListener_parseRedactionSettings(t *testing.T) { | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestParseAndClearBool(t *testing.T) { | ||||
| 	testcases := []struct { | ||||
| 		name           string | ||||
| 		raw            interface{} | ||||
| 		rawAssertion   func(assert.TestingT, any, ...any) bool | ||||
| 		expectedParsed bool | ||||
| 		errorAssertion func(assert.TestingT, error, ...any) bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:           "valid-true-as-string", | ||||
| 			raw:            "true", | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: true, | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "valid-false-as-string", | ||||
| 			raw:            "false", | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: false, | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "valid-true-as-bool", | ||||
| 			raw:            true, | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: true, | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "valid-false-as-bool", | ||||
| 			raw:            false, | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: false, | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "valid-true-as-string-mix-case", | ||||
| 			raw:            "True", | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: true, | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "valid-false-as-integer", | ||||
| 			raw:            0, | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: false, | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "valid-true-as-integer", | ||||
| 			raw:            2, | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: true, | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "valid-true-as-float", | ||||
| 			raw:            3.14, | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: true, | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "valid-false-as-float", | ||||
| 			raw:            0.0, | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: false, | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "invalid-as-string", | ||||
| 			raw:            "0.0.0.0:8200", | ||||
| 			rawAssertion:   assert.NotNil, | ||||
| 			errorAssertion: assert.Error, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "invalid-as-struct", | ||||
| 			raw:            struct{}{}, | ||||
| 			rawAssertion:   assert.NotNil, | ||||
| 			errorAssertion: assert.Error, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "not-set", | ||||
| 			raw:            nil, | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, testcase := range testcases { | ||||
| 		var parsed bool | ||||
| 		err := parseAndClearBool(&testcase.raw, &parsed) | ||||
|  | ||||
| 		testcase.errorAssertion(t, err, testcase.name) | ||||
| 		assert.Equal(t, testcase.expectedParsed, parsed, testcase.name) | ||||
| 		testcase.rawAssertion(t, testcase.raw, testcase.name) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestParseAndClearString(t *testing.T) { | ||||
| 	testcases := []struct { | ||||
| 		name           string | ||||
| 		raw            any | ||||
| 		rawAssertion   func(assert.TestingT, any, ...any) bool | ||||
| 		expectedParsed string | ||||
| 		errorAssertion func(assert.TestingT, error, ...any) bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:           "valid-empty-string", | ||||
| 			raw:            "", | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: "", | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "valid-some-string", | ||||
| 			raw:            "blah blah", | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: "blah blah", | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "valid-as-integer", | ||||
| 			raw:            8, | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: "8", | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "valid-as-bool", | ||||
| 			raw:            true, | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: "1", | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "not-set", | ||||
| 			raw:            nil, | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: "", | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "invalid-as-struct", | ||||
| 			raw:            struct{}{}, | ||||
| 			rawAssertion:   assert.NotNil, | ||||
| 			errorAssertion: assert.Error, | ||||
| 		}, | ||||
| 	} | ||||
| 	for _, testcase := range testcases { | ||||
| 		var parsed string | ||||
| 		err := parseAndClearString(&testcase.raw, &parsed) | ||||
|  | ||||
| 		testcase.errorAssertion(t, err, testcase.name) | ||||
| 		assert.Equal(t, testcase.expectedParsed, parsed, testcase.name) | ||||
| 		testcase.rawAssertion(t, testcase.raw, testcase.name) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestParseAndClearInt(t *testing.T) { | ||||
| 	testcases := []struct { | ||||
| 		name           string | ||||
| 		raw            any | ||||
| 		rawAssertion   func(assert.TestingT, any, ...any) bool | ||||
| 		expectedParsed int64 | ||||
| 		errorAssertion func(assert.TestingT, error, ...any) bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:           "valid-as-int", | ||||
| 			raw:            200, | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: int64(200), | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "valid-as-string", | ||||
| 			raw:            "53", | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: int64(53), | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "invalid-as-hex-string", | ||||
| 			raw:            "0xa", | ||||
| 			rawAssertion:   assert.NotNil, | ||||
| 			errorAssertion: assert.Error, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "not-set", | ||||
| 			raw:            nil, | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, testcase := range testcases { | ||||
| 		var parsed int64 | ||||
| 		err := parseAndClearInt(&testcase.raw, &parsed) | ||||
|  | ||||
| 		testcase.errorAssertion(t, err, testcase.name) | ||||
| 		assert.Equal(t, testcase.expectedParsed, parsed, testcase.name) | ||||
| 		testcase.rawAssertion(t, testcase.raw, testcase.name) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestParseAndClearDurationSecond(t *testing.T) { | ||||
| 	testcases := []struct { | ||||
| 		name           string | ||||
| 		raw            any | ||||
| 		rawAssertion   func(assert.TestingT, any, ...any) bool | ||||
| 		expectedParsed time.Duration | ||||
| 		errorAssertion func(assert.TestingT, error, ...any) bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:           "valid-as-string", | ||||
| 			raw:            "30s", | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: time.Duration(30 * time.Second), | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "valid-as-string-more-complex", | ||||
| 			raw:            "29h24m49s", | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: time.Duration((29 * time.Hour) + (24 * time.Minute) + (49 * time.Second)), | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "invalid-as-string-using-days", | ||||
| 			raw:            "1d3s", | ||||
| 			rawAssertion:   assert.NotNil, | ||||
| 			errorAssertion: assert.Error, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "valid-as-integer", | ||||
| 			raw:            87, | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			expectedParsed: time.Duration(87 * time.Second), | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "not-set", | ||||
| 			raw:            nil, | ||||
| 			rawAssertion:   assert.Nil, | ||||
| 			errorAssertion: assert.NoError, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "invalid-as-struct", | ||||
| 			raw:            struct{}{}, | ||||
| 			rawAssertion:   assert.NotNil, | ||||
| 			errorAssertion: assert.Error, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, testcase := range testcases { | ||||
| 		var parsed time.Duration | ||||
|  | ||||
| 		err := parseAndClearDurationSecond(&testcase.raw, &parsed) | ||||
| 		testcase.errorAssertion(t, err, testcase.name) | ||||
| 		assert.Equal(t, testcase.expectedParsed, parsed) | ||||
| 		testcase.rawAssertion(t, testcase.raw, testcase.name) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -202,6 +202,9 @@ default value in the `"/sys/config/ui"` [API endpoint](/vault/api-docs/system/co | ||||
|   there is no X-Forwarded-For header or it is empty, the client address will be | ||||
|   used as-is, rather than the client connection rejected. | ||||
|  | ||||
| - `disable_replication_status_endpoints` `(bool: false)` - Disables replication | ||||
|   status endpoints for the configured listener when set to `true`. | ||||
|  | ||||
| ### `telemetry` parameters | ||||
|  | ||||
| - `unauthenticated_metrics_access` `(bool: false)` - If set to true, allows | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Marc Boudreau
					Marc Boudreau