The Magic of .sock: Why Modern Infrastructure Relies on Unix Domain Sockets
Source: Dev.to
/var/run/docker.sock – used for controlling Docker.
agent.sock – used by SPIRE to authenticate workloads.
It isn’t just “because it’s fast.” There is a decisive security reason: absolute identity assurance provided by the OS kernel.
In this article we will:
- Explain the true nature of sockets and the critical differences from TCP.
- Conduct an experiment in Go that extracts the identity of the connection peer (PID/UID) directly from the kernel.
Sockets vs. Files
Many people are taught that “sockets are handled as files.” When you list them with ls -l they indeed appear as files:
srw-rw---- 1 root docker 0 Jan 1 12:00 /var/run/docker.sock
The file size is always 0 because the “data” isn’t stored on disk; it is merely an address‑book entry for a communication endpoint (a window) in kernel memory.
Data‑flow Comparison
| Feature | TCP (INET) | UNIX Domain Socket (UDS) |
|---|---|---|
| Addressing | IP:Port (e.g., 127.0.0.1:8080) | File path (e.g., /tmp/app.sock) |
| Scope | Over network (remote) | Same host only (local) |
| Overhead | High – protocol headers, checksum, routing table look‑ups | Minimal – memory copy only |
| Access control | Firewall (iptables), TLS | File permissions (chmod/chown) |
| Identity | Source IP (spoofable) | PID/UID/GID (guaranteed by kernel) |
- TCP – Even for local communication the data traverses the full network stack (packet encapsulation, checksum calculation, routing).
- UDS – Bypasses the network stack entirely; communication is performed by copying buffers inside the kernel, giving overwhelmingly low latency.
In TCP you can see the source IP address, but there is no reliable way to know which process initiated the connection (IP can be spoofed).
With UDS the server can ask the kernel to reveal the credentials of the peer using the SO_PEERCRED socket option. Because this information comes straight from the kernel’s internal tables, the client cannot spoof it. This is why Zero‑Trust systems such as SPIRE adopt UDS.
⚠️ Note:
SO_PEERCREDis a Linux‑specific feature. It does not work on macOS or Windows.
Demo: Extracting PID/UID with Go
Below is a minimal server that prints the peer’s PID, UID, and GID.
// main.go
package main
import (
"fmt"
"net"
"os"
"syscall"
)
func main() {
const socketPath = "/tmp/test.sock"
// Remove any stale socket file.
_ = os.Remove(socketPath)
// Start a UDS listener.
l, err := net.Listen("unix", socketPath)
if err != nil {
panic(err)
}
defer l.Close()
// Make the socket world‑writable for the experiment.
_ = os.Chmod(socketPath, 0o777)
fmt.Println("🕵️ Server is listening on", socketPath)
fmt.Println("waiting for connection...")
for {
conn, err := l.Accept()
if err != nil {
fmt.Println("accept error:", err)
continue
}
go handleConnection(conn)
}
}
func handleConnection(c net.Conn) {
defer c.Close()
// Convert net.Conn to *net.UnixConn to obtain the underlying file descriptor.
unixConn, ok := c.(*net.UnixConn)
if !ok {
fmt.Println("Not a unix connection")
return
}
file, err := unixConn.File()
if err != nil {
fmt.Println("Failed to get file descriptor:", err)
return
}
defer file.Close()
fd := int(file.Fd())
// Query the kernel for peer credentials.
ucred, err := syscall.GetsockoptUcred(fd, syscall.SOL_SOCKET, syscall.SO_PEERCRED)
if err != nil {
fmt.Println("Failed to get credentials:", err)
return
}
// Display the results.
fmt.Printf("\n[🚨 DETECTED]\n")
fmt.Printf(" - Connected by PID : %d\n", ucred.Pid)
fmt.Printf(" - User ID (UID) : %d\n", ucred.Uid)
fmt.Printf(" - Group ID (GID) : %d\n", ucred.Gid)
_, _ = c.Write([]byte("Identity Verified. closing.\n"))
}
Running the Demo (Linux / Docker)
# 1️⃣ Save the code above as main.go
# 2️⃣ Start a Linux container with Go installed.
docker run -it --rm -v "$PWD":/app -w /app golang:1.25 bash
# Inside the container:
# ── Start the server in the background
go run main.go &
# ── Connect with netcat (nc)
echo | sh -c 'echo "Client PID: $$"; exec nc -U /tmp/test.sock'
Expected output (server side)
🕵️ Server is listening on /tmp/test.sock
waiting for connection...
[🚨 DETECTED]
- Connected by PID : 757
- User ID (UID) : 0
- Group ID (GID) : 0
The PID shown (757 in the example) matches the nc process that performed the connection – proof that the kernel guarantees the identity.
Real‑World Use: SPIRE & Docker
- Access to
docker.sockis effectively root‑level because the Docker daemon runs as root. - SPIRE’s agent uses
SO_PEERCREDon its Unix socket to verify that a request truly originates from the expected workload (e.g., a Pod).
SPIRE does more than just read the PID:
- Calls
watcher.IsAlive()to guard against PID‑reuse attacks (a process dies, its PID is reassigned, and the attacker tries to masquerade). - Passes the PID to Workload Attestor plugins (Docker, Kubernetes, etc.). Those plugins translate the PID into richer attributes such as container IDs, pod labels, etc.
On Windows, SPIRE uses named pipes with an analogous mechanism: the kernel still guarantees the client’s identity, even though the underlying OS differs.
Take‑aways
.sock(Unix Domain Socket) is not an “old technology” – it provides high performance and kernel‑verified identity.SO_PEERCREDgives you a trustworthy PID/UID/GID pair that cannot be forged by the client.- This capability is the cornerstone of many Zero‑Trust designs in cloud‑native environments (Docker, SPIRE, etc.).
Feel free to experiment with the code above, and remember that the security guarantees only hold on Linux where SO_PEERCRED is available. Happy hacking!
Performance
Bypassing the network stack.
Strongest Security
ID assurance at the kernel level (Peer Cred).
Simple Access Control
Utilization of file system permissions.
Combining these features, UDS continues to be a critical component supporting the “last one mile of host communication” in modern container infrastructure where Zero Trust security is required.