protectsystem=strict 是生产环境首选配置,它通过只读 bind-mount /usr、/boot、/etc 实现硬隔离,需配合 readwritedirectories 显式声明可写路径,否则服务可能因写入失败而退出。

ProtectSystem=strict 是最常用且推荐的配置方式
它会把 /usr、/boot、/etc 三个路径以只读方式 bind-mount 到服务的 mount namespace 中,进程即使以 root 身份运行,也无法修改二进制、内核镜像或系统配置文件。这不是靠文件权限控制,而是通过命名空间挂载实现的硬隔离。
直接在服务单元文件的 [Service] 段添加这一行即可:
ProtectSystem=strict
无需额外路径声明或重启策略配合,systemd 启动服务时自动生效。
常见错误现象包括:
- 服务启动失败,日志中出现
Permission denied在写入/etc/myapp/config时——说明你没放开可写路径 - 程序调用
system("cp /tmp/patch /usr/bin/app")静默失败——这是预期行为,不是 bug -
ls -l /usr/bin看起来正常,但echo test > /usr/bin/test报错——因为只读挂载已生效
ProtectSystem=full 和 ProtectSystem=yes 的区别要搞清
三者行为差异明显,不能混用:
-
ProtectSystem=strict:只读挂载/usr、/boot、/etc—— 生产环境首选 -
ProtectSystem=full:在 strict 基础上,额外将/run、/var/run、/var/lock也设为只读 —— 多数服务依赖/run存 socket 或 pid,容易启动失败 -
ProtectSystem=yes:等价于ProtectSystem=full,是别名,不建议使用,语义模糊
如果你的服务需要往 /run/myapp/ 写 socket,就绝不能用 full 或 yes;而 strict 完全不影响 /run 的读写自由。
只读保护必须搭配显式可写路径才真正可用
ProtectSystem=strict 是“默认禁止”,不是“全部禁止”。它不阻止你声明哪些目录允许写入,但也不会自动帮你开白名单。一旦启用,所有未被明确放行的路径都继承只读限制(包括父目录)。
正确做法是组合使用:
- 用
ReadWriteDirectories=显式声明业务需要持久化或临时写入的目录,例如:ReadWriteDirectories=/var/lib/myapp /run/myapp /var/log/myapp
- 避免用
ReadWritePaths=直接指定文件(如/etc/myapp.conf),它不检查父目录是否可写,可能绕过ProtectSystem的保护 - 对缓存类路径,优先用
TemporaryFileSystem=/tmp:mode=0755或/var/run:rw挂 tmpfs,重启即清空
漏掉 ReadWriteDirectories 是服务秒退最常见的原因之一——程序尝试写日志或创建 pid 文件失败后直接退出,systemd 认为启动失败,状态迅速变为 inactive (dead)。
和 ProtectHome、PrivateTmp 组合使用时要注意顺序与边界
这三个选项各自作用域不同,但共用同一套 mount namespace 机制,叠加后效果不是简单相加,而是按挂载顺序叠加覆盖:
-
ProtectHome=read-only会影响/home、/root、/run/user—— 和ProtectSystem=strict不冲突,因为目标路径无重叠 -
PrivateTmp=yes会重新挂载/tmp和/var/tmp为 tmpfs —— 这两个路径不在ProtectSystem保护范围内,所以无需额外放行 - 但如果你同时用了
ProtectHome=true,它会隐藏/run/user,而某些服务(如 dbus 用户会话代理)可能依赖该路径,此时需评估是否真有必要开启true模式
最易被忽略的一点是:ProtectSystem=strict 对 /etc 的只读挂载,会使得 /etc/alternatives、/etc/ca-certificates 等符号链接目标不可写——如果服务内部执行了 update-ca-certificates 类操作,依然会失败。这类逻辑应移出服务进程,改由部署脚本或 systemd generator 处理。











