尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Podman `--read-only-tmpfs` 选项详解:只读容器的可写 tmpfs 挂载机制

Podman `--read-only-tmpfs` 选项详解:只读容器的可写 tmpfs 挂载机制 Podman--read-only-tmpfs选项详解只读容器的可写 tmpfs 挂载机制【免费下载链接】podmanPodman: A tool for managing OCI containers and pods.项目地址: https://gitcode.com/gh_mirrors/po/podman在 Podman 中--read-only将容器的根文件系统挂载为只读但进程运行时通常仍需在/tmp、/run、/dev/shm等目录写入临时数据。--read-only-tmpfs正是用来控制这些目录在只读容器中的读写状态的关键选项。本文基于仓库中该选项的官方文档docs/source/markdown/options/read-only-tmpfs.md展开结合命令行参数注册、specgen 挂载生成与系统测试完整讲解其默认行为、四种组合场景、底层实现原理、Quadlet 用法与常见实战场景帮助读者精准设计容器文件系统的读写边界。一、选项概述与适用命令--read-only-tmpfs是一个布尔开关其官方文档定义如下当运行--read-only容器时在/dev、/dev/shm、/run、/tmp和/var/tmp上挂载可读写的 tmpfs。默认值为true。该选项由一份共用文档模板生成同时应用于podman create、podman run两个 CLI 命令以及 Quadlet 的podman-container.unitsystemd 单元格式文档头部注释明确标注This option file is used in: podman podman-container.unit.5.md.in, create, run。在命令行为 CLI 参数在 Quadlet 中则写作ReadOnlyTmpfstrue|false。从源码看该标志在 cmd/podman/common/create.go 中注册createFlags.BoolVar( cf.ReadWriteTmpFS, read-only-tmpfs, cf.ReadWriteTmpFS, When running --read-only containers mount read-write tmpfs on /dev, /dev/shm, /run, /tmp and /var/tmp, )注意其默认值取自cf.ReadWriteTmpFSContainerCreateOptions的初始值该值最终由containers.conf配置驱动。也就是说--read-only-tmpfs的默认值可以通过容器配置文件统一调整。与--read-only的配合关系--read-only-tmpfs单独使用没有意义它只在与--read-only组合时生效。仓库源码在 pkg/specgenutil/specgen.go 中明确给出了这一组合逻辑// Only add ReadWrite tmpfs mounts iff the container is // being run ReadOnly and ReadWriteTmpFS is not disabled, // (user specifying --read-only-tmpfsfalse.) localRWTmpfs : c.ReadOnly c.ReadWriteTmpFS s.ReadWriteTmpfs localRWTmpfs当--read-onlyfalse默认时无论--read-only-tmpfs取何值ReadWriteTmpfs都恒为 falsetmpfs 的读写特性完全由容器镜像决定——即/dev、/dev/shm默认可读写/tmp、/run、/var/tmp也是镜像中的可读写目录。二、四种组合的行为对照表原文档给出了一张非常直观的组合矩阵本文完整保留并逐行解读--read-only--read-only-tmpfs/根文件系统/run,/tmp,/var/tmptruetruer/or/wtruefalser/or/ofalsefalser/wr/wfalsetruer/wr/w场景一--read-onlytrue--read-only-tmpfstrue默认组合根文件系统只读同时在/tmp、/run、/var/tmp三个目录上额外挂载可写 tmpfs。这是 Podman 的默认行为——当你只写--read-only而不显式指定--read-only-tmpfs时实际生效的就是这个组合。容器既能保证镜像层不可篡改又能在运行时向这些标准临时目录写入数据适合大多数“只读根文件系统 运行时临时数据”的应用场景。场景二--read-onlytrue--read-only-tmpfsfalse完全只读根文件系统只读且/dev与/dev/shm被标记为只读/tmp、/run、/var/tmp上不挂载任何 tmpfs。这些目录直接暴露底层镜像中的内容默认同样是只读的。此时容器内部不存在任何可写目录成为一个“完全只读”的容器。若应用需要写入数据只能通过外部卷--volume、--mount或外部挂载显式提供可写位置。场景三、四--read-onlyfalse根文件系统可写/dev、/dev/shm可读写/tmp、/run、/var/tmp使用镜像中原本的可读写目录。此时--read-only-tmpfs无论传true还是false行为都完全相同——该选项在非只读容器中不产生任何影响。三、底层实现原理从选项到挂载点--read-only-tmpfs的行为在容器创建链路中被完整实现涉及三层参数到 spec 的转换、挂载点生成、OCI 运行时配置的调整。1. 参数转换为 specgen 字段ContainerCreateOptions.ReadWriteTmpFS经过 pkg/specgenutil/specgen.go 转换为SpecGenerator.ReadWriteTmpfs指针该字段在 pkg/specgen/specgen.go 中定义其 JSON 名为read_write_tmpfs并明确注释其语义ReadWriteTmpfsindicates that when running with a ReadOnlyFilesystem, the tmpfs should be mounted read-write。随后通过 pkg/specgen/generate/container_create.go 中的libpod.WithReadWriteTmpfs()选项注入容器配置最终存储于libpod层容器配置的ReadWriteTmpfs bool字段libpod/container_config.go。2. tmpfs 挂载点的生成真正的挂载点生成位于 pkg/specgen/generate/storage.go 的addReadWriteTmpfsMounts函数func addReadWriteTmpfsMounts(mounts map[string]spec.Mount, volumes []*specgen.NamedVolume, runPath string) map[string]spec.Mount { readonlyTmpfs : []string{/tmp, /var/tmp, runPath} options : []string{rw, rprivate, nosuid, nodev, tmpcopyup} for _, dest : range readonlyTmpfs { if _, ok : mounts[dest]; ok { continue } for _, m : range volumes { if m.Dest dest { continue } } mnt : spec.Mount{ Destination: dest, Type: define.TypeTmpfs, Source: define.TypeTmpfs, Options: options, } mounts[dest] mnt } return mounts }这段实现揭示了三个值得注意的细节挂载点集合/tmp、/var/tmp以及runPath运行时确定的/run路径与文档列出的目录一致挂载选项rw可读写、rprivate私有传播、nosuid禁止 setuid 位、nodev禁止设备文件、tmpcopyup将上层目录已有内容复制到 tmpfs保证/tmp等目录中镜像预置的文件仍然可见冲突规避若目标目录已存在挂载或用户显式指定的命名卷目标恰好是这些目录则跳过自动 tmpfs 挂载避免与用户配置冲突。该函数仅在s.ReadWriteTmpfs ! nil *s.ReadWriteTmpfs时被调用pkg/specgen/generate/storage.go与文档描述完全吻合。3./dev与/dev/shm的只读化当--read-onlytrue且--read-only-tmpfsfalse时/dev与/dev/shm需要被标记为只读。这一逻辑在 OCI spec 生成阶段完成pkg/specgen/generate/oci_linux.goroFS : false if s.ReadOnlyFilesystem ! nil { roFS *s.ReadOnlyFilesystem } rwTmpfs : false if s.ReadWriteTmpfs ! nil { rwTmpfs *s.ReadWriteTmpfs } if roFS !rwTmpfs { setDevOptsReadOnly(g) }setDevOptsReadOnly会将/dev、/dev/shm相关挂载的选项调整为只读。而在完全只读模式下libpod层还会在复制文件到容器如podman cp时对/dev/shm做特别判断——container_internal_common.go中的复制逻辑仅在dstPath /dev/shm ReadWriteTmpfs时才允许写入否则一律拒绝libpod/container_internal_common.go 与 libpod/container_internal_common.go从运行时层面保证“完全只读”的语义。4. 与用户自定义挂载、卷的优先级从addReadWriteTmpfsMounts的实现可以看出Podman 遵循“用户显式配置优先”的原则只要/tmp、/var/tmp、/run中任意目录已被用户挂载无论是否 tmpfs或已绑定命名卷自动挂载就会跳过该目录。因此在完全只读模式下用户可以精确地为个别目录补上可写挂载例如--volume mydata:/data而不会影响其他目录的只读状态。四、Quadletsystemd 单元中的用法除 CLI 外该选项在 Quadlet 的[Container]组中写作ReadOnlyTmpfstrue|false对应键定义在 pkg/systemd/quadlet/quadlet.go并作为受支持的容器组键注册pkg/systemd/quadlet/quadlet.go在生成 systemd 单元时被翻译为--read-only-tmpfs参数pkg/systemd/quadlet/quadlet.go。仓库的 e2e 测试提供了三个直观的 Quadlet 示例readonly.container默认组合ReadOnlyyes且不写ReadOnlyTmpfs[Container] Imagelocalhost/imagename ReadOnlyyes断言生成的 podman 参数为--read-only-tmpfs --read-only即默认值true被显式带上。readonly-tmpfs.container显式开启可写 tmpfs[Container] Imagelocalhost/imagename ReadOnlyyes ReadOnlyTmpfsyesreadonly-notmpfs.container完全只读[Container] Imagelocalhost/imagename ReadOnlyyes ReadOnlyTmpfsno断言生成的参数为--read-only-tmpfsfalse --read-only对应上文“场景二”的完全只读语义。这三个文件分别位于 test/e2e/quadlet/readonly.container、test/e2e/quadlet/readonly-tmpfs.container 与 test/e2e/quadlet/readonly-notmpfs.container。五、实战验证与测试佐证仓库的系统级测试 test/system/030-run.bats 用containers.conf设置read_onlytrue然后对/tmp/a、/var/tmp/b、/dev/c、/dev/shm/d、/run/e这五个文件逐一执行touch完整验证了各组合的行为--read-only默认 tmpfstruetouch全部成功——五个目录都是可写 tmpfs--read-onlyfalse或--read-onlyfalse --read-only-tmpfstruetouch全部成功非只读模式下选项不生效--read-only --read-only-tmpfsfalsetouch全部失败输出touch: /tmp/a: Read-only file system等五条错误——容器完全只读。这一测试直接印证了文档中四种组合的全部行为读者可以在本地运行podman run --read-only --read-only-tmpfsfalse image touch /tmp/a复现“完全只读”的效果。快速验证命令# 默认组合根文件系统只读/tmp、/run、/var/tmp 可写 podman run --rm --read-only registry.fedoraproject.org/fedora-minimal touch /tmp/hello /var/tmp/world # 完全只读所有目录只读写入被拒绝 podman run --rm --read-only --read-only-tmpfsfalse registry.fedoraproject.org/fedora-minimal touch /tmp/hello # 输出: touch: /tmp/hello: Read-only file system # 显式关闭只读一切照旧 podman run --rm --read-onlyfalse --read-only-tmpfstrue registry.fedoraproject.org/fedora-minimal touch /tmp/hello六、实践建议与注意事项安全加固场景优先使用完全只读对于不需要写临时文件的安全敏感容器如只读代理、校验器推荐--read-only --read-only-tmpfsfalse再按需通过--volume或--mount挂载唯一的数据写入点攻击面最小。默认组合适合常规只读容器很多应用如 JVM、Python 解释器依赖/tmp写缓存直接使用默认的--read-only即可获得“镜像只读 临时目录可写”的平衡。注意/dev与/dev/shm的差异在默认组合下/dev与/dev/shm上挂载的是可写 tmpfs只有完全只读模式下它们才被标记为只读。若容器依赖共享内存如--ipchost下的 IPC需留意/dev/shm的实际大小与读写状态。用户挂载优先级最高自动 tmpfs 挂载会主动避让用户显式指定的卷与挂载这一设计保证了--read-only与自定义挂载组合时的可预测性。Quadlet 中显式声明更稳妥虽然ReadOnlyTmpfs默认是true但建议在.container文件中显式写出ReadOnlyTmpfsyes|no避免升级或配置变化带来的隐性行为差异。通过本文的解读可以看到--read-only-tmpfs虽只是一个布尔选项但其背后是 Podman 从 CLI 解析、specgen 转换、挂载生成到 OCI 运行时配置的一条完整实现链路。理解这四种组合的语义是设计安全、可预期、可运维的只读容器的关键一步。【免费下载链接】podmanPodman: A tool for managing OCI containers and pods.项目地址: https://gitcode.com/gh_mirrors/po/podman创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表