diff --git a/agent/config/builder.go b/agent/config/builder.go
index 940e6f207..34125ac1c 100644
--- a/agent/config/builder.go
+++ b/agent/config/builder.go
@@ -690,6 +690,7 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) {
DisableAnonymousSignature: b.boolVal(c.DisableAnonymousSignature),
DisableCoordinates: b.boolVal(c.DisableCoordinates),
DisableHostNodeID: b.boolVal(c.DisableHostNodeID),
+ DisableHTTPUnprintableCharFilter: b.boolVal(c.DisableHTTPUnprintableCharFilter),
DisableKeyringFile: b.boolVal(c.DisableKeyringFile),
DisableRemoteExec: b.boolVal(c.DisableRemoteExec),
DisableUpdateCheck: b.boolVal(c.DisableUpdateCheck),
diff --git a/agent/config/config.go b/agent/config/config.go
index c54090b3f..0baf45325 100644
--- a/agent/config/config.go
+++ b/agent/config/config.go
@@ -136,106 +136,107 @@ func Parse(data string, format string) (c Config, err error) {
// configuration it should be treated as an external API which cannot be
// changed and refactored at will since this will break existing setups.
type Config struct {
- ACLAgentMasterToken *string `json:"acl_agent_master_token,omitempty" hcl:"acl_agent_master_token" mapstructure:"acl_agent_master_token"`
- ACLAgentToken *string `json:"acl_agent_token,omitempty" hcl:"acl_agent_token" mapstructure:"acl_agent_token"`
- ACLDatacenter *string `json:"acl_datacenter,omitempty" hcl:"acl_datacenter" mapstructure:"acl_datacenter"`
- ACLDefaultPolicy *string `json:"acl_default_policy,omitempty" hcl:"acl_default_policy" mapstructure:"acl_default_policy"`
- ACLDownPolicy *string `json:"acl_down_policy,omitempty" hcl:"acl_down_policy" mapstructure:"acl_down_policy"`
- ACLEnableKeyListPolicy *bool `json:"acl_enable_key_list_policy,omitempty" hcl:"acl_enable_key_list_policy" mapstructure:"acl_enable_key_list_policy"`
- ACLEnforceVersion8 *bool `json:"acl_enforce_version_8,omitempty" hcl:"acl_enforce_version_8" mapstructure:"acl_enforce_version_8"`
- ACLMasterToken *string `json:"acl_master_token,omitempty" hcl:"acl_master_token" mapstructure:"acl_master_token"`
- ACLReplicationToken *string `json:"acl_replication_token,omitempty" hcl:"acl_replication_token" mapstructure:"acl_replication_token"`
- ACLTTL *string `json:"acl_ttl,omitempty" hcl:"acl_ttl" mapstructure:"acl_ttl"`
- ACLToken *string `json:"acl_token,omitempty" hcl:"acl_token" mapstructure:"acl_token"`
- Addresses Addresses `json:"addresses,omitempty" hcl:"addresses" mapstructure:"addresses"`
- AdvertiseAddrLAN *string `json:"advertise_addr,omitempty" hcl:"advertise_addr" mapstructure:"advertise_addr"`
- AdvertiseAddrWAN *string `json:"advertise_addr_wan,omitempty" hcl:"advertise_addr_wan" mapstructure:"advertise_addr_wan"`
- Autopilot Autopilot `json:"autopilot,omitempty" hcl:"autopilot" mapstructure:"autopilot"`
- BindAddr *string `json:"bind_addr,omitempty" hcl:"bind_addr" mapstructure:"bind_addr"`
- Bootstrap *bool `json:"bootstrap,omitempty" hcl:"bootstrap" mapstructure:"bootstrap"`
- BootstrapExpect *int `json:"bootstrap_expect,omitempty" hcl:"bootstrap_expect" mapstructure:"bootstrap_expect"`
- CAFile *string `json:"ca_file,omitempty" hcl:"ca_file" mapstructure:"ca_file"`
- CAPath *string `json:"ca_path,omitempty" hcl:"ca_path" mapstructure:"ca_path"`
- CertFile *string `json:"cert_file,omitempty" hcl:"cert_file" mapstructure:"cert_file"`
- Check *CheckDefinition `json:"check,omitempty" hcl:"check" mapstructure:"check"` // needs to be a pointer to avoid partial merges
- CheckUpdateInterval *string `json:"check_update_interval,omitempty" hcl:"check_update_interval" mapstructure:"check_update_interval"`
- Checks []CheckDefinition `json:"checks,omitempty" hcl:"checks" mapstructure:"checks"`
- ClientAddr *string `json:"client_addr,omitempty" hcl:"client_addr" mapstructure:"client_addr"`
- Connect Connect `json:"connect,omitempty" hcl:"connect" mapstructure:"connect"`
- DNS DNS `json:"dns_config,omitempty" hcl:"dns_config" mapstructure:"dns_config"`
- DNSDomain *string `json:"domain,omitempty" hcl:"domain" mapstructure:"domain"`
- DNSRecursors []string `json:"recursors,omitempty" hcl:"recursors" mapstructure:"recursors"`
- DataDir *string `json:"data_dir,omitempty" hcl:"data_dir" mapstructure:"data_dir"`
- Datacenter *string `json:"datacenter,omitempty" hcl:"datacenter" mapstructure:"datacenter"`
- DisableAnonymousSignature *bool `json:"disable_anonymous_signature,omitempty" hcl:"disable_anonymous_signature" mapstructure:"disable_anonymous_signature"`
- DisableCoordinates *bool `json:"disable_coordinates,omitempty" hcl:"disable_coordinates" mapstructure:"disable_coordinates"`
- DisableHostNodeID *bool `json:"disable_host_node_id,omitempty" hcl:"disable_host_node_id" mapstructure:"disable_host_node_id"`
- DisableKeyringFile *bool `json:"disable_keyring_file,omitempty" hcl:"disable_keyring_file" mapstructure:"disable_keyring_file"`
- DisableRemoteExec *bool `json:"disable_remote_exec,omitempty" hcl:"disable_remote_exec" mapstructure:"disable_remote_exec"`
- DisableUpdateCheck *bool `json:"disable_update_check,omitempty" hcl:"disable_update_check" mapstructure:"disable_update_check"`
- DiscardCheckOutput *bool `json:"discard_check_output" hcl:"discard_check_output" mapstructure:"discard_check_output"`
- DiscoveryMaxStale *string `json:"discovery_max_stale" hcl:"discovery_max_stale" mapstructure:"discovery_max_stale"`
- EnableACLReplication *bool `json:"enable_acl_replication,omitempty" hcl:"enable_acl_replication" mapstructure:"enable_acl_replication"`
- EnableAgentTLSForChecks *bool `json:"enable_agent_tls_for_checks,omitempty" hcl:"enable_agent_tls_for_checks" mapstructure:"enable_agent_tls_for_checks"`
- EnableDebug *bool `json:"enable_debug,omitempty" hcl:"enable_debug" mapstructure:"enable_debug"`
- EnableScriptChecks *bool `json:"enable_script_checks,omitempty" hcl:"enable_script_checks" mapstructure:"enable_script_checks"`
- EnableSyslog *bool `json:"enable_syslog,omitempty" hcl:"enable_syslog" mapstructure:"enable_syslog"`
- EncryptKey *string `json:"encrypt,omitempty" hcl:"encrypt" mapstructure:"encrypt"`
- EncryptVerifyIncoming *bool `json:"encrypt_verify_incoming,omitempty" hcl:"encrypt_verify_incoming" mapstructure:"encrypt_verify_incoming"`
- EncryptVerifyOutgoing *bool `json:"encrypt_verify_outgoing,omitempty" hcl:"encrypt_verify_outgoing" mapstructure:"encrypt_verify_outgoing"`
- HTTPConfig HTTPConfig `json:"http_config,omitempty" hcl:"http_config" mapstructure:"http_config"`
- KeyFile *string `json:"key_file,omitempty" hcl:"key_file" mapstructure:"key_file"`
- LeaveOnTerm *bool `json:"leave_on_terminate,omitempty" hcl:"leave_on_terminate" mapstructure:"leave_on_terminate"`
- Limits Limits `json:"limits,omitempty" hcl:"limits" mapstructure:"limits"`
- LogLevel *string `json:"log_level,omitempty" hcl:"log_level" mapstructure:"log_level"`
- NodeID *string `json:"node_id,omitempty" hcl:"node_id" mapstructure:"node_id"`
- NodeMeta map[string]string `json:"node_meta,omitempty" hcl:"node_meta" mapstructure:"node_meta"`
- NodeName *string `json:"node_name,omitempty" hcl:"node_name" mapstructure:"node_name"`
- NonVotingServer *bool `json:"non_voting_server,omitempty" hcl:"non_voting_server" mapstructure:"non_voting_server"`
- Performance Performance `json:"performance,omitempty" hcl:"performance" mapstructure:"performance"`
- PidFile *string `json:"pid_file,omitempty" hcl:"pid_file" mapstructure:"pid_file"`
- Ports Ports `json:"ports,omitempty" hcl:"ports" mapstructure:"ports"`
- RPCProtocol *int `json:"protocol,omitempty" hcl:"protocol" mapstructure:"protocol"`
- RaftProtocol *int `json:"raft_protocol,omitempty" hcl:"raft_protocol" mapstructure:"raft_protocol"`
- RaftSnapshotThreshold *int `json:"raft_snapshot_threshold,omitempty" hcl:"raft_snapshot_threshold" mapstructure:"raft_snapshot_threshold"`
- RaftSnapshotInterval *string `json:"raft_snapshot_interval,omitempty" hcl:"raft_snapshot_interval" mapstructure:"raft_snapshot_interval"`
- ReconnectTimeoutLAN *string `json:"reconnect_timeout,omitempty" hcl:"reconnect_timeout" mapstructure:"reconnect_timeout"`
- ReconnectTimeoutWAN *string `json:"reconnect_timeout_wan,omitempty" hcl:"reconnect_timeout_wan" mapstructure:"reconnect_timeout_wan"`
- RejoinAfterLeave *bool `json:"rejoin_after_leave,omitempty" hcl:"rejoin_after_leave" mapstructure:"rejoin_after_leave"`
- RetryJoinIntervalLAN *string `json:"retry_interval,omitempty" hcl:"retry_interval" mapstructure:"retry_interval"`
- RetryJoinIntervalWAN *string `json:"retry_interval_wan,omitempty" hcl:"retry_interval_wan" mapstructure:"retry_interval_wan"`
- RetryJoinLAN []string `json:"retry_join,omitempty" hcl:"retry_join" mapstructure:"retry_join"`
- RetryJoinMaxAttemptsLAN *int `json:"retry_max,omitempty" hcl:"retry_max" mapstructure:"retry_max"`
- RetryJoinMaxAttemptsWAN *int `json:"retry_max_wan,omitempty" hcl:"retry_max_wan" mapstructure:"retry_max_wan"`
- RetryJoinWAN []string `json:"retry_join_wan,omitempty" hcl:"retry_join_wan" mapstructure:"retry_join_wan"`
- SegmentName *string `json:"segment,omitempty" hcl:"segment" mapstructure:"segment"`
- Segments []Segment `json:"segments,omitempty" hcl:"segments" mapstructure:"segments"`
- SerfBindAddrLAN *string `json:"serf_lan,omitempty" hcl:"serf_lan" mapstructure:"serf_lan"`
- SerfBindAddrWAN *string `json:"serf_wan,omitempty" hcl:"serf_wan" mapstructure:"serf_wan"`
- ServerMode *bool `json:"server,omitempty" hcl:"server" mapstructure:"server"`
- ServerName *string `json:"server_name,omitempty" hcl:"server_name" mapstructure:"server_name"`
- Service *ServiceDefinition `json:"service,omitempty" hcl:"service" mapstructure:"service"`
- Services []ServiceDefinition `json:"services,omitempty" hcl:"services" mapstructure:"services"`
- SessionTTLMin *string `json:"session_ttl_min,omitempty" hcl:"session_ttl_min" mapstructure:"session_ttl_min"`
- SkipLeaveOnInt *bool `json:"skip_leave_on_interrupt,omitempty" hcl:"skip_leave_on_interrupt" mapstructure:"skip_leave_on_interrupt"`
- StartJoinAddrsLAN []string `json:"start_join,omitempty" hcl:"start_join" mapstructure:"start_join"`
- StartJoinAddrsWAN []string `json:"start_join_wan,omitempty" hcl:"start_join_wan" mapstructure:"start_join_wan"`
- SyslogFacility *string `json:"syslog_facility,omitempty" hcl:"syslog_facility" mapstructure:"syslog_facility"`
- TLSCipherSuites *string `json:"tls_cipher_suites,omitempty" hcl:"tls_cipher_suites" mapstructure:"tls_cipher_suites"`
- TLSMinVersion *string `json:"tls_min_version,omitempty" hcl:"tls_min_version" mapstructure:"tls_min_version"`
- TLSPreferServerCipherSuites *bool `json:"tls_prefer_server_cipher_suites,omitempty" hcl:"tls_prefer_server_cipher_suites" mapstructure:"tls_prefer_server_cipher_suites"`
- TaggedAddresses map[string]string `json:"tagged_addresses,omitempty" hcl:"tagged_addresses" mapstructure:"tagged_addresses"`
- Telemetry Telemetry `json:"telemetry,omitempty" hcl:"telemetry" mapstructure:"telemetry"`
- TranslateWANAddrs *bool `json:"translate_wan_addrs,omitempty" hcl:"translate_wan_addrs" mapstructure:"translate_wan_addrs"`
- UI *bool `json:"ui,omitempty" hcl:"ui" mapstructure:"ui"`
- UIDir *string `json:"ui_dir,omitempty" hcl:"ui_dir" mapstructure:"ui_dir"`
- UnixSocket UnixSocket `json:"unix_sockets,omitempty" hcl:"unix_sockets" mapstructure:"unix_sockets"`
- VerifyIncoming *bool `json:"verify_incoming,omitempty" hcl:"verify_incoming" mapstructure:"verify_incoming"`
- VerifyIncomingHTTPS *bool `json:"verify_incoming_https,omitempty" hcl:"verify_incoming_https" mapstructure:"verify_incoming_https"`
- VerifyIncomingRPC *bool `json:"verify_incoming_rpc,omitempty" hcl:"verify_incoming_rpc" mapstructure:"verify_incoming_rpc"`
- VerifyOutgoing *bool `json:"verify_outgoing,omitempty" hcl:"verify_outgoing" mapstructure:"verify_outgoing"`
- VerifyServerHostname *bool `json:"verify_server_hostname,omitempty" hcl:"verify_server_hostname" mapstructure:"verify_server_hostname"`
- Watches []map[string]interface{} `json:"watches,omitempty" hcl:"watches" mapstructure:"watches"`
+ ACLAgentMasterToken *string `json:"acl_agent_master_token,omitempty" hcl:"acl_agent_master_token" mapstructure:"acl_agent_master_token"`
+ ACLAgentToken *string `json:"acl_agent_token,omitempty" hcl:"acl_agent_token" mapstructure:"acl_agent_token"`
+ ACLDatacenter *string `json:"acl_datacenter,omitempty" hcl:"acl_datacenter" mapstructure:"acl_datacenter"`
+ ACLDefaultPolicy *string `json:"acl_default_policy,omitempty" hcl:"acl_default_policy" mapstructure:"acl_default_policy"`
+ ACLDownPolicy *string `json:"acl_down_policy,omitempty" hcl:"acl_down_policy" mapstructure:"acl_down_policy"`
+ ACLEnableKeyListPolicy *bool `json:"acl_enable_key_list_policy,omitempty" hcl:"acl_enable_key_list_policy" mapstructure:"acl_enable_key_list_policy"`
+ ACLEnforceVersion8 *bool `json:"acl_enforce_version_8,omitempty" hcl:"acl_enforce_version_8" mapstructure:"acl_enforce_version_8"`
+ ACLMasterToken *string `json:"acl_master_token,omitempty" hcl:"acl_master_token" mapstructure:"acl_master_token"`
+ ACLReplicationToken *string `json:"acl_replication_token,omitempty" hcl:"acl_replication_token" mapstructure:"acl_replication_token"`
+ ACLTTL *string `json:"acl_ttl,omitempty" hcl:"acl_ttl" mapstructure:"acl_ttl"`
+ ACLToken *string `json:"acl_token,omitempty" hcl:"acl_token" mapstructure:"acl_token"`
+ Addresses Addresses `json:"addresses,omitempty" hcl:"addresses" mapstructure:"addresses"`
+ AdvertiseAddrLAN *string `json:"advertise_addr,omitempty" hcl:"advertise_addr" mapstructure:"advertise_addr"`
+ AdvertiseAddrWAN *string `json:"advertise_addr_wan,omitempty" hcl:"advertise_addr_wan" mapstructure:"advertise_addr_wan"`
+ Autopilot Autopilot `json:"autopilot,omitempty" hcl:"autopilot" mapstructure:"autopilot"`
+ BindAddr *string `json:"bind_addr,omitempty" hcl:"bind_addr" mapstructure:"bind_addr"`
+ Bootstrap *bool `json:"bootstrap,omitempty" hcl:"bootstrap" mapstructure:"bootstrap"`
+ BootstrapExpect *int `json:"bootstrap_expect,omitempty" hcl:"bootstrap_expect" mapstructure:"bootstrap_expect"`
+ CAFile *string `json:"ca_file,omitempty" hcl:"ca_file" mapstructure:"ca_file"`
+ CAPath *string `json:"ca_path,omitempty" hcl:"ca_path" mapstructure:"ca_path"`
+ CertFile *string `json:"cert_file,omitempty" hcl:"cert_file" mapstructure:"cert_file"`
+ Check *CheckDefinition `json:"check,omitempty" hcl:"check" mapstructure:"check"` // needs to be a pointer to avoid partial merges
+ CheckUpdateInterval *string `json:"check_update_interval,omitempty" hcl:"check_update_interval" mapstructure:"check_update_interval"`
+ Checks []CheckDefinition `json:"checks,omitempty" hcl:"checks" mapstructure:"checks"`
+ ClientAddr *string `json:"client_addr,omitempty" hcl:"client_addr" mapstructure:"client_addr"`
+ Connect Connect `json:"connect,omitempty" hcl:"connect" mapstructure:"connect"`
+ DNS DNS `json:"dns_config,omitempty" hcl:"dns_config" mapstructure:"dns_config"`
+ DNSDomain *string `json:"domain,omitempty" hcl:"domain" mapstructure:"domain"`
+ DNSRecursors []string `json:"recursors,omitempty" hcl:"recursors" mapstructure:"recursors"`
+ DataDir *string `json:"data_dir,omitempty" hcl:"data_dir" mapstructure:"data_dir"`
+ Datacenter *string `json:"datacenter,omitempty" hcl:"datacenter" mapstructure:"datacenter"`
+ DisableAnonymousSignature *bool `json:"disable_anonymous_signature,omitempty" hcl:"disable_anonymous_signature" mapstructure:"disable_anonymous_signature"`
+ DisableCoordinates *bool `json:"disable_coordinates,omitempty" hcl:"disable_coordinates" mapstructure:"disable_coordinates"`
+ DisableHostNodeID *bool `json:"disable_host_node_id,omitempty" hcl:"disable_host_node_id" mapstructure:"disable_host_node_id"`
+ DisableHTTPUnprintableCharFilter *bool `json:"disable_http_unprintable_char_filter,omitempty" hcl:"disable_http_unprintable_char_filter" mapstructure:"disable_http_unprintable_char_filter"`
+ DisableKeyringFile *bool `json:"disable_keyring_file,omitempty" hcl:"disable_keyring_file" mapstructure:"disable_keyring_file"`
+ DisableRemoteExec *bool `json:"disable_remote_exec,omitempty" hcl:"disable_remote_exec" mapstructure:"disable_remote_exec"`
+ DisableUpdateCheck *bool `json:"disable_update_check,omitempty" hcl:"disable_update_check" mapstructure:"disable_update_check"`
+ DiscardCheckOutput *bool `json:"discard_check_output" hcl:"discard_check_output" mapstructure:"discard_check_output"`
+ DiscoveryMaxStale *string `json:"discovery_max_stale" hcl:"discovery_max_stale" mapstructure:"discovery_max_stale"`
+ EnableACLReplication *bool `json:"enable_acl_replication,omitempty" hcl:"enable_acl_replication" mapstructure:"enable_acl_replication"`
+ EnableAgentTLSForChecks *bool `json:"enable_agent_tls_for_checks,omitempty" hcl:"enable_agent_tls_for_checks" mapstructure:"enable_agent_tls_for_checks"`
+ EnableDebug *bool `json:"enable_debug,omitempty" hcl:"enable_debug" mapstructure:"enable_debug"`
+ EnableScriptChecks *bool `json:"enable_script_checks,omitempty" hcl:"enable_script_checks" mapstructure:"enable_script_checks"`
+ EnableSyslog *bool `json:"enable_syslog,omitempty" hcl:"enable_syslog" mapstructure:"enable_syslog"`
+ EncryptKey *string `json:"encrypt,omitempty" hcl:"encrypt" mapstructure:"encrypt"`
+ EncryptVerifyIncoming *bool `json:"encrypt_verify_incoming,omitempty" hcl:"encrypt_verify_incoming" mapstructure:"encrypt_verify_incoming"`
+ EncryptVerifyOutgoing *bool `json:"encrypt_verify_outgoing,omitempty" hcl:"encrypt_verify_outgoing" mapstructure:"encrypt_verify_outgoing"`
+ HTTPConfig HTTPConfig `json:"http_config,omitempty" hcl:"http_config" mapstructure:"http_config"`
+ KeyFile *string `json:"key_file,omitempty" hcl:"key_file" mapstructure:"key_file"`
+ LeaveOnTerm *bool `json:"leave_on_terminate,omitempty" hcl:"leave_on_terminate" mapstructure:"leave_on_terminate"`
+ Limits Limits `json:"limits,omitempty" hcl:"limits" mapstructure:"limits"`
+ LogLevel *string `json:"log_level,omitempty" hcl:"log_level" mapstructure:"log_level"`
+ NodeID *string `json:"node_id,omitempty" hcl:"node_id" mapstructure:"node_id"`
+ NodeMeta map[string]string `json:"node_meta,omitempty" hcl:"node_meta" mapstructure:"node_meta"`
+ NodeName *string `json:"node_name,omitempty" hcl:"node_name" mapstructure:"node_name"`
+ NonVotingServer *bool `json:"non_voting_server,omitempty" hcl:"non_voting_server" mapstructure:"non_voting_server"`
+ Performance Performance `json:"performance,omitempty" hcl:"performance" mapstructure:"performance"`
+ PidFile *string `json:"pid_file,omitempty" hcl:"pid_file" mapstructure:"pid_file"`
+ Ports Ports `json:"ports,omitempty" hcl:"ports" mapstructure:"ports"`
+ RPCProtocol *int `json:"protocol,omitempty" hcl:"protocol" mapstructure:"protocol"`
+ RaftProtocol *int `json:"raft_protocol,omitempty" hcl:"raft_protocol" mapstructure:"raft_protocol"`
+ RaftSnapshotThreshold *int `json:"raft_snapshot_threshold,omitempty" hcl:"raft_snapshot_threshold" mapstructure:"raft_snapshot_threshold"`
+ RaftSnapshotInterval *string `json:"raft_snapshot_interval,omitempty" hcl:"raft_snapshot_interval" mapstructure:"raft_snapshot_interval"`
+ ReconnectTimeoutLAN *string `json:"reconnect_timeout,omitempty" hcl:"reconnect_timeout" mapstructure:"reconnect_timeout"`
+ ReconnectTimeoutWAN *string `json:"reconnect_timeout_wan,omitempty" hcl:"reconnect_timeout_wan" mapstructure:"reconnect_timeout_wan"`
+ RejoinAfterLeave *bool `json:"rejoin_after_leave,omitempty" hcl:"rejoin_after_leave" mapstructure:"rejoin_after_leave"`
+ RetryJoinIntervalLAN *string `json:"retry_interval,omitempty" hcl:"retry_interval" mapstructure:"retry_interval"`
+ RetryJoinIntervalWAN *string `json:"retry_interval_wan,omitempty" hcl:"retry_interval_wan" mapstructure:"retry_interval_wan"`
+ RetryJoinLAN []string `json:"retry_join,omitempty" hcl:"retry_join" mapstructure:"retry_join"`
+ RetryJoinMaxAttemptsLAN *int `json:"retry_max,omitempty" hcl:"retry_max" mapstructure:"retry_max"`
+ RetryJoinMaxAttemptsWAN *int `json:"retry_max_wan,omitempty" hcl:"retry_max_wan" mapstructure:"retry_max_wan"`
+ RetryJoinWAN []string `json:"retry_join_wan,omitempty" hcl:"retry_join_wan" mapstructure:"retry_join_wan"`
+ SegmentName *string `json:"segment,omitempty" hcl:"segment" mapstructure:"segment"`
+ Segments []Segment `json:"segments,omitempty" hcl:"segments" mapstructure:"segments"`
+ SerfBindAddrLAN *string `json:"serf_lan,omitempty" hcl:"serf_lan" mapstructure:"serf_lan"`
+ SerfBindAddrWAN *string `json:"serf_wan,omitempty" hcl:"serf_wan" mapstructure:"serf_wan"`
+ ServerMode *bool `json:"server,omitempty" hcl:"server" mapstructure:"server"`
+ ServerName *string `json:"server_name,omitempty" hcl:"server_name" mapstructure:"server_name"`
+ Service *ServiceDefinition `json:"service,omitempty" hcl:"service" mapstructure:"service"`
+ Services []ServiceDefinition `json:"services,omitempty" hcl:"services" mapstructure:"services"`
+ SessionTTLMin *string `json:"session_ttl_min,omitempty" hcl:"session_ttl_min" mapstructure:"session_ttl_min"`
+ SkipLeaveOnInt *bool `json:"skip_leave_on_interrupt,omitempty" hcl:"skip_leave_on_interrupt" mapstructure:"skip_leave_on_interrupt"`
+ StartJoinAddrsLAN []string `json:"start_join,omitempty" hcl:"start_join" mapstructure:"start_join"`
+ StartJoinAddrsWAN []string `json:"start_join_wan,omitempty" hcl:"start_join_wan" mapstructure:"start_join_wan"`
+ SyslogFacility *string `json:"syslog_facility,omitempty" hcl:"syslog_facility" mapstructure:"syslog_facility"`
+ TLSCipherSuites *string `json:"tls_cipher_suites,omitempty" hcl:"tls_cipher_suites" mapstructure:"tls_cipher_suites"`
+ TLSMinVersion *string `json:"tls_min_version,omitempty" hcl:"tls_min_version" mapstructure:"tls_min_version"`
+ TLSPreferServerCipherSuites *bool `json:"tls_prefer_server_cipher_suites,omitempty" hcl:"tls_prefer_server_cipher_suites" mapstructure:"tls_prefer_server_cipher_suites"`
+ TaggedAddresses map[string]string `json:"tagged_addresses,omitempty" hcl:"tagged_addresses" mapstructure:"tagged_addresses"`
+ Telemetry Telemetry `json:"telemetry,omitempty" hcl:"telemetry" mapstructure:"telemetry"`
+ TranslateWANAddrs *bool `json:"translate_wan_addrs,omitempty" hcl:"translate_wan_addrs" mapstructure:"translate_wan_addrs"`
+ UI *bool `json:"ui,omitempty" hcl:"ui" mapstructure:"ui"`
+ UIDir *string `json:"ui_dir,omitempty" hcl:"ui_dir" mapstructure:"ui_dir"`
+ UnixSocket UnixSocket `json:"unix_sockets,omitempty" hcl:"unix_sockets" mapstructure:"unix_sockets"`
+ VerifyIncoming *bool `json:"verify_incoming,omitempty" hcl:"verify_incoming" mapstructure:"verify_incoming"`
+ VerifyIncomingHTTPS *bool `json:"verify_incoming_https,omitempty" hcl:"verify_incoming_https" mapstructure:"verify_incoming_https"`
+ VerifyIncomingRPC *bool `json:"verify_incoming_rpc,omitempty" hcl:"verify_incoming_rpc" mapstructure:"verify_incoming_rpc"`
+ VerifyOutgoing *bool `json:"verify_outgoing,omitempty" hcl:"verify_outgoing" mapstructure:"verify_outgoing"`
+ VerifyServerHostname *bool `json:"verify_server_hostname,omitempty" hcl:"verify_server_hostname" mapstructure:"verify_server_hostname"`
+ Watches []map[string]interface{} `json:"watches,omitempty" hcl:"watches" mapstructure:"watches"`
// This isn't used by Consul but we've documented a feature where users
// can deploy their snapshot agent configs alongside their Consul configs
diff --git a/agent/config/runtime.go b/agent/config/runtime.go
index d63c7e20d..dfb7893e0 100644
--- a/agent/config/runtime.go
+++ b/agent/config/runtime.go
@@ -564,6 +564,16 @@ type RuntimeConfig struct {
// flag: -disable-host-node-id
DisableHostNodeID bool
+ // DisableHTTPUnprintableCharFilter will bypass the filter preventing HTTP
+ // URLs from containing unprintable chars. This filter was added in 1.0.3 as a
+ // response to a vulnerability report. Disabling this is never recommended in
+ // general however some users who have keys written in older versions of
+ // Consul may use this to temporarily disable the filter such that they can
+ // delete those keys again! We do not recommend leaving it disabled long term.
+ //
+ // hcl: disable_http_unprintable_char_filter
+ DisableHTTPUnprintableCharFilter bool
+
// DisableKeyringFile disables writing the keyring to a file.
//
// hcl: disable_keyring_file = (true|false)
diff --git a/agent/config/runtime_test.go b/agent/config/runtime_test.go
index c81ed7e3a..def6246d6 100644
--- a/agent/config/runtime_test.go
+++ b/agent/config/runtime_test.go
@@ -2621,6 +2621,7 @@ func TestFullConfig(t *testing.T) {
"disable_anonymous_signature": true,
"disable_coordinates": true,
"disable_host_node_id": true,
+ "disable_http_unprintable_char_filter": true,
"disable_keyring_file": true,
"disable_remote_exec": true,
"disable_update_check": true,
@@ -3084,6 +3085,7 @@ func TestFullConfig(t *testing.T) {
disable_anonymous_signature = true
disable_coordinates = true
disable_host_node_id = true
+ disable_http_unprintable_char_filter = true
disable_keyring_file = true
disable_remote_exec = true
disable_update_check = true
@@ -3678,75 +3680,76 @@ func TestFullConfig(t *testing.T) {
"connect_timeout_ms": float64(1000),
"pedantic_mode": true,
},
- DNSAddrs: []net.Addr{tcpAddr("93.95.95.81:7001"), udpAddr("93.95.95.81:7001")},
- DNSARecordLimit: 29907,
- DNSAllowStale: true,
- DNSDisableCompression: true,
- DNSDomain: "7W1xXSqd",
- DNSEnableTruncate: true,
- DNSMaxStale: 29685 * time.Second,
- DNSNodeTTL: 7084 * time.Second,
- DNSOnlyPassing: true,
- DNSPort: 7001,
- DNSRecursorTimeout: 4427 * time.Second,
- DNSRecursors: []string{"63.38.39.58", "92.49.18.18"},
- DNSServiceTTL: map[string]time.Duration{"*": 32030 * time.Second},
- DNSUDPAnswerLimit: 29909,
- DNSNodeMetaTXT: true,
- DataDir: dataDir,
- Datacenter: "rzo029wg",
- DevMode: true,
- DisableAnonymousSignature: true,
- DisableCoordinates: true,
- DisableHostNodeID: true,
- DisableKeyringFile: true,
- DisableRemoteExec: true,
- DisableUpdateCheck: true,
- DiscardCheckOutput: true,
- DiscoveryMaxStale: 5 * time.Second,
- EnableACLReplication: true,
- EnableAgentTLSForChecks: true,
- EnableDebug: true,
- EnableScriptChecks: true,
- EnableSyslog: true,
- EnableUI: true,
- EncryptKey: "A4wELWqH",
- EncryptVerifyIncoming: true,
- EncryptVerifyOutgoing: true,
- HTTPAddrs: []net.Addr{tcpAddr("83.39.91.39:7999")},
- HTTPBlockEndpoints: []string{"RBvAFcGD", "fWOWFznh"},
- HTTPPort: 7999,
- HTTPResponseHeaders: map[string]string{"M6TKa9NP": "xjuxjOzQ", "JRCrHZed": "rl0mTx81"},
- HTTPSAddrs: []net.Addr{tcpAddr("95.17.17.19:15127")},
- HTTPSPort: 15127,
- KeyFile: "IEkkwgIA",
- LeaveDrainTime: 8265 * time.Second,
- LeaveOnTerm: true,
- LogLevel: "k1zo9Spt",
- NodeID: types.NodeID("AsUIlw99"),
- NodeMeta: map[string]string{"5mgGQMBk": "mJLtVMSG", "A7ynFMJB": "0Nx6RGab"},
- NodeName: "otlLxGaI",
- NonVotingServer: true,
- PidFile: "43xN80Km",
- RPCAdvertiseAddr: tcpAddr("17.99.29.16:3757"),
- RPCBindAddr: tcpAddr("16.99.34.17:3757"),
- RPCHoldTimeout: 15707 * time.Second,
- RPCProtocol: 30793,
- RPCRateLimit: 12029.43,
- RPCMaxBurst: 44848,
- RaftProtocol: 19016,
- RaftSnapshotThreshold: 16384,
- RaftSnapshotInterval: 30 * time.Second,
- ReconnectTimeoutLAN: 23739 * time.Second,
- ReconnectTimeoutWAN: 26694 * time.Second,
- RejoinAfterLeave: true,
- RetryJoinIntervalLAN: 8067 * time.Second,
- RetryJoinIntervalWAN: 28866 * time.Second,
- RetryJoinLAN: []string{"pbsSFY7U", "l0qLtWij"},
- RetryJoinMaxAttemptsLAN: 913,
- RetryJoinMaxAttemptsWAN: 23160,
- RetryJoinWAN: []string{"PFsR02Ye", "rJdQIhER"},
- SegmentName: "BC2NhTDi",
+ DNSAddrs: []net.Addr{tcpAddr("93.95.95.81:7001"), udpAddr("93.95.95.81:7001")},
+ DNSARecordLimit: 29907,
+ DNSAllowStale: true,
+ DNSDisableCompression: true,
+ DNSDomain: "7W1xXSqd",
+ DNSEnableTruncate: true,
+ DNSMaxStale: 29685 * time.Second,
+ DNSNodeTTL: 7084 * time.Second,
+ DNSOnlyPassing: true,
+ DNSPort: 7001,
+ DNSRecursorTimeout: 4427 * time.Second,
+ DNSRecursors: []string{"63.38.39.58", "92.49.18.18"},
+ DNSServiceTTL: map[string]time.Duration{"*": 32030 * time.Second},
+ DNSUDPAnswerLimit: 29909,
+ DNSNodeMetaTXT: true,
+ DataDir: dataDir,
+ Datacenter: "rzo029wg",
+ DevMode: true,
+ DisableAnonymousSignature: true,
+ DisableCoordinates: true,
+ DisableHostNodeID: true,
+ DisableHTTPUnprintableCharFilter: true,
+ DisableKeyringFile: true,
+ DisableRemoteExec: true,
+ DisableUpdateCheck: true,
+ DiscardCheckOutput: true,
+ DiscoveryMaxStale: 5 * time.Second,
+ EnableACLReplication: true,
+ EnableAgentTLSForChecks: true,
+ EnableDebug: true,
+ EnableScriptChecks: true,
+ EnableSyslog: true,
+ EnableUI: true,
+ EncryptKey: "A4wELWqH",
+ EncryptVerifyIncoming: true,
+ EncryptVerifyOutgoing: true,
+ HTTPAddrs: []net.Addr{tcpAddr("83.39.91.39:7999")},
+ HTTPBlockEndpoints: []string{"RBvAFcGD", "fWOWFznh"},
+ HTTPPort: 7999,
+ HTTPResponseHeaders: map[string]string{"M6TKa9NP": "xjuxjOzQ", "JRCrHZed": "rl0mTx81"},
+ HTTPSAddrs: []net.Addr{tcpAddr("95.17.17.19:15127")},
+ HTTPSPort: 15127,
+ KeyFile: "IEkkwgIA",
+ LeaveDrainTime: 8265 * time.Second,
+ LeaveOnTerm: true,
+ LogLevel: "k1zo9Spt",
+ NodeID: types.NodeID("AsUIlw99"),
+ NodeMeta: map[string]string{"5mgGQMBk": "mJLtVMSG", "A7ynFMJB": "0Nx6RGab"},
+ NodeName: "otlLxGaI",
+ NonVotingServer: true,
+ PidFile: "43xN80Km",
+ RPCAdvertiseAddr: tcpAddr("17.99.29.16:3757"),
+ RPCBindAddr: tcpAddr("16.99.34.17:3757"),
+ RPCHoldTimeout: 15707 * time.Second,
+ RPCProtocol: 30793,
+ RPCRateLimit: 12029.43,
+ RPCMaxBurst: 44848,
+ RaftProtocol: 19016,
+ RaftSnapshotThreshold: 16384,
+ RaftSnapshotInterval: 30 * time.Second,
+ ReconnectTimeoutLAN: 23739 * time.Second,
+ ReconnectTimeoutWAN: 26694 * time.Second,
+ RejoinAfterLeave: true,
+ RetryJoinIntervalLAN: 8067 * time.Second,
+ RetryJoinIntervalWAN: 28866 * time.Second,
+ RetryJoinLAN: []string{"pbsSFY7U", "l0qLtWij"},
+ RetryJoinMaxAttemptsLAN: 913,
+ RetryJoinMaxAttemptsWAN: 23160,
+ RetryJoinWAN: []string{"PFsR02Ye", "rJdQIhER"},
+ SegmentName: "BC2NhTDi",
Segments: []structs.NetworkSegment{
{
Name: "PExYMe2E",
@@ -4403,6 +4406,7 @@ func TestSanitize(t *testing.T) {
"DevMode": false,
"DisableAnonymousSignature": false,
"DisableCoordinates": false,
+ "DisableHTTPUnprintableCharFilter": false,
"DisableHostNodeID": false,
"DisableKeyringFile": false,
"DisableRemoteExec": false,
diff --git a/agent/http.go b/agent/http.go
index 1cce524af..1f8919539 100644
--- a/agent/http.go
+++ b/agent/http.go
@@ -187,10 +187,15 @@ func (s *HTTPServer) handler(enableDebug bool) http.Handler {
}
// Wrap the whole mux with a handler that bans URLs with non-printable
- // characters.
+ // characters, unless disabled explicitly to deal with old keys that fail this
+ // check.
+ h := cleanhttp.PrintablePathCheckHandler(mux, nil)
+ if s.agent.config.DisableHTTPUnprintableCharFilter {
+ h = mux
+ }
return &wrappedMux{
mux: mux,
- handler: cleanhttp.PrintablePathCheckHandler(mux, nil),
+ handler: h,
}
}
diff --git a/agent/http_test.go b/agent/http_test.go
index 42e0eec55..ffe560d12 100644
--- a/agent/http_test.go
+++ b/agent/http_test.go
@@ -327,6 +327,19 @@ func TestHTTPAPI_Ban_Nonprintable_Characters(t *testing.T) {
}
}
+func TestHTTPAPI_Allow_Nonprintable_Characters_With_Flag(t *testing.T) {
+ a := NewTestAgent(t.Name(), "disable_http_unprintable_char_filter = true")
+ defer a.Shutdown()
+
+ req, _ := http.NewRequest("GET", "/v1/kv/bad\x00ness", nil)
+ resp := httptest.NewRecorder()
+ a.srv.Handler.ServeHTTP(resp, req)
+ // Key doesn't actually exist so we should get 404
+ if got, want := resp.Code, http.StatusNotFound; got != want {
+ t.Fatalf("bad response code got %d want %d", got, want)
+ }
+}
+
func TestHTTPAPI_TranslateAddrHeader(t *testing.T) {
t.Parallel()
// Header should not be present if address translation is off.
diff --git a/website/source/docs/agent/options.html.md b/website/source/docs/agent/options.html.md
index 8d7894d3c..7aa463496 100644
--- a/website/source/docs/agent/options.html.md
+++ b/website/source/docs/agent/options.html.md
@@ -749,6 +749,17 @@ Consul will not enable TLS for the HTTP API unless the `https` port has been ass
* `disable_host_node_id`
Equivalent to the [`-disable-host-node-id` command-line flag](#_disable_host_node_id).
+* `disable_http_unprintable_char_filter`
+ Defaults to false. Consul 1.0.3 fixed a potential security vulnerability where
+ malicious users could craft KV keys with unprintable chars that would confuse
+ operators using the CLI or UI into taking wrong actions. Users who had data
+ written in older versions of Consul that did not have this restriction will be
+ unable to delete those values by default in 1.0.3 or later. This setting
+ enables those users to _temporarily_ disable the filter such that delete
+ operations can work on those keys again to get back to a healthy state. It is
+ strongly recommended that this filter is not disabled permanently as it
+ exposes the original security vulnerability.
+
* `disable_remote_exec`
Disables support for remote execution. When set to true, the agent will ignore any incoming
remote exec requests. In versions of Consul prior to 0.8, this defaulted to false. In Consul