ProtectHome=true会彻底屏蔽/home、/root、/run/user,使其对服务进程完全不可见、不可访问;它通过mount namespace隐藏目录树,而非仅设为只读,必须配合NoNewPrivileges=true和CapabilityBoundingSet限制才能真正生效。

ProtectHome=true 会彻底屏蔽 /home、/root、/run/user
设置 ProtectHome=true 后,服务进程对这三个路径的任何访问(ls、open、stat)都会直接返回 Permission denied,不是只读,是“不可见”。它不依赖文件权限或用户身份,而是通过 mount namespace + bind mount 隐藏整个目录树。这意味着即使服务以 root 运行,也拿不到 /home/alice/.ssh/id_rsa —— 它根本看不到 /home 这个目录存在。
ProtectHome=read-only 和 ProtectHome=true 的区别
两者行为完全不同:
-
ProtectHome=read-only:把/home、/root、/run/user挂为只读,进程能看到目录结构,但写操作失败;仍可能读取敏感配置或密钥文件 -
ProtectHome=true:在 mount namespace 中将这些路径 bind mount 到空目录或/dev/null,进程ls /home直接报错,连目录列表都拿不到
生产环境应无条件选 true。只有调试时临时用 read-only 观察是否因读取家目录内容而崩溃。
常见错误现象和兼容性问题
启用 ProtectHome=true 后,服务启动失败或日志中出现以下错误,基本可锁定原因:
-
No such file or directory:程序硬编码了~/.config/myapp/或$HOME/.cache路径,找不到$HOME -
Permission denied在 open() / stat() 系统调用处:比如尝试读/root/.bashrc或检查/home/*/.ssh -
getpwuid_r failed:C 库函数试图从/etc/passwd解析用户主目录,但后续访问$HOME失败导致逻辑中断
解决方法不是关掉 ProtectHome,而是:
- 改代码或配置,用绝对路径替代
~或$HOME(如/var/lib/myapp/config) - 若必须用用户配置,改用
DynamicUser=yes+StateDirectory=myapp,让 systemd 自动创建并管理服务专属状态目录 - 极少数场景需挂入特定子目录(不推荐),可用
BindPaths=/etc/skel/.profile:/home/appuser/.profile:ro,但必须确认该文件不包含敏感信息
必须搭配 NoNewPrivileges=true 才真正生效
ProtectHome=true 单独启用是脆弱的。如果服务二进制本身带 setuid 或拥有 CAP_SYS_ADMIN,它可以在运行时重新 mount 或 chroot 绕过限制。所以务必同时设置:
NoNewPrivileges=true —— 禁用所有 execve 提权路径;CapabilityBoundingSet=~CAP_SYS_ADMIN CAP_SETUID CAP_SETGID —— 显式移除高危能力。
否则,攻击者利用一个内存破坏漏洞,就能轻易逃逸出 ProtectHome 的沙箱。这点常被忽略,也是线上服务配置中最容易漏掉的一环。


















