.sock의 마법: 현대 인프라가 Unix Domain Sockets에 의존하는 이유

발행: (2026년 1월 15일 오후 11:05 GMT+9)
8 min read
원문: Dev.to

Source: Dev.to

/var/run/docker.sock – Docker 제어에 사용됩니다.
agent.sock – SPIRE가 워크로드를 인증하는 데 사용됩니다.

단순히 “빠르기 때문”만은 아닙니다. 결정적인 보안 이유가 있습니다: OS 커널이 제공하는 절대적인 신원 보증.

이 글에서는:

  1. 소켓의 진정한 본질과 TCP와의 중요한 차이점을 설명합니다.
  2. Go에서 커널로부터 직접 **연결 피어의 신원(PID/UID)**을 추출하는 실험을 수행합니다.

소켓 vs. 파일

많은 사람들에게 “소켓은 파일처럼 취급된다”고 가르칩니다. ls -l 로 나열하면 실제로 파일처럼 보입니다:

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

파일 크기가 항상 0인 이유는 “데이터”가 디스크에 저장되는 것이 아니라, 커널 메모리 내의 통신 엔드포인트(윈도우)를 가리키는 주소록 항목에 불과하기 때문입니다.

데이터 흐름 비교

기능TCP (INET)UNIX 도메인 소켓 (UDS)
주소 지정IP:Port (예: 127.0.0.1:8080)파일 경로 (예: /tmp/app.sock)
범위네트워크 전체 (원격)동일 호스트만 (로컬)
오버헤드높음 – 프로토콜 헤더, 체크섬, 라우팅 테이블 조회최소 – 메모리 복사만
접근 제어방화벽 (iptables), TLS파일 권한 (chmod/chown)
식별자소스 IP (스푸핑 가능)PID/UID/GID (커널이 보장)
  • TCP – 로컬 통신이라 할지라도 데이터가 전체 네트워크 스택을 통과합니다 (패킷 캡슐화, 체크섬 계산, 라우팅).
  • UDS – 네트워크 스택을 완전히 우회하며, 커널 내부에서 버퍼를 복사하여 압도적으로 낮은 지연 시간을 제공합니다.

TCP에서는 소스 IP 주소를 확인할 수 있지만, 연결을 시작한 프로세스를 신뢰할 수 있게 알 방법은 없습니다 (IP는 스푸핑될 수 있음).

UDS에서는 서버가 SO_PEERCRED 소켓 옵션을 사용해 피어의 자격 증명을 커널에 요청할 수 있습니다. 이 정보는 커널 내부 테이블에서 직접 가져오므로 클라이언트가 이를 위조할 수 없습니다. 이러한 이유로 SPIRE와 같은 제로 트러스트 시스템이 UDS를 채택합니다.

⚠️ 참고: 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 프로세스와 일치합니다 — 커널이 신원을 보장한다는 증거입니다.

실제 사용 사례: SPIRE & Docker

  • docker.sock에 대한 접근은 Docker 데몬이 루트 권한으로 실행되기 때문에 사실상 루트 수준입니다.
  • SPIRE의 agent는 Unix 소켓에서 SO_PEERCRED를 사용하여 요청이 실제로 예상된 워크로드(예: Pod)에서 온 것인지 확인합니다.

SPIRE는 PID를 단순히 읽는 것 이상의 작업을 수행합니다:

  1. watcher.IsAlive()를 호출하여 PID 재사용 공격을 방지합니다(프로세스가 종료되고 PID가 재할당된 뒤 공격자가 가장하려는 경우).
  2. PID를 Workload Attestor 플러그인(Docker, Kubernetes 등)에 전달합니다. 해당 플러그인들은 PID를 컨테이너 ID, pod 라벨 등과 같은 더 풍부한 속성으로 변환합니다.

Windows에서는 SPIRE가 named pipe를 사용하여 유사한 메커니즘을 구현합니다. 기본 OS가 다르더라도 커널은 클라이언트의 신원을 보장합니다.

요점

  • .sock (Unix Domain Socket)은 아니라 “구식 기술” – 고성능 그리고 커널‑검증 신원을 제공합니다.
  • SO_PEERCRED는 클라이언트가 위조할 수 없는 신뢰할 수 있는 PID/UID/GID 쌍을 제공합니다.
  • 이 기능은 클라우드‑네이티브 환경(Docker, SPIRE 등)에서 많은 Zero‑Trust 설계의 핵심입니다.

위 코드를 자유롭게 실험해 보세요. 보안 보장은 SO_PEERCRED가 지원되는 Linux에서만 적용된다는 점을 기억하십시오. 즐거운 해킹 되세요!

성능

네트워크 스택을 우회합니다.

가장 강력한 보안

커널 수준에서의 ID 보증 (Peer Cred).

간단한 접근 제어

파일 시스템 권한의 활용.

이러한 기능들을 결합하여, UDS는 제로 트러스트 보안이 요구되는 현대 컨테이너 인프라에서 **“last one mile of host communication”**을 지원하는 중요한 구성 요소로 계속해서 역할을 수행합니다.

Back to Blog

관련 글

더 보기 »

기술은 구원자가 아니라 촉진자다

왜 사고의 명확성이 사용하는 도구보다 더 중요한가? Technology는 종종 마법 스위치처럼 취급된다—켜기만 하면 모든 것이 개선된다. 새로운 software, ...

에이전틱 코딩에 입문하기

Copilot Agent와의 경험 나는 주로 GitHub Copilot을 사용해 인라인 편집과 PR 리뷰를 수행했으며, 대부분의 사고는 내 머리로 했습니다. 최근 나는 t...