最标准安全的做法是在服务单元文件的[Service]段添加PrivateTmp=yes,systemd会为服务挂载独立tmpfs到/tmp和/var/tmp实现完全隔离,且服务停止后自动销毁;须搭配ProtectSystem=strict等增强纵深防御。

PrivateTmp=yes 是唯一标准启用方式
直接在服务单元文件的 [Service] 段写 PrivateTmp=yes,这是 systemd 官方定义的、唯一被支持的启用私有 /tmp 的方式。PrivateTmp=true 虽然语法上能解析,但属于历史遗留别名,不推荐;PrivateTmp=1 或其他值会被忽略或报错。它默认同时作用于 /tmp 和 /var/tmp,无需额外配置。
常见错误现象:systemctl show myapp.service | grep PrivateTmp 输出为空或 PrivateTmp=no,说明配置未生效——大概率是没加在正确的段(比如错加到 [Unit]),或拼写为 Privatetmp(大小写敏感)。
验证必须进 mount namespace 看,不能只查配置
配置写对了不等于运行时隔离成功。必须用 nsenter 进入服务进程的挂载命名空间,才能确认 /tmp 是否真的独立:
- 先获取主进程 PID:
pgrep -f 'myapp'(注意不是systemctl status里显示的 “Main PID”,那个可能是 systemd 的代理 PID) - 再执行:
sudo nsenter -t $PID -m -p ls /tmp—— 如果看到的是空目录或仅本服务创建的文件,说明隔离成功;如果能看到其他服务或用户的临时文件,说明没生效或被覆盖 - 不要依赖
ls /tmp在主机 shell 下的结果,那永远是全局视图
旧应用强依赖全局 /tmp 时的应对原则
极少数老旧程序会硬编码路径如 /tmp/myapp.sock 并期望被其他进程访问,启用 PrivateTmp=yes 后这类通信会中断。此时应优先改造应用,而非妥协隔离:
- 首选方案:改代码或启动参数,用
$XDG_RUNTIME_DIR或--socket-dir=/run/myapp等更规范的位置 - 次选(不推荐):用
BindPaths=/tmp/shared:/tmp/shared:ro显式挂入一个共享子目录,但必须确保该子目录权限严格、内容可控 - 绝对禁止:
PrivateTmp=no回退,或用ReadWriteDirectories=/tmp打开全局写入 —— 这等于完全放弃隔离
搭配 ProtectSystem=strict 才算真正落地
PrivateTmp=yes 单独启用只是“隔离”,不是“防护”。如果服务以 root 运行且未限制能力,它仍可重新挂载、绕过 tmpfs、甚至卸载整个 /tmp。必须组合基础纵深防御:
-
ProtectSystem=strict:把/usr、/boot、/etc全部设为只读,阻止修改系统关键路径 -
NoNewPrivileges=true:禁止服务后续通过execve获得新特权(如 setuid 二进制) -
CapabilityBoundingSet=CAP_NET_BIND_SERVICE:按需保留最小能力,去掉CAP_SYS_ADMIN等高危项
这些选项之间无依赖顺序,但缺一不可——PrivateTmp 是入口,其余是守住出口的闸门。


















