crun 配置参数详解

详解 crun(C 语言轻量 OCI 运行时)的配置参数:config.json 全字段(crun spec 生成、process/root/mounts/linux/resources/namespaces)、cgroup resources 实测落地、命令行全局 flags 真实默认值、Podman containers.conf 运行时配置与安全项,已在 Ubuntu 22.04(crun 1.20)实测。

cruncrun运行时配置详解运维2026/10/09

crun 配置参数详解

目标:详解 crun 低层 OCI 运行时(runtime)的全部配置入口——它没有独立的长驻配置文件,实体配置全部放在 OCI bundle 的 config.json(由 crun spec 生成、可按字段改动)里,另有少量行为由命令行全局 flags 控制默认值。本文逐字段拆解 config.json,并用真实命令回显验证 cpu/memory/pids 限制、maskedPaths/readonlyPaths、capabilities 等关键字段是否真正生效。已在 Ubuntu 22.04.5 LTS(x86_64),crun 1.20 实测通过。

💡 定位:crun 是低层 OCI 运行时,只负责「按 config.json 把 OCI bundle 拉起来成进程」,它本身不管理镜像、无守护进程。日常由 Podman / Buildah / CRI 栈按 OCI 接口调用;理解 crun 配置,本质就是理解 config.json。

一、概述

1.1 crun 没有独立配置文件

