fix(security): decide daemon auth locality from the accepting socket, not a spoofable REMOTE_ADDR header (1.1.23)#226
Merged
Conversation
… not a spoofable REMOTE_ADDR header (1.1.23)
crated grants full trust (bypassing bearer token AND pool ACL) to
"Unix-socket peers" and detected them via
req.get_header_value("REMOTE_ADDR").empty(). cpp-httplib stores request
headers in a multimap and get_header_value returns the first match, so a
remote TCP-listener client sending its own empty REMOTE_ADDR: header
could shadow the server value and be mistaken for a local socket peer —
a potential complete token-layer bypass (incl. privops uid=0) when the
TCP listener is enabled. The same header keyed the rate-limit bucket.
The daemon already runs two separate httplib::Server instances (TCP and
UDS). registerRoutes now takes an isUnixListener flag and installs a
pre-routing handler that erases any client-supplied X-Crated-Listener
header and stamps the authoritative value (unix/tcp) from the accepting
server instance. isUnixSocketPeer reads that marker instead of
REMOTE_ADDR; getClientId keys on the marker plus req.remote_addr (filled
by httplib from the accepted socket, not a header). A missing marker
reads as untrusted TCP — fail-closed.
Structural change on the httplib-coupled auth path (no pure unit
surface); compile-gated by the FreeBSD build, warrants a runtime check
against a live TCP+UDS daemon. Bumps to 1.1.23; CHANGELOG + trust-model
(en/uk) updated; TODO item marked fixed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X6t6tzVypHye5bDGLxzmZK
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The highest-severity item from the second-pass audit.
cratedgrants full trust — bypassing both the bearer token and the pool ACL — to "Unix-socket peers", and detected them with:req.get_header_value("REMOTE_ADDR").empty()cpp-httplib stores request headers in a multimap and
get_header_valuereturns the first match, so a remote client on the TCP listener that sends its own emptyREMOTE_ADDR:header could shadow the server-set value and be mistaken for a local socket peer — a potential complete bypass of the token layer (includingPOST /api/v1/privops/:verb, uid=0) wheneverlisten.tcp_portis enabled. The same header also keyed the rate-limit bucket, letting a client rotate its own key.Fix
The daemon already runs two separate
httplib::Serverinstances — one on TCP, one on the Unix socket. So trust locality is knowable from which instance accepted the connection, with no dependency on any client-influenced value:registerRoutes(srv, config, isUnixListener)now installs a pre-routing handler that, on every request, erases any client-suppliedX-Crated-Listenerheader and stamps the authoritative value (unix/tcp) from the accepting server instance.isUnixSocketPeerreads that marker instead ofREMOTE_ADDR.getClientIdkeys the rate limiter on the marker plusreq.remote_addr(the address httplib fills from the accepted socket — not a header).server.cpppassesfalsefor the TCP registration,truefor the UDS one.Because the pre-routing handler erases client copies before stamping, a TCP client sending
X-Crated-Listener: unixis overwritten totcp— the marker cannot be forged.Testing / caveats
This is a structural change on the httplib-coupled auth path — there is no pure unit surface to assert against (the token logic in
auth_pureis already covered separately). It is compile-gated by the FreeBSD build (which linkscrated), and I recommend a runtime check against a live TCP+UDS daemon to confirm the pre-routing handler fires on every path before relying on it in an internet-exposed deployment. Normal UDS-only deployments are behaviorally unchanged (UDS →unix→ trusted).Version
Bumps to 1.1.23;
CHANGELOG.md+docs/trust-model.{md,uk.md}updated; the correspondingTODOitem is marked fixed (the lease-race and socket_proxy items from the same audit remain deferred).🤖 Generated with Claude Code
Generated by Claude Code