在 systemd 服务单元文件的 [Service] 段添加 NoNewPrivileges=yes 能阻止进程通过 setuid 或文件能力提权。该配置适合不需要特权 escalation 的用户态服务,开启后会导致依赖 setuid 二进制文件的程序运行失败。
先说结论:NoNewPrivileges 是 systemd 提供的安全加固选项,能有效限制服务进程的权限扩散风险。
- 先判断:确认服务是否依赖 setuid 二进制文件或需要绑定 1024 以下端口。
- 优先做:在 Unit 文件的 [Service] 段落中设置 NoNewPrivileges=yes。
- 再验证:通过查看进程状态文件确认 NoNewPrivs 标志已生效。
命令速用版
直接编辑 systemd 单元文件,在 [Service] 段加入配置项,无需额外脚本。
[Service]
NoNewPrivileges=yes保存文件后执行 systemctl daemon-reload 使配置生效。
为什么会这样
该参数映射到 Linux 内核的 PR_SET_NO_NEW_PRIVS 标志,强制进程无法获取新权限。
当 NoNewPrivileges 被启用时,当前进程及其子进程无法通过 execve 调用获得新的特权。这包括 setuid 位、setgid 位、文件能力(file capabilities)以及文件系统能力。即使攻击者利用了漏洞执行了恶意程序,该标志也能阻止恶意程序获得 root 权限或其他高阶权限,从而限制权限提升攻击面。
分步处理
按以下步骤为指定服务启用 NoNewPrivileges,每步完成后需确认状态。
步骤 1:编辑单元文件
使用 systemctl edit 命令创建覆盖配置,避免直接修改原始文件。
systemctl edit <服务名>在编辑器中输入以下内容:
[Service]
NoNewPrivileges=yes步骤 2:重载配置
通知 systemd 重新加载单元文件配置。
systemctl daemon-reload步骤 3:重启服务
重启目标服务以应用新的安全策略。
systemctl restart <服务名>回滚提醒:如果服务启动失败,立即执行 systemctl edit <服务名> 删除该配置行,然后重载并重启。
怎么验证是否生效
通过检查进程状态文件中的 NoNewPrivs 字段确认配置已应用。
方法 1:使用 systemctl show
systemctl show <服务名> | grep NoNewPrivileges输出应显示 NoNewPrivileges=yes。
方法 2:检查 proc 状态
获取服务主进程 PID,查看 /proc/
cat /proc/$(systemctl show <服务名> -p MainPID `--value`)/status | grep NoNewPrivs输出应显示 NoNewPrivs: 1。如果显示 0,则说明未生效。
常见坑
某些合法功能会被该参数拦截,需提前评估业务兼容性。
- 端口绑定限制:普通用户进程无法绑定 1024 以下的特权端口,除非配合 AmbientCapabilities 使用 CAP_NET_BIND_SERVICE。
- Setuid 程序失效:服务调用的外部命令如果依赖 setuid 提权(如 ping、passwd 等),执行时会失败。
- 容器环境冲突:在部分容器运行时中,该参数可能与容器安全策略冲突,导致容器启动异常。
常见问题
Root 用户运行的服务需要开启吗?
需要,Root 用户运行的服务开启后能防止子进程通过 setuid 获取额外能力。
即使主进程是 root,限制子进程提权也能减少漏洞被利用后的危害范围。
开启后会影响性能吗?
公开资料中没有看到可靠的量化数据表明该参数会带来显著性能损耗。
该标志仅在内核权限检查路径增加一个判断位,对常规 I/O 和计算无影响。
子进程会继承这个设置吗?
会,该标志在 execve 调用期间会被子进程继承。
一旦主进程设置了 NoNewPrivileges,所有后续衍生的子进程都无法获得新权限。
参考来源
- freedesktop.org, systemd.exec — Execution environment configuration, https://www.freedesktop.org/software/systemd/man/systemd.exec.html
- Linux Kernel Documentation, prctl(2), https://man7.org/linux/man-pages/man2/prctl.2.html