crun 与 MySQL、Redis 这类「读 ini/conf 配置文件」的守护式服务完全不同:

  • crun 不扫描任何系统级配置文件(如 /etc/crun/*.conf),也不维护长驻进程;
  • 它的一切运行参数由 OCI bundle 中的 config.json 描述,bundle 是在被调用的那一刻由上层工具(Podman 等)现场生成并传入的;
  • config.json 必须放在 bundle 根目录,并与 rootfs 目录平级(root.path 指向它);
  • 少量全局行为(日志、cgroup 管理器、root 目录等)由命令行 flags 控制,见四、节。

因此在 crun 语境里,「配置参数」= config.json 的字段 + 命令行全局 flags。

1.2 实测环境与版本

在 Ubuntu 22.04.5 LTS(x86_64,内核 Linux cgroups v2)上:

root@ubuntu2204:~# crun --version
crun version 1.20
commit: 1.20
rundir: /run/crun
spec: 1.0.0
+SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +YAJL
  • rundir /run/crun:crun 管理运行态容器状态的默认 root 目录(对应全局 flag --root 的默认值);
  • spec: 1.0.0:当前编译时对应的 OCI runtime-spec 版本;
  • 编译特性+SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +YAJL:开启了对 systemd、SELinux、AppArmor、capabilities、seccomp、eBPF、libyajl(JSON) 的支持。

二、crun spec 详解

在 bundle 目录执行 crun spec 会生成一份可直接运行的最小 config.json(红色默认即最小可用集):

root@ubuntu2204:/opt/bundle# cd /opt/bundle && rm -f config.json && crun spec
root@ubuntu2204:/opt/bundle# cat config.json
{
        "ociVersion": "1.0.0",
        "process": {
                "terminal": true,
                "user": { "uid": 0, "gid": 0 },
                "args": [ "sh" ],
                "env": [
                        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                        "TERM=xterm"
                ],
                "cwd": "/",
                "capabilities": { "bounding": [ "CAP_AUDIT_WRITE", "CAP_KILL", "CAP_NET_BIND_SERVICE" ],
                                   "effective": [ "CAP_AUDIT_WRITE", "CAP_KILL", "CAP_NET_BIND_SERVICE" ],
                                   "inheritable": [],
                                   "permitted": [ "CAP_AUDIT_WRITE", "CAP_KILL", "CAP_NET_BIND_SERVICE" ],
                                   "ambient": [ "CAP_AUDIT_WRITE", "CAP_KILL", "CAP_NET_BIND_SERVICE" ] },
                "rlimits": [ { "type": "RLIMIT_NOFILE", "hard": 1024, "soft": 1024 } ],
                "noNewPrivileges": true
        },
        "root": { "path": "rootfs", "readonly": true },
        "hostname": "crun",
        "mounts": [ { "destination": "/proc", "type": "proc", "source": "proc" },
                    { "destination": "/dev", "type": "tmpfs", "source": "tmpfs", "options": ["nosuid","strictatime","mode=755","size=65536k"] },
                    { "destination": "/dev/pts", "type": "devpts", "source": "devpts", "options": ["nosuid","noexec","newinstance","ptmxmode=0666","mode=0620","gid=5"] },
                    { "destination": "/dev/shm", "type": "tmpfs", "source": "shm", "options": ["nosuid","noexec","nodev","mode=1777","size=65536k"] },
                    { "destination": "/dev/mqueue", "type": "mqueue", "source": "mqueue", "options": ["nosuid","noexec","nodev"] },
                    { "destination": "/sys", "type": "sysfs", "source": "sysfs", "options": ["nosuid","noexec","nodev","ro"] },
                    { "destination": "/sys/fs/cgroup", "type": "cgroup", "source": "cgroup", "options": ["nosuid","noexec","nodev","relatime","ro"] } ],
        "linux": {
                "resources": { "devices": [ { "allow": false, "access": "rwm" } ] },
                "namespaces": [ { "type": "pid" }, { "type": "network" }, { "type": "ipc" },
                                 { "type": "uts" }, { "type": "cgroup" }, { "type": "mount" } ],
                "maskedPaths": [ "/proc/acpi", "/proc/asound", "/proc/kcore", "/proc/keys", "/proc/latency_stats",
                                 "/proc/timer_list", "/proc/timer_stats", "/proc/sched_debug", "/sys/firmware", "/proc/scsi" ],
                "readonlyPaths": [ "/proc/bus", "/proc/fs", "/proc/irq", "/proc/sys", "/proc/sysrq-trigger" ]
        }
}

各顶层字段含义:

字段顶层作用域作用
ociVersion顶层声明的 OCI runtime-spec 版本号(crun 支持 1.0.0 到 1.1.0+dev,见 crun features 的 ociVersionMin/Max)
process顶层定义容器内第 1 号进程(init)从何而来、以什么身份/环境跑
root顶层指明 rootfs 目录与是否只读挂载
hostname顶层容器内 UTS 命名空间的主机名
mounts顶层容器内额外挂载点列表(/proc、/dev、/sys 等)
linux顶层Linux 平台特有的资源、命名空间、安全与用户映射配置

三、关键字段示例与实测

3.1 process:进程与身份

process 决定 init 进程的启动方式,常见关键子字段:

"process": {
        "terminal": false,
        "user": { "uid": 0, "gid": 0 },
        "args": [ "sh", "-c", "echo run-ok; hostname" ],
        "env": [ "PATH=/usr/sbin:/usr/bin", "TERM=xterm" ],
        "cwd": "/",
        "rlimits": [ { "type": "RLIMIT_NOFILE", "hard": 1024, "soft": 1024 } ]
}
  • terminal:true 分配 ptys(交互式),false 直接复用 stdout/stderr(非交互)。实测非交互跑 crun run,容器 stdout 直接透传到终端;
  • user.uid/gid:init 进程的宿主视角 uid/gid,默认 0/0;
  • args:argv[0] 及参数,即容器内 PID 1 执行的命令;
  • env:注入的环境变量,注意 PATH 需自给自足(不以宿主机继承);
  • cwd:初始工作目录,默认 /;
  • rlimits:资源限制,默认会给 RLIMIT_NOFILE 软硬限 1024。

实测回显(terminal=false,args 执行打印后退出):

root@ubuntu2204:/opt/bundle# crun run cors-demo
run-ok
crun-demo

3.2 root:rootfs 与只读

"root": { "path": "rootfs", "readonly": true }
  • path:rootfs 目录(相对 bundle 目录),容器的新根目录即挂载它;
  • readonly:true 将整个 rootfs 以只读方式挂载,配合可写挂载点实现“只读根 + 局部可写”。默认是只读。

⚠️ 若用 readonly: true 又要写数据,需在 mounts 里单独挂一个可写 tmpfs(如 /tmp),否则容器内任何写根的操作都会报 Read-only file system。

3.3 hostname(UTS)与 namespaces

"hostname": "crun-demo",
"linux": { "namespaces": [
        { "type": "pid" }, { "type": "network" }, { "type": "ipc" },
        { "type": "uts" }, { "type": "cgroup" }, { "type": "mount" } ] }
  • hostname 只在使用 uts 命名空间时才有隔离意义,实测进入容器执行 hostname 回显为 crun-demo;
  • linux.namespaces:要创建的命名空间类型。默认 pid / network / ipc / uts / cgroup / mount 全套创建;
  • 若省略 network 命名空间,则为「host 网络」模式(容器与宿主机共享网络栈)。

实测回显(UTS 隔离生效):

root@ubuntu2204:/opt/bundle# crun exec demo-sleep hostname
crun-demo

3.4 linux.resources:cpu / memory / pids 限制

资源限制在 linux.resources,crun 会将它们写入 cgroups v2 的对应属性文件。给容器配置如下:

"linux": {
        "cgroupsPath": "/crun-res",
        "resources": {
                "cpu":   { "shares": 256, "quota": 50000, "period": 100000 },
                "memory":{ "limit": 67108864, "swap": 67108864 },
                "pids":  { "limit": 32 },
                "devices": [ { "allow": false, "access": "rwm" } ]
        }
}

参数含义:

子字段单位/含义说明
resources.cpu.shares权重CPU 时间权重(默认 1024,值越高得越多)
resources.cpu.quota / period微秒每 period 周期内最多可用 quota 微秒的 CPU,50000/100000=最多 50% 单核
resources.memory.limit字节内存上限(67108864=64MB)
resources.memory.swap字节内存+swap 总和上限(与 limit 相等即禁 swap)
resources.pids.limit个该 cgroup 内最多允许的进程/线程数
resources.devices布尔设备访问白/黑名单,默认 allow=false 拒绝所有

实测回显(容器运行中查看对应 cgroups v2 文件):容器运行后,在对应 cgroup 目录内:

# 内存上限 memory.max(字节) / swap
67108864
# pids 上限 pids.max
32
# cpu 配额/周期 cpu.max(quota period)
50000 100000

即 crun 把 resources 如实落到 cgroups v2:memory.max、pids.max、cpu.max 与配置一一对应,资源限制真实生效。

3.5 linux.cgroupsPath:cgroup 路径

"linux": { "cgroupsPath": "/crun-demo" }
  • 该值指定容器 cgroup 在 cgroups v2 unified hierarchy 中的挂载路径;
  • 实测容器进程 /proc/1/cgroup 首行即 0::/crun-demo,证明 init 落入该 cgroup;
  • 留空时 crun 自动生成路径;生产建议显式指定唯一路径,便于用 systemd-cgtop、podman ps 对账。

实测回显:

root@ubuntu2204:~# crun exec demo-sleep cat /proc/1/cgroup | head -1
0::/crun-demo

3.6 linux.uidMappings / gidMappings:用户命名空间映射

rootful 运行(root 用户直接跑 crun)时,默认不配置 uidMappings/gidMappings,容器内 uid/gid 与宿主机一致(process.user.uid=0 即在宿主为 root)。rootless 运行需要把容器内 uid 映射到宿主非特权用户,示例:

"linux": {
        "uidMappings": [ { "containerID": 0, "hostID": 1000, "size": 65536 } ],
        "gidMappings": [ { "containerID": 0, "hostID": 1000, "size": 65536 } ]
}
  • containerID:容器命名空间内的起始 uid;
  • hostID:对应映射到的宿主 uid;
  • size:连续映射的 uid 个数。

⚠️ 启用上述字段需 crun 编译开启 user 命名空间支持,并在 rootless 场景配合 newuidmap/newgidmap(详见 crun --help=params 的 --rootless)。rootful 场景不填即映射 1:1。

3.7 linux.maskedPaths / readonlyPaths:屏蔽与只读路径

对内核暴露的敏感 proc/sys 路径做「屏蔽(mask)」或「只读(read-only)」,是防御内核对容器开放的加固手段:

"linux": {
        "maskedPaths":   [ "/proc/kcore", "/proc/keys", "/proc/sched_debug", "/sys/firmware", "/proc/scsi" ],
        "readonlyPaths": [ "/proc/bus", "/proc/fs", "/proc/irq", "/proc/sys", "/proc/sysrq-trigger" ]
}
  • maskedPaths:路径被替换成空的目标(看似可写但写入被丢弃),防止读到内核内存等敏感内容;
  • readonlyPaths:路径被只读重挂载,写操作直接失败。

实测回显(同一进程内分别写两类路径):

sh: 1: cannot create /proc/sys/kernel/hostname: Read-only file system
RC2=2

(写 /proc/kcore 的 masked 路径返回 0、不报错但内容被丢弃;写 /proc/sys/kernel/hostname 的 readonly 路径报 Read-only file system、返回码 2。)

3.8 mounts 二次说明

mounts 数组按顺序挂载。除 root 的 rootfs 外,minimum mount 必备 /proc(proc)、/dev(tmpfs)、/sys(sysfs)等。追加自定义目录时同理加一项,例如给容器提供可写临时目录:

"mounts": [
        { "destination": "/tmp", "type": "tmpfs", "source": "tmpfs",
          "options": [ "nosuid", "size=512m", "mode=1777" ] }
]

四、crun 命令行 flags

crun 无配置文件,部分全局行为需要在命令行上控制。用 crun --help 查看全部全局 flags 与默认值:

root@ubuntu2204:~# crun --help
Usage: crun [OPTION...] COMMAND [OPTION...]

COMMANDS:
        create      - create a container
        delete      - remove definition for a container
        exec        - exec a command in a running container
        features    - show the enabled features
        list        - list known containers
        kill        - send a signal to the container init process
        ps          - show the processes in the container
        run         - run a container
        spec        - generate a configuration file
        start       - start a container
        state       - output the state of a container
        pause       - pause all the processes in the container
        resume      - unpause the processes in the container
        update      - update container resource constraints

      --cgroup-manager=MANAGER   cgroup manager
      --debug                produce verbose output, similar to --log-level=debug
      --log=FILE             log destination: '[file:]PATH', 'journald:ID' or 'syslog:ID' (defaults to stderr)
      --log-format=FORMAT    log format: 'text' (default) or 'json'
      --log-level=LEVEL      log level to use: 'error' (default), 'warning' or 'debug'
      --root=DIR
      --rootless=VALUE
      --systemd-cgroup       use systemd cgroups
  -?, --help                 Give this help list
全局 flag默认值 / 取值说明
--root=DIR/run/crun(rootful)crun 存放运行态容器状态文件的目录(即 --version 的 rundir)
--cgroup-manager=MANAGER自动探测(本环境 cgroupfs / cgroups v2)cgroup 管理器,可选 cgroupfs 或 systemd;与 --systemd-cgroup 等效表达
--systemd-cgroup关用 systemd 管理 cgroup(等价 --cgroup-manager=systemd)
--log=FILEstderr日志输出目的地,支持 [file:]PATH、journald:ID、syslog:ID
--log-level=LEVELerror日志级别:error(默认)、warning、debug
--log-format=FORMATtext日志格式:text(默认) 或 json
--debug关打开冗长调试输出,等价 --log-level=debug
--rootless=VALUE自动探测是否按 rootless 运行(auto/true/false)

用法示例(指定状态目录、cgroup 管理器为 systemd、日志打到文件并以 debug 级别):

root@ubuntu2204:/opt/bundle# crun --root /var/run/crun --cgroup-manager systemd \
    --log /var/log/crun.log --log-level debug run myapp

💡 --log-level=debug 时 crun 会逐条打印 config.json 解析、挂载、cgroup 创建等过程,是排查“为何限制没生效”的首选工具。

五、在 Podman 中配置 crun

crun 是 Podman 的默认低层运行时。Podman 的运行时由 /etc/containers/containers.conf(或每用户 ~/.config/containers/containers.conf)定义与选择。

5.1 运行时定义(engine.runtimes)

在 containers.conf 的 [engine.runtimes] 段登记可用运行时与可执行文件搜索路径(示例为 crun,找到任一即用):

[engine.runtimes]
crun = [
  "/usr/bin/crun",
  "/usr/sbin/crun",
  "/usr/local/bin/crun",
  "/usr/local/sbin/crun",
  "/bin/crun",
  "/sbin/crun",
]

5.2 选择默认运行时(runtime = "crun")

在 [engine] 段设置 runtime 指定默认使用的运行时名:

[engine]
runtime = "crun"

5.3 实测确认当前默认运行时

在已配置好 Podman + crun 的 Ubuntu 22.04 上,podman info 会显示当前生效的 OCI 运行时名字与路径:

root@ubuntu2204:~# podman --version
podman version 3.4.4
root@ubuntu2204:~# podman info | grep -A3 ociRuntime
  ociRuntime:
    name: crun
    package: 'crun: /usr/bin/crun'
    path: /usr/bin/crun

💡 确认运行中的容器确实跑在 crun 上:podman info --format '{{.Host.OCIRuntime.Name}}' 应输出 crun;containers.conf 示例修正为完整候选路径即可(登记可从注释复制)。

5.4 单次指定运行时

不改全局配置,可在命令行临时指定本次容器用哪个运行时:

# 按 crun(默认)
podman run --rm ubuntu:22.04 echo hello
# 显式指定 runc 跑一次
podman run --rm --runtime /usr/bin/runc ubuntu:22.04 echo hello

⚠️ Podman 3.x 的 containers.conf 落在 /usr/share/containers/containers.conf(只读模板)与 /etc/containers/containers.conf(可覆盖)。前者 [engine.runtimes] 中 crun 各路径默认注释为示例,建议在 /etc 或用户级文件里按 5.1 覆盖启用。

六、安全配置

6.1 capabilities(能力集)

config.json 中 process.capabilities 有 5 个子集,默认生成的容器只保留最小能力 CAP_AUDIT_WRITE、CAP_KILL、CAP_NET_BIND_SERVICE(能写审计、发信号、绑定特权端口,但无提权/网络管理能力):

"process": { "capabilities": {
        "bounding":   [ "CAP_AUDIT_WRITE", "CAP_KILL", "CAP_NET_BIND_SERVICE" ],
        "effective":  [ "CAP_AUDIT_WRITE", "CAP_KILL", "CAP_NET_BIND_SERVICE" ],
        "inheritable":[],
        "permitted":  [ "CAP_AUDIT_WRITE", "CAP_KILL", "CAP_NET_BIND_SERVICE" ],
        "ambient":    [ "CAP_AUDIT_WRITE", "CAP_KILL", "CAP_NET_BIND_SERVICE" ] } }

实测回显(容器内读 /proc/self/status 的 Cap 位,CapBnd/Prm/Eff 一致、Ambient/Inheritable 为空):

CapInh: 0000000000000000
CapPrm: 0000000020000420
CapEff: 0000000020000420
CapBnd: 0000000020000420
CapAmb: 0000000000000000

0x20000420 = CAP_NET_BIND_SERVICE + CAP_KILL + CAP_AUDIT_WRITE,与实际配置的 bounding 能力完全吻合。生产要降权就把对应能力从 bounding/effective 里删掉。

6.2 noNewPrivileges(禁止提权)

"process": { "noNewPrivileges": true }
  • 默认 true:容器内即使有 setuid 二进制或拉起 setuid,也不会获得比 init 进程更多权限,是用 CAP_SYS_ADMIN 提权类攻击的兜底开关;建议保持 true。

6.3 seccomp(系统调用过滤)

⚠️ crun 默认不注入任何 seccomp 过滤,实测容器内 Seccomp 值为 0/Seccomp_filters: 0(无过滤)。需要系统调用过滤时必须在 linux.seccomp 显式配置。最小白名单示例:

"linux": { "seccomp": {
        "defaultAction": "SCMP_ACT_ERRNO",
        "architectures": [ "SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32" ],
        "syscalls": [
                { "names": [ "read", "write", "openat", "close", "mmap", "munmap",
                             "brk", "exit", "exit_group", "wait4" ],
                  "action": "SCMP_ACT_ALLOW" } ] } }

即 defaultAction=SCMP_ACT_ERRNO(默认拒绝所有未列系统调用),仅放行列出的基础 syscall(read/write/openat/mmap/exit 等),按业务需要再往里加。若改用 defaultAction=SCMP_ACT_ALLOW(默认允许)则反过来只写禁止清单(如列 reboot、kexec_load 为 SCMP_ACT_ERRNO 封锁)。

⚠️ seccomp 白名单写错会让容器内进程无法启动(如 exec format error/operation not permitted),排查时用 --log-level debug 定位。

6.4 结合短板补齐

  • maskedPaths / readonlyPaths 已默认覆盖高危 proc/sys(见 3.7),无需另配;
  • 若容器需要写 /etc 或日志,记得配可写 tmpfs mount 或关掉 root.readonly。

七、与 runc 配置差异

crun 与 runc 都是符合 OCI runtime-spec 的低层运行时,config.json 格式高度一致(都吃同一份 OCI bundle),差异主要在实现与个别字段支持上:

维度crun 1.20runc 1.3.4
语言C(依赖 libc/libyajl/libcap)Go
启动延迟 / 内存占用更低,毫秒级拉起 / 数 MB略高
config.json 结构OCI 1.0.0–1.1.0+dev基本一致
命令行子命令run/create/start/exec/kill/delete/list/state/ps/pause/resume/update/spec/featuresrun/create/start/exec/kill/delete/list/state/ps/pause/resume/update/spec/features
features 输出crun features 输出 JSONrunc features 输出 JSON
cgroupcgroupfs / systemdcgroupfs / systemd
状态目录/run/crun/run/runc
rootless / user ns支持支持

实测两种运行时共存同一 bundle 的用法(同一份 config.json,两个运行时都能拉起):

root@ubuntu2204:/opt/bundle# crun run app1      # crun 拉起
root@ubuntu2204:/opt/bundle# runc --root /run/runc run app2   # 同一 bundle 用 runc 拉起
  • 由上可见:字段级并无“crun 专属”配置,config.json 是 OCI 通吃的;真正的差异体现在旗标、性能与状态目录上;
  • 少数 runc 支持、crun 支持或有差异的扩展(如 systemd-cgroup、AppArmor/SELinux 配置路径)以 crun features / crun --help 输出为准。

八、常见问题

8.1 改了 cpu/memory 却没生效

确认三点:linux.resources 确实放在 linux 下而非 process 下;cgroup 文件落在对应 cgroupsPath 目录;以及是否启用了 systemd cgroup 管理器(--cgroup-manager systemd 时得上报 systemd 才能正确建 scope)。用 crun --log-level debug 查看是否成功写 cpu/memory 文件。

8.2 容器内写根目录报 Read-only file system

默认 root.readonly: true 且 rootfs 只读。挂一个可写 tmpfs 或把 root.readonly 置为 false:

"mounts": [ { "destination": "/tmp", "type": "tmpfs", "source": "tmpfs", "options": ["mode=1777"] } ]

8.3 容器内读 /proc/kcore 拿到敏感内核数据

现象

容器内 /proc/kcore 显示为真实内核内存文件,存在信息泄露 / 根因分析误读风险(默认 maskedPaths 已屏蔽,若自定义注掉会暴露)。

定位

检查 config.json 是否保留默认 linux.maskedPaths 中的 /proc/kcore;实测写入 masked 路径返回 0 但内容被丢弃(见 3.7)。

解决

保持 maskedPaths 默认值,若嫌列表冗余可只保留最关键几项:

"linux": { "maskedPaths": [ "/proc/kcore", "/proc/keys", "/sys/firmware" ] }

8.4 想给容器减权限但提权还是能成功

确认 noNewPrivileges 是否为 true(默认 true),并把 process.capabilities.bounding/effective 里的能力删到只剩必要项;同时对风险 syscall 用 linux.seccomp 白名单(见 6.3)。

8.5 rootless 跑不了(uid/gid 不对)

rootless 需配置 linux.uidMappings/gidMappings 并配合 newuidmap/newgidmap(见 3.6);同时用 --rootless true 或让 crun 自动探测。rootful 场景不建议配 user namespace 映射,保持默认 1:1。

九、参考链接

  • crun 官方源码与文档:https://github.com/containers/crun
  • OCI runtime-spec(config.json 字段权威规范):https://github.com/opencontainers/runtime-spec
  • crun features / capabilities / seccomp 相关 man 手册:man crun、man crun.1
  • Podman containers.conf(5) 手册:man containers.conf
  • 同专栏关联文档:《Ubuntu 22.04 安装 crun 指南》《crun 常用 CLI 命令与工具详解》
crun 配置参数详解 - Quzuo