Skip to content

Commit

Permalink
Add config option to disable HTTP printable char path check (#4442)
Browse files Browse the repository at this point in the history
  • Loading branch information
banks authored Jul 26, 2018
1 parent 859ac35 commit 8dd50d5
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 171 deletions.
1 change: 1 addition & 0 deletions agent/config/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,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),
Expand Down
201 changes: 101 additions & 100 deletions agent/config/config.go

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions agent/config/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
142 changes: 73 additions & 69 deletions agent/config/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2622,6 +2622,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,
Expand Down Expand Up @@ -3096,6 +3097,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
Expand Down Expand Up @@ -3701,75 +3703,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",
Expand Down Expand Up @@ -4436,6 +4439,7 @@ func TestSanitize(t *testing.T) {
"DevMode": false,
"DisableAnonymousSignature": false,
"DisableCoordinates": false,
"DisableHTTPUnprintableCharFilter": false,
"DisableHostNodeID": false,
"DisableKeyringFile": false,
"DisableRemoteExec": false,
Expand Down
9 changes: 7 additions & 2 deletions agent/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}

Expand Down
13 changes: 13 additions & 0 deletions agent/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
11 changes: 11 additions & 0 deletions website/source/docs/agent/options.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,17 @@ Consul will not enable TLS for the HTTP API unless the `https` port has been ass
* <a name="disable_host_node_id"></a><a href="#disable_host_node_id">`disable_host_node_id`</a>
Equivalent to the [`-disable-host-node-id` command-line flag](#_disable_host_node_id).
* <a name="disable_http_unprintable_char_filter"></a><a href="#disable_http_unprintable_char_filter">`disable_http_unprintable_char_filter`</a>
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.
* <a name="disable_remote_exec"></a><a href="#disable_remote_exec">`disable_remote_exec`</a>
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
Expand Down

0 comments on commit 8dd50d5

Please sign in to comment.