Linux 服务器 /tmp 目录:使用机制与安全加固
本文基于 Kylin V10 SP3 / RHEL 8 系列系统实践整理,适用于云服务器和 Kubernetes 节点场景。
1. /tmp 的两种存储后端
很多人以为 /tmp 就是磁盘上的一个目录,但实际上它可能运行在两种完全不同的存储后端上。
基于磁盘(默认目录)
如果 /tmp 没有被单独挂载,它就是根文件系统 / 下的一个普通目录,数据存储在磁盘上,不占用内存。
基于内存(tmpfs)
如果 /tmp 被挂载为 tmpfs,它使用物理内存(RAM 不足时会使用 swap)。tmpfs 默认最大可使用 50% 的系统内存,但它是按需分配的------如果 /tmp 里只有 10MB 文件,就只占用 10MB 内存。
用以下命令确认你的系统使用的是哪种方式:
bash
df -hT /tmp
findmnt /tmp
如果输出中 FSTYPE 是 tmpfs,说明是内存模式;如果和根分区一样(如 xfs、ext4),说明是磁盘模式。
2. systemd 的隐式挂载行为
在 RHEL 8 / Kylin V10 系统上,即使 /etc/fstab 中没有 /tmp 的条目,systemd 也可能自动将 /tmp 挂载为 tmpfs。这是通过 tmp.mount 单元实现的。
bash
systemctl status tmp.mount
你可能会看到类似如下输出:
● tmp.mount - Temporary Directory (/tmp)
Loaded: loaded (/usr/lib/systemd/system/tmp.mount; disabled; vendor preset: disabled)
Active: active (mounted)
注意 disabled 但 active------说明这个单元没有被显式启用,但 systemd 的默认配置仍然触发了挂载。这种"隐式行为"很容易被忽视。
3. 对 Kubernetes 节点的影响
在 K8s 节点上,如果 /tmp 使用 tmpfs 且没有设置大小限制,需要特别注意:
- 内存竞争 :kubelet、系统服务和 Pod 都在争用内存。如果某个进程向
/tmp写入大量临时文件,会直接挤占可用内存,可能触发 OOM Killer。 - 容器隔离 :好消息是,容器内部有自己独立的
/tmp,不会使用宿主机的/tmp。但宿主机上的系统进程仍然会使用它。
建议方案:
- 如果内存充足,保留
tmpfs但设置大小限制(如size=512M) - 如果内存紧张,改为磁盘模式,避免
/tmp与 Pod 争用内存
4. CIS 安全加固要求
CIS Benchmark(国际互联网安全中心基准)是业界广泛认可的安全加固标准,其中对 /tmp 的要求主要涉及三个挂载选项:
| 选项 | 作用 |
|---|---|
noexec |
禁止执行 /tmp 中的任何程序或脚本 |
nosuid |
忽略 /tmp 中文件的 SUID/SGID 位 |
nodev |
禁止在 /tmp 中创建设备文件 |
为什么需要 noexec?
/tmp 是所有用户都可以写入的目录(权限为 1777)。攻击者常见的手法是:将恶意可执行文件上传到 /tmp,然后执行它。noexec 在内核层面阻止了这种行为。
noexec 和 chmod 去掉 x 权限是一回事吗?
不是。这是两个完全不同层面的机制:
chmod -x /tmp(目录权限) :去掉目录的x权限意味着用户无法"进入"该目录(无法cd /tmp),这会导致系统大面积故障,绝对不能这么做。noexec(挂载选项) :在内核层面禁止执行该挂载点上的任何文件。文件的读写不受影响,只是不能作为程序运行。即使文件本身有chmod +x的执行权限,内核也会拒绝执行。
bash
# noexec 效果演示
cp /usr/bin/ls /tmp/ls
chmod +x /tmp/ls
/tmp/ls
# 结果:Permission denied(内核拒绝执行)
5. 加固操作步骤
检查当前状态
bash
# 查看 /tmp 挂载信息
findmnt /tmp
# 查看当前挂载选项
mount | grep /tmp
方案一:保留 tmpfs + 安全选项 + 大小限制
在 /etc/fstab 中添加:
tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev,size=512M 0 0
立即生效(无需重启):
bash
mount -o remount,noexec,nosuid,nodev /tmp
方案二:磁盘模式 --- 独立分区(推荐)
如果选择将 /tmp 放在磁盘上,强烈建议使用独立分区而不是让它留在根文件系统中。
原因很简单:/tmp 是所有用户可写的目录,如果没有隔离,任何用户或失控进程都可以向 /tmp 写入大量数据,直到撑满整个根分区。根分区一旦满了,后果很严重------系统日志无法写入、服务无法启动、甚至无法通过 SSH 登录。这在生产环境中是非常危险的故障模式。
独立分区的好处:
- 空间隔离 :
/tmp写满只影响/tmp自身,不会波及根分区和其他关键目录(如/var/log、/etc) - 容量可控 :分区大小在创建时确定,天然限制了
/tmp的最大占用 - 安全挂载选项 :独立分区可以直接在
/etc/fstab中设置noexec,nosuid,nodev
操作步骤:
如果是新装系统,在安装时规划分区,为 /tmp 分配 1-2GB 的独立分区即可。
如果是已有系统,可以使用 LVM 创建新的逻辑卷(前提是 VG 中有剩余空间):
bash
# 1. 禁用 systemd 的 tmpfs 挂载
systemctl disable tmp.mount
systemctl mask tmp.mount
# 2. 创建逻辑卷(假设 VG 名为 klas,分配 2GB)
lvcreate -L 2G -n tmp klas
# 3. 格式化
mkfs.xfs /dev/klas/tmp
# 4. 添加到 /etc/fstab
echo '/dev/mapper/klas-tmp /tmp xfs defaults,noexec,nosuid,nodev 0 0' >> /etc/fstab
# 5. 备份当前 /tmp 内容,挂载新分区
cp -a /tmp /tmp.bak
mount /tmp
chmod 1777 /tmp
cp -a /tmp.bak/* /tmp/ 2>/dev/null; rm -rf /tmp.bak
方案三:磁盘模式 --- bind mount(无法划分独立分区时的替代方案)
如果系统已经部署完毕且 VG 没有剩余空间,无法创建独立分区,可以退而求其次使用 bind mount。这种方式可以添加安全挂载选项,但无法实现空间隔离 ------/tmp 仍然和根分区共享磁盘空间。
bash
# 禁用 systemd 的 tmpfs 挂载
systemctl disable tmp.mount
systemctl mask tmp.mount
在 /etc/fstab 中添加:
/tmp /tmp none bind,noexec,nosuid,nodev 0 0
使用 bind mount 时,建议配合 磁盘配额(quota) 或定期清理计划任务来防止 /tmp 无限增长:
bash
# 添加定时清理(例如清理超过 7 天的文件)
echo 'systemd-tmpfiles --clean' > /etc/cron.daily/tmp-cleanup
chmod +x /etc/cron.daily/tmp-cleanup
验证
bash
findmnt /tmp
# 确认 OPTIONS 中包含 rw,nosuid,nodev,noexec
6. 注意事项
兼容性影响
某些软件会在 /tmp 中写入并执行临时文件,添加 noexec 后可能受到影响:
- 部分软件安装程序(如某些 RPM 的
%post脚本) pip install编译 C 扩展时- 某些 Java 应用的临时文件
rpmbuild构建过程
遇到问题时可以临时放开限制:
bash
mount -o remount,exec /tmp
# 完成操作后恢复
mount -o remount,noexec /tmp
K8s 节点特别说明
对于 K8s 节点,noexec 通常不会影响容器化工作负载,因为容器使用自己的文件系统。但如果你在宿主机上直接运行构建任务或安装软件,需要留意兼容性。
7. 总结
| 场景 | 推荐方案 |
|---|---|
| 内存充足的通用服务器 | tmpfs + noexec,nosuid,nodev + size= 限制 |
| 内存紧张的 K8s 节点 | 独立分区 + noexec,nosuid,nodev(首选) |
| 已部署系统,无法新建分区 | bind mount + noexec,nosuid,nodev + 定期清理 |
| 开发/构建服务器 | 独立分区或 bind mount + nosuid,nodev(考虑不加 noexec) |
无论选择哪种方案,核心原则有两个:一是让 /tmp 的配置显式可控 ------不要依赖 systemd 的隐式行为,在 /etc/fstab 中明确定义,确保重启后配置一致;二是空间隔离 ------尽量避免 /tmp 与根分区共享空间,防止 /tmp 被写满后影响整个系统。