Docker 容器只读文件系统配置与临时目录写入实战 [复制链接]

一级用户组
金小颖论坛 AI 摘要
AI 正在阅读全文并生成摘要,请稍等……

在生产环境中,容器默认拥有可写层,应用进程可以修改配置、写入脚本或生成临时文件。一旦程序存在漏洞或配置错误,可写根文件系统可能被用于植入文件、篡改运行内容,甚至留下持久化痕迹。🔒 将 Docker 容器的根文件系统设为只读,再为确有需要的目录单独提供临时写入空间,是一种简单、实用的最小权限方案。

一、只读文件系统解决什么问题

Docker 的只读模式会限制容器对根文件系统的写操作,但不会影响外部挂载的卷、绑定挂载或 tmpfs。这样可以把“允许写入”的范围从整个容器收缩到几个明确目录,既减少误写配置的风险,也更容易发现应用不合理的文件依赖。

需要注意,只读根文件系统并不等于容器绝对安全。它无法替代非 root 用户、能力限制、镜像漏洞扫描和网络隔离,但可以作为纵深防御中的重要一层。🛡️

二、先用最小命令验证

启动一个只读容器,可以使用 --read-only 参数:

docker run --rm --read-only alpine sh -c "touch /test.txt"

运行后会看到“Read-only file system”之类的错误,这说明根文件系统已经不能写入。如果应用只是读取配置并提供无状态服务,这种模式可能直接可用;但多数程序还需要使用 /tmp、/run、缓存目录或 PID 文件目录。

三、使用 tmpfs 提供临时写入空间

tmpfs 适合保存无需持久化的临时文件。它独立于容器可写层,容器停止后其中的数据会消失。Docker 官方说明,tmpfs 主要驻留在主机内存中,但在主机启用交换空间时,相关页面仍可能被换出,因此不应简单地把它理解为“敏感数据永远不会落盘”。具体机制可参考 Docker tmpfs 官方文档

下面为只读容器开放 /tmp,并限制容量与权限:

docker run --rm --read-only --tmpfs /tmp:rw,nosuid,nodev,noexec,size=64m,mode=1777 alpine sh -c "echo ok > /tmp/result && cat /tmp/result"
  • rw:允许在该挂载点写入。
  • nosuid:不执行 setuid、setgid 权限位。
  • nodev:不解释设备文件。
  • noexec:禁止直接执行该目录中的程序。
  • size=64m:限制临时文件系统的最大容量。
  • mode=1777:提供类似常见 /tmp 的权限规则。

容量限制非常重要。tmpfs 会消耗主机内存资源,如果应用持续写入且没有边界,可能给主机带来内存压力。📦 应根据真实业务峰值设置上限,并配合内存监控,而不是无限制开放。

四、Docker Compose 实战配置

在 Compose 中,可通过 read_onlytmpfs 组合实现相同效果:

services:
app:
image: my-app:latest
read_only: true
tmpfs:
- /tmp:size=64m,mode=1777
- /run:size=16m,mode=755
volumes:
- ./config:/app/config:ro

这里的根文件系统保持只读,/tmp 和 /run 用于短期运行数据,配置目录则通过只读绑定挂载提供。若应用产生必须保留的数据,例如上传文件、数据库内容或审计记录,应使用命名卷或受控的持久化存储,不能放入 tmpfs。

临时数据与持久化数据要分开

  • 临时缓存、套接字、PID 文件:优先使用 tmpfs。
  • 业务数据、用户上传内容:使用命名卷或外部存储。
  • 配置文件、证书和静态资源:采用只读挂载。
  • 应用日志:优先输出到标准输出和标准错误,由日志系统采集。

五、排查应用到底要写哪些目录

不要一开始就给 /var、/app 等大目录整体开放写权限。更稳妥的方式是先在测试环境开启只读模式,启动应用并观察报错,再逐个识别必需的写入路径。常见错误包括无法创建缓存、PID 文件、Unix Socket、临时编译文件以及锁文件。

  1. 先执行应用的启动流程和健康检查。
  2. 覆盖上传、导出、定时任务等功能路径。
  3. 记录每一个只读文件系统错误对应的目录。
  4. 判断数据是否需要跨容器重启保留。
  5. 仅为必要目录配置 tmpfs 或持久化卷。

还可以查看容器的挂载和只读配置:

docker inspect 容器名

重点检查 ReadonlyRootfs 是否为 true,并确认目标目录对应的是 tmpfs、volume 还是 bind mount。✅

六、非 root 用户的权限陷阱

如果容器以非 root 用户运行,tmpfs 的默认所有者和权限可能不符合应用要求,最终仍会出现“Permission denied”。此时应根据镜像内用户的 UID、GID 设置挂载权限,或选择合适的 mode。不要为了快速通过测试直接使用 0777,更不要因此把应用改回 root 用户。

另外,tmpfs 挂载会遮挡目标目录中原有的内容。如果镜像已经在 /run/app 中预置文件,再把 tmpfs 挂载到同一路径,这些文件在容器运行期间将不可见。部署前应检查目录内容,必要时调整挂载点或在启动阶段初始化所需文件。

七、推荐的加固组合

只读文件系统最好与其他限制同时使用:

docker run --rm --read-only --user 10001:10001 --cap-drop ALL --security-opt no-new-privileges:true --tmpfs /tmp:rw,nosuid,nodev,noexec,size=64m,mode=1777 my-app:latest

这组参数分别限制根文件系统写入、避免 root 身份、移除 Linux capabilities、阻止进程获取新权限,并只开放受控的临时目录。正式上线前仍需完成兼容性测试,尤其要验证健康检查、优雅退出、证书加载和故障诊断流程。

总结

Docker 只读根文件系统的核心思路不是“禁止一切写入”,而是把写权限精确缩小到业务真正需要的位置。🚀 临时数据使用有容量限制的 tmpfs,持久化数据使用卷,配置和静态文件采用只读挂载,再配合非 root 用户与权限收敛,就能在不明显增加运维复杂度的前提下,提高容器运行环境的可控性与抗篡改能力。

最新回复

请先登录后再回复 登录

uid:2 一级用户组
关注
发帖 1053
评论 0
粉丝 0
关注 0
发新帖
目录
Docker 容器只读文件系统配置与临时目录写入实战