.sock 的魔力:为何现代基础设施依赖 Unix Domain Sockets

发布: (2026年1月15日 GMT+8 22:05)
7 min read
原文: Dev.to

Source: Dev.to

/var/run/docker.sock – 用于控制 Docker。
agent.sock – 被 SPIRE 用来对工作负载进行身份验证。

这不仅仅是“因为它快”。还有一个决定性的安全原因:由操作系统内核提供的绝对身份保证

在本文中我们将:

  1. 解释套接字的真实本质以及它们与 TCP 的关键区别。
  2. 用 Go 进行实验,直接从内核中提取 连接对等方的身份(PID/UID)

套接字 vs. 文件

很多人被教导说“套接字被当作文件来处理”。当你用 ls -l 列出它们时,它们确实看起来像文件:

srw-rw---- 1 root docker 0 Jan 1 12:00 /var/run/docker.sock

文件大小始终为 0,因为“数据”并未存储在磁盘上;它仅仅是内核内存中通信端点(一个窗口)的地址簿条目。

Data‑flow Comparison

FeatureTCP (INET)UNIX Domain Socket (UDS)
AddressingIP:Port (e.g., 127.0.0.1:8080)文件路径 (e.g., /tmp/app.sock)
Scope跨网络(远程)仅同一主机(本地)
Overhead高 – 协议头、校验和、路由表查找极低 – 仅内存拷贝
Access control防火墙(iptables)、TLS文件权限(chmod/chown
Identity源 IP(可伪造)PID/UID/GID(内核保证)
  • TCP – 即使是本地通信,数据也要经过完整的网络栈(数据包封装、校验和计算、路由)。
  • UDS – 完全绕过网络栈;通信通过在内核内部拷贝缓冲区实现,因而 延迟极低

在 TCP 中可以看到源 IP 地址,但 没有可靠的方法 知道是哪一个进程发起的连接(IP 可以被伪造)。

使用 UDS 时,服务器可以通过 SO_PEERCRED 套接字选项让内核 返回对等方的凭证。由于这些信息直接来源于内核内部表,客户端无法伪造。这也是 Zero‑Trust 系统(如 SPIRE)采用 UDS 的原因。

⚠️ Note: SO_PEERCRED 是 Linux 专有特性。它 不适用于 macOS 或 Windows。

演示:使用 Go 提取 PID/UID

下面是一个最小的服务器示例,它会打印对等方的 PID、UID 和 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"))
}

运行演示(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'

预期输出(服务器端)

🕵️  Server is listening on /tmp/test.sock
waiting for connection...

[🚨 DETECTED]
 - Connected by PID : 757
 - User ID (UID)    : 0
 - Group ID (GID)   : 0

显示的 PID(示例中的 757)与执行连接的 nc 进程相匹配——这证明了内核能够保证对等方的身份。

Real‑World Use: SPIRE & Docker

  • 访问 docker.sock 实际上相当于 root 级别,因为 Docker 守护进程是以 root 身份运行的。
  • SPIRE 的 agent 在其 Unix 套接字上使用 SO_PEERCRED 来验证请求确实来自预期的工作负载(例如 Pod)。

SPIRE 做的不仅仅是读取 PID:

  1. 调用 watcher.IsAlive() 来防御 PID 重用攻击(进程死亡后,其 PID 被重新分配,攻击者尝试冒充)。
  2. 将 PID 传递给 Workload Attestor 插件(Docker、Kubernetes 等)。这些插件会把 PID 转换为更丰富的属性,如容器 ID、Pod 标签等。

在 Windows 上,SPIRE 使用 命名管道 并采用类似的机制:即使底层操作系统不同,内核仍然保证客户端的身份。

要点

  • .sock(Unix 域套接字)不是“旧技术”——它提供高性能 以及 内核验证的身份
  • SO_PEERCRED 为你提供一个可信的 PID/UID/GID 对,客户端无法伪造。
  • 这种能力是云原生环境中许多 Zero‑Trust(零信任)设计的基石(Docker、SPIRE 等)。

随意尝试上面的代码,并请记住,安全保证仅在支持 SO_PEERCRED 的 Linux 上有效。祝玩得开心!

性能

绕过网络栈。

最强安全

在内核层面的身份保证(Peer Cred)。

简单访问控制

利用文件系统权限。

结合这些特性,UDS 仍然是现代容器基础设施中支持 “last one mile of host communication” 的关键组件,在该环境中需要零信任安全。

Back to Blog

相关文章

阅读更多 »

Rapg:基于 TUI 的密钥管理器

我们都有这种经历。你加入一个新项目,首先听到的就是:“在 Slack 的置顶消息里查找 .env 文件”。或者你有多个 .env …

技术是赋能者,而非救世主

为什么思考的清晰度比你使用的工具更重要。Technology 常被视为一种魔法开关——只要打开,它就能让一切改善。新的 software,...

踏入 agentic coding

使用 Copilot Agent 的经验 我主要使用 GitHub Copilot 进行 inline edits 和 PR reviews,让我的大脑完成大部分思考。最近我决定 t...