GitHub
07/06/2026, 8:28 AMosctrl-tls can dump every incoming HTTP request to a debug log (--enable-http-debug /
HTTP_DEBUG), but the toggle is all-or-nothing: either every request from every node is
dumped, or none are. On a busy server this is a firehose — the operator cannot isolate the
traffic of a single host, which is the only case where request-level debugging is useful.
Change
Add an optional host filter to the existing HTTP debug feature. The filter is the osquery
node UUID (case-insensitive), configured via a new hostIdentifier field on the debug
config block, or --http-debug-host / HTTP_DEBUG_HOST.
• debug.hostIdentifier empty (default) → behavior is identical to today: every request is
dumped when enableHttp is true. No semantic change for existing deployments.
• debug.hostIdentifier set → only requests whose node UUID matches are dumped. The expensive
dump work (httputil.DumpRequest + lumberjack write) runs only for the matching host.
The matching is keyed on the canonical node UUID:
• enroll → the request's host_identifier (which becomes the node UUID, uppercased).
• config, log, queryRead, queryWrite, carveInit → node.UUID from the GetByKey
lookup the handler already performs.
• Pre-enroll / no-node endpoints (flags, cert, verify, script, quick enroll/remove,
enroll package, osquery-config-endpoint) and carveBlock are skipped in filtered mode,
since they carry no node UUID to match against. They still dump in legacy (no-filter) mode.
Performance
The filter is designed so that the non-matching path (the overwhelming majority of traffic on a
busy server) adds only a single string comparison on top of the JSON parse and node lookup
the handler already does — no body re-read, no extra DB/Redis lookup, no dump formatting, no
file write. This removes the dominant cost of the current debug mode (httputil.DumpRequest
re-reading and formatting every request body) for all non-matching traffic. The matching path
pays the dump cost, but only for one host, so the debug file stays readable.
The filtered dump uses a new DebugHTTPDumpWithBody helper that re-wraps the already-buffered
request body bytes, so the existing httputil-based formatter serializes the request without
re-reading the stream.
Behavior changes
• No filter → byte-for-byte identical to the previous implementation (the legacy "dump
everything" path runs at the top of each handler, including malformed/unparseable bodies).
• Filter set → malformed bodies (failed json.Unmarshal) and invalid node_key requests
are no longer dumped, since the node UUID cannot be determined. This is intentional: the goal
is to see traffic from one specific host, not anonymous/malformed traffic.
Files
• pkg/config/types.go, pkg/config/flags.go — new TargetHostIdentifier field + flag/env.
• pkg/utils/http-utils.go — DebugHTTPDumpWithBody helper.
• cmd/tls/handlers/handlers.go — shouldDebugHTTP / debugHTTPAll gate helpers.
• cmd/tls/handlers/post.go — gate top-of-handler dumps and add filtered dumps on the six
node-bearing endpoints.
• deploy/config/tls.yml — document the new hostIdentifier field.
• Tests: cmd/tls/handlers/debug_http_filter_test.go, pkg/utils/http-utils_test.go.
Validation
• go build ./... — passes.
• go vet ./cmd/tls/handlers/... — clean.
• go test ./cmd/tls/handlers/ — passes, including new TestShouldDebugHTTP* (disabled,
no-filter/legacy, filtered match/mismatch/unknown-node, nil-config) and
TestDebugHTTPDumpWithBody (show-body / omit-body).
• The pre-existing TestSendRequest in pkg/utils fails in the sandbox because it binds a
network port (disallowed here); unrelated to this change.
Usage
debug:
enableHttp: true
httpFile: ./debug-http-tls.log
showBody: false
hostIdentifier: "DEADBEEF-1234-ABCD" # only dump this node's traffic
or
HTTP_DEBUG=true HTTP_DEBUG_HOST=deadbeef-1234-abcd osctrl-tls ...
Follow-ups (not in this PR)
• carveBlock is excluded from the filtered path (it carries session_id, not node_key).
Wiring it in requires a NodeManager.GetByID to resolve carve.NodeID → UUID.
• The filter is currently startup-only (flag/YAML). A runtime-toggleable version could reuse the
settings-refresh goroutine in cmd/tls/main.go via a settings.DebugHostFilter entry.
jmpsec/osctrlGitHub
07/06/2026, 8:27 PM