chattr +a 是唯一能真正实现“只追加”的机制,它强制文件仅允许 o_append 方式写入,禁止覆盖、截断等操作,但需文件系统支持且需配合 logrotate 等工具适配。

chattr +a 是唯一能真正实现“只追加”的机制
标准 chmod 权限做不到“只追加”,因为 POSIX 的 w 位是二元的:有则允许任意写(>、>>、truncate 全开;无则全部拒绝,连 >> 都失败)。必须用内核级扩展属性 chattr +a。
它强制文件只能以 O_APPEND 方式打开,即仅允许追加,禁止覆盖、截断、重命名、删除——但注意:删除操作实际作用于父目录,所以 rm 是否失败取决于父目录权限,不是文件本身。
-
sudo chattr +a /var/log/app.log—— 必须 root 执行,普通用户无法设置 - 验证是否生效:
lsattr /var/log/app.log→ 输出中应含a(如-----a-------e--) - 若报
Operation not supported,说明文件系统不支持(如tmpfs、nfs、btrfs默认禁用+a) - 空文件可能被拒绝设置:
touch写入一点内容后再chattr +a更稳妥
哪些操作会被 +a 拦住,哪些仍能成功
chattr +a 拦的是系统调用层面的写方式,不是命令名。关键看底层是否用了 O_TRUNC 或非 O_APPEND 的 O_RDWR。
- ❌ 失败(返回
Operation not permitted):echo "x" > file、cp new.log file、vim file(保存时)、truncate -s 0 file、rm file(若父目录也设了+a或+i) - ✅ 成功:
echo "x" >> file、logger "msg"、rsyslog写入、cat more.log >> file、tail -f file(读不受影响) - ⚠️ 注意:
dd conv=notrunc可绕过,+a不防 root 级暴力覆盖
logrotate 和 systemd-journald 场景下的典型崩点
默认 logrotate 用 rename() 替换旧日志,而 +a 会拦截该系统调用;systemd-journald 在 /var/log/journal/ 下自由创建轮转文件,+a 目录会导致其停写。
- logrotate 解法:禁用
copytruncate,改用create+postrotate中手动chattr +a新文件,并先chattr -a老文件再mv - 避免对整个
/var/log/journal设+a,journald 会直接报Failed to rotate logs - 若需保护整个日志目录,可对父目录设
chattr +a /var/log/myapp(此时rm app.log失败),但touch、cp新文件仍被允许
为什么 chmod a-w + chattr +a 要一起用
单独 chattr +a 已能阻止覆盖,但保留 w 权限容易误导人——比如有人看到自己有 w 就尝试 echo > file,结果失败还纳闷。加上 chmod a-w 是辅助性安全层。
-
chmod a-w filename移除所有写权限(不影响>>,因+a优先级更高) - 不建议只用
chmod u-w或g-w,除非你明确知道谁该写、谁不该写 - 切记:
chmod a-w单独使用会让>>也失败,必须和chattr +a配合才有效 - 恢复时顺序无关:先
chattr -a或先chmod a+w都行
真正难搞的不是设 +a,而是确认文件系统支持、父目录权限协同、以及轮转工具兼容性——漏掉任一环,表面看着生效,实际运行时崩得悄无声息。











