systemd通过挂载命名空间隔离实现服务目录读写限制:先用protectsystem=strict等参数锁定关键路径为只读,再用readwritedirectories=显式放行业务所需目录,最后以inaccessiblepaths=屏蔽敏感路径,配合验证确保生效。

用 systemd 限制服务对特定目录的读写,核心不是改文件权限,而是让服务进程在自己的挂载命名空间里“根本看不到可写的路径”——即通过只读挂载、路径屏蔽和显式放行三步协同实现。
锁定关键系统目录为只读
在服务单元文件的 [Service] 段中添加:
-
ProtectSystem=strict:将
/usr、/boot、/etc在服务视角下全部 bind-mount 为只读。即使进程是 root,也无法修改配置或二进制文件。 -
ProtectHome=read-only:把
/home、/root、/run/user设为只读,防止密钥、SSH 配置被读取或覆盖。 -
ProtectKernelTunables=yes 和 ProtectKernelModules=yes:禁止写
/proc/sys和/sys,堵住 sysctl 提权或模块加载路径。
按需开放业务必需的可写目录
全局只读(ReadOnlyFileSystem=yes)基本不可用,应采用“默认受限 + 显式放行”策略:
- 用 ReadWriteDirectories= 列出真正需要写入的路径,例如:
ReadWriteDirectories=/var/lib/myapp /run/myapp /var/log/myapp
它会检查父目录权限,比RWPaths=更安全。 - 临时数据优先用 TemporaryFileSystem=,例如:
TemporaryFileSystem=/tmp:mode=0755
挂载 tmpfs,重启即清空,不落盘也不绕过只读控制。 - 避免直接对单个配置文件放开写权限(如
RWPaths=/etc/myapp.conf),这可能因父目录可写而失效;目录级控制更可靠。
彻底屏蔽不需要访问的路径
有些服务会尝试绕过文件系统层直连设备或读敏感伪文件,需额外隔离:
-
InaccessiblePaths= 将路径挂载为空目录,服务调用
open("/dev/sdb", ...)直接返回ENOENT,例如:InaccessiblePaths=/dev/sdb /proc/kcore /sys/firmware - 若服务确实需访问某设备(如备份工具用
/dev/sg0),配合 DeviceAllow= 显式授权,并启用 RestrictDevices=true 关闭自动暴露其他设备。 -
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6:禁用
AF_NETLINK、AF_PACKET等高风险协议族,防通过 netlink 查询块设备状态。
验证是否真正生效
配置后必须验证,不能只看参数是否写上:
- 检查挂载视图:
sudo nsenter -t $(pgrep -f 'myapp') -m -p findmnt | grep "ro,.*\|/usr\|/etc"
确认关键路径显示ro(只读)且挂载类型为bind。 - 测试写入是否失败:
sudo nsenter -t $(pgrep -f 'myapp') -m -p sh -c "echo test > /etc/test.txt 2>&1"
应返回Permission denied或Read-only file system。 - 确认屏蔽路径不可见:
sudo nsenter -t $(pgrep -f 'myapp') -m -p ls /dev/sdb
应提示No such file or directory。











