Unix Socket Peer Credentials Bind Identity to a Local Connection

A privileged local service often accepts requests from processes that share the same host but do not share the same authority. A pathname on a Unix domain socket can control who reaches the listener, yet a successful connection does not by itself tell the service which process is on the other end. Linux provides a second boundary: SO_PEERCRED lets a connected Unix socket expose peer credentials supplied by the kernel.

Those credentials are useful because the peer does not serialize them into an application message. The kernel derives the credential record for the connection, so a client cannot replace the reported UID with an arbitrary number by changing request bytes. That property makes peer credentials suitable as an input to local authorization, but not as a complete authorization system.

The credential record belongs to the socket relationship

On Linux, getsockopt() with SOL_SOCKET and SO_PEERCRED returns a struct ucred containing a PID, UID, and GID for the peer of a connected AF_UNIX socket. The interface also applies to a socket pair.

The important boundary is that these values come from socket state maintained by the kernel. They are not equivalent to a client sending fields such as uid=1000 in a protocol frame. Application data remains attacker-controlled; peer credentials are separate metadata obtained through the socket API.

For a service that needs to distinguish local callers, this changes the trust model:

request bytes --------------------> parse as untrusted input
connected Unix socket
        |
        +---- SO_PEERCRED ---------> kernel-supplied peer identity

The service can bind a request to the identity associated with the connection instead of trusting an identity claim inside that request.

Credential timing creates a snapshot, not a live identity feed

Linux documents the returned credentials as those in effect when the peer connected, when listen() was called for a listening socket, or when a socket pair was created. That timing matters for long-lived connections.

A process can change parts of its credential state after a connection exists, subject to normal kernel permission rules. SO_PEERCRED is not a subscription to every later identity transition. An authorization design that requires current process state cannot assume that repeatedly reading the option converts the original connection credential into a live process monitor.

This snapshot property can be desirable. A daemon may intentionally authorize the principal that established a channel and keep that decision stable for the channel lifetime. It can also be too broad if privileges, roles, or application membership are expected to change while the connection remains open. In that case, connection identity and current authorization state need separate handling.

A PID is useful context but a fragile long-term principal

The PID field can identify the peer process in the relevant PID namespace view, but numeric PIDs are reusable. Once a process exits, the same number can later refer to a different process.

That makes the PID poor material for durable authorization records. A service should not store a numeric PID and later treat any process with that number as the original peer. If a design needs a stable kernel reference to a process beyond the immediate credential check, Linux pidfds provide a different mechanism with lifecycle semantics tied to a process object.

UID and GID also require policy interpretation. They identify kernel credentials, not application accounts, tenant IDs, organization roles, or business permissions. Mapping a local UID to application authority is deployment policy and can be valid in one service model while being incorrect in another.

User namespaces change the meaning of numeric identity

Linux user namespaces permit credential IDs to be mapped between namespace views. As a result, a numeric UID has meaning relative to a namespace context rather than being a universal host identity label.

A service using peer credentials across container or namespace boundaries has to account for the namespace arrangement in which the socket and processes operate. Treating a displayed integer as globally self-explanatory can collapse distinct identity domains into one policy decision.

This limitation is architectural rather than a defect in SO_PEERCRED. The kernel supplies credentials according to Linux namespace semantics. The application still decides whether those credentials correspond to an allowed principal in its deployment model.

For host services exposed into containers, this point is especially important. Socket reachability, user-namespace mapping, filesystem permissions on the socket node, and the service’s authorization table form one combined boundary. Peer credentials contribute trustworthy kernel metadata, but they do not define the policy that consumes it.

Socket pathname permissions and peer identity solve different problems

Filesystem permissions on a pathname-based Unix socket can restrict which processes can connect. Peer credentials answer a different question after a connection exists.

Relying only on the pathname can be insufficient when several principals are intentionally allowed to reach one endpoint but receive different authority. Conversely, checking peer credentials does not make careless socket exposure harmless. Reducing reachability limits load and attack surface before protocol parsing begins.

Abstract Unix sockets on Linux do not use a filesystem pathname for access control in the same manner as pathname sockets. Deployments using them therefore cannot assume that directory and socket-node permissions provide the same admission boundary.

A robust local service can combine layers: constrain endpoint reachability where possible, retrieve kernel peer credentials on accepted connections, map those credentials into explicit service policy, and continue to validate all request content independently.

Credential checks do not authenticate delegated intent

A process with an allowed UID may itself be a broker, desktop agent, job runner, or other intermediary acting for less privileged callers. SO_PEERCRED identifies the process at the socket boundary; it does not reveal the full delegation chain that caused that process to send a request.

This distinction becomes critical when a trusted local component accepts untrusted input and forwards privileged operations. The receiving daemon sees the trusted intermediary as its peer. If policy needs the original caller, the protocol requires a delegation mechanism with its own integrity and authorization rules. Kernel peer credentials cannot reconstruct provenance that lies beyond the direct socket peer.

The same limit applies to compromised processes. A credential check can establish that the request arrived over a connection attributed to a permitted UID. It cannot establish that the process is behaving according to intended application logic.

Message credentials are a separate mechanism

Linux also supports credential ancillary data through SCM_CREDENTIALS when the relevant socket options and message flow are used. That mechanism attaches credentials to messages and has rules distinct from SO_PEERCRED.

The two interfaces should not be treated as interchangeable names for one feature. SO_PEERCRED is queried from a connected socket relationship. Credential control messages participate in message delivery and are useful when per-message provenance matters, including datagram-oriented designs.

Choosing between them follows the protocol boundary. A stream service that authorizes a peer at connection establishment may fit SO_PEERCRED. A design that needs credentials associated with individual messages may require ancillary credentials and careful handling of their documented semantics.

Local identity still needs explicit authorization

Kernel-supplied peer credentials remove one dangerous ambiguity: the service does not need to trust a client-provided UID claim for the direct Unix-socket peer. The remaining decisions stay above that mechanism.

The daemon still needs a policy for allowed UIDs or GIDs, namespace mappings, connection lifetime, delegated callers, and operations permitted to each principal. It also needs normal protocol defenses for lengths, object identifiers, state transitions, and resource consumption.

SO_PEERCRED is therefore a narrow identity primitive with a useful trust property. It binds kernel credential metadata to a local socket relationship. Security comes from keeping that property narrow: use it to identify the direct peer at the boundary where the kernel can attest that fact, and avoid extending it into claims about application roles, current process state, or delegated intent that the interface does not supply.