avatar🌌
TangentTangent Blog

技术博客 | Go & Kubernetes & Linux

技术博客 | Go & Kubernetes & Linux

runc 源码解读(八):OCI Spec 核心数据结构解析 (中) —— Linux 平台配置

前言 ​

在上一篇文章中,我们解析了 OCI 规范中容器的顶层架构 Spec 和进程环境 Process。然而,真正让 Linux 容器具备隔离魔法的,是封装在 Spec 中的 Linux 字段。

这个 Linux 结构体可以说是整个 config.go 中最核心、最具技术深度的部分,它将 Linux 内核提供的底层特性(Namespaces, Cgroups, Seccomp, Sysctls 等)完美地映射为了结构化的 JSON 数据。本文将重点拆解这一部分,看看容器是如何与 Linux 内核特性对接的。

核心结构:Linux ​

在 vendor/github.com/opencontainers/runtime-spec/specs-go/config.go 中,Linux 结构体的定义如下(为了便于理解,对其进行了分类和略微精简):

go
type Linux struct {
	// --- 1. 命名空间与身份映射 ---
	Namespaces  []LinuxNamespace            `json:"namespaces,omitempty"`
	UIDMappings []LinuxIDMapping            `json:"uidMappings,omitempty"`
	GIDMappings []LinuxIDMapping            `json:"gidMappings,omitempty"`
	TimeOffsets map[string]LinuxTimeOffset  `json:"timeOffsets,omitempty"`

	// --- 2. 资源控制 ---
	CgroupsPath  string              `json:"cgroupsPath,omitempty"`
	Resources    *LinuxResources     `json:"resources,omitempty"`
	IntelRdt     *LinuxIntelRdt      `json:"intelRdt,omitempty"`
	MemoryPolicy *LinuxMemoryPolicy  `json:"memoryPolicy,omitempty"`

	// --- 3. 安全与系统调用 ---
	Seccomp     *LinuxSeccomp     `json:"seccomp,omitempty"`
	Personality *LinuxPersonality `json:"personality,omitempty"`

	// --- 4. 文件系统与设备 ---
	RootfsPropagation string                      `json:"rootfsPropagation,omitempty"`
	MaskedPaths       []string                    `json:"maskedPaths,omitempty"`
	ReadonlyPaths     []string                    `json:"readonlyPaths,omitempty"`
	Devices           []LinuxDevice               `json:"devices,omitempty"`
	NetDevices        map[string]LinuxNetDevice   `json:"netDevices,omitempty"`

	// --- 5. 内核参数调优 ---
	Sysctl map[string]string `json:"sysctl,omitempty"`
}

接下来,我们按分类对其中的关键字段进行深度剖析。

1. 命名空间与身份映射 ​

这是容器实现“视图隔离”的根基。

Namespaces ​

go
type LinuxNamespace struct {
	Type LinuxNamespaceType `json:"type"`
	Path string             `json:"path,omitempty"`
}

Namespaces 是一个数组,定义了容器需要创建或加入的命名空间(如 pid, network, mount, uts, ipc, cgroup)。 特别注意其中的 Path 字段。如果不填,runc 会调用 unshare 创建一个全新的 Namespace;如果填了一个具体的路径(例如 /proc/1234/ns/net),runc 会调用 setns 让容器加入到一个已经存在的 Namespace 中。这就是 Kubernetes Pod 中所有容器能共享同一个网络栈的底层实现原理!

UIDMappings 与 GIDMappings ​

go
type LinuxIDMapping struct {
	ContainerID uint32 `json:"containerID"`
	HostID      uint32 `json:"hostID"`
	Size        uint32 `json:"size"`
}

只有在启用了 User Namespace 的情况下,这部分才有效。它定义了容器内外用户 ID 的映射关系。例如:ContainerID=0, HostID=100000, Size=65536 表示将容器内的 root (0) 映射为宿主机的普通用户 (100000),从而彻底消除容器逃逸带来提权风险(Rootless 容器的核心)。

TimeOffsets (较新的特性) ​

支持 Time Namespace,允许容器内部拥有与宿主机不同的系统时间(例如,为了模拟某个过去的时间点来测试应用),而不会影响宿主机的真实时钟。

2. 内核参数调优 (Sysctl) ​

go
Sysctl map[string]string `json:"sysctl,omitempty"`

这是一个键值对字典,对应 Linux 的 /proc/sys 下的内核参数。 在容器化环境中,某些内核参数(特别是网络栈相关的,如 net.ipv4.ip_forward 或 net.core.somaxconn)是属于 Network Namespace 的,这意味着在容器内部修改它们是安全的,不会影响宿主机。通过这个字段,用户可以为每个容器独立调优内核参数,而无需在应用启动脚本中执行 sysctl -w。

3. 安全配置:文件系统掩蔽 (MaskedPaths / ReadonlyPaths) ​

在默认情况下,由于容器共享宿主机内核,宿主机的 /proc 和 /sys 目录会向进程泄露大量底层硬件、内核态信息甚至密钥。

  • MaskedPaths []string:被指定的路径在容器内会被“掩蔽”。runc 的做法通常是在这些路径上挂载一个空的只读文件或目录(例如 /dev/null),让容器内的进程无法读取其实际内容。常见的掩蔽路径包括 /proc/kcore(内核物理内存镜像)、/proc/timer_list 等。
  • ReadonlyPaths []string:被指定的路径会被重新以只读 (ro) 模式挂载(Bind Mount),防止容器内进程修改系统级别配置。典型例子是 /proc/sys 和 /proc/sysrq-trigger。

这两个数组在上一篇文章中我们通过 runc spec 生成的默认 config.json 里已经见识过了,是容器防御内核态攻击的重要防线。

4. 平台执行域 (Personality) ​

go
type LinuxPersonality struct {
	Domain LinuxPersonalityDomain `json:"domain"`
	Flags  []string               `json:"flags,omitempty"`
}

这是一个非常底层的特性,对应 Linux 的 personality(2) 系统调用。它允许进程改变其“执行域”。最常见的用例是:在 64 位 (x86_64) 的内核上,为容器提供一个 32 位 (i386) 的运行环境视图,这对于兼容一些老旧的静态编译程序非常有用。

结语 ​

在 config.go 的 Linux 结构体中,我们看到了 OCI 规范是如何细致入微地管控一个容器的“内核视野”的。从网络栈的加入方式,到内核参数的独立配置,再到敏感内核文件系统的掩蔽,每一项配置都对应着一个经典的 Linux 系统编程概念。

细心的读者可能注意到了,我们故意跳过了 Linux 结构体中极其庞大的 Resources(Cgroups 资源组)和 Seccomp(系统调用过滤)。由于这两部分包含的数据结构过于复杂且重要,我们将在下一篇(下篇)中专门对它们进行深度拆解!

Reading runc Source Code (Part 8): Core OCI Spec Data Structures (Part 2) — Linux Platform Configuration
Reading runc Source Code (Part 6): Runtime, Init Process and Actions in SPEC.md
由 Valaxy v1.2.2 驱动|主题-Yunv1.2.2