当前位置:首页 > 文章列表 > 文章 > linux > systemd ProtectSystem 与 ReadWritePaths 怎么组合

systemd ProtectSystem 与 ReadWritePaths 怎么组合

来源:17golang原创 2026-10-04 11:00:10 0浏览 收藏

ProtectSystem= 与 ReadWritePaths= 的正确组合思路是:先把服务看到的文件系统设成默认只读,再只为业务确实需要的目录恢复写权限。最常用的加固基线是 ProtectSystem=strict,配合少量绝对路径的 ReadWritePaths=;若路径属于状态、日志、缓存或运行时数据,优先改用 StateDirectory=、LogsDirectory=、CacheDirectory=、RuntimeDirectory=。

不要把 ReadWritePaths=/var 当成省事写法。它会让过大的目录重新可写,削弱 strict 的价值。迁移时应从服务真实写入清单出发,尽量把可写范围收敛到单个应用目录。

systemd 官方参考地址:https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html

先看清 ProtectSystem 的三档范围

ProtectSystem= 通过文件系统命名空间改变服务进程看到的挂载属性,不会把主机文件系统永久改成只读。三档配置的覆盖范围不同:

配置服务内变为只读的主要范围适合场景
ProtectSystem=yes/usr 以及引导目录 /boot、/efi低风险起步,不阻止服务修改 /etc 和大部分本地数据
ProtectSystem=full在 yes 的基础上增加 /etc服务只读配置但仍需写入其他业务目录
ProtectSystem=strict几乎整个文件系统层级;/dev、/proc、/sys 作为 API 文件系统子树单独处理默认拒绝写入,再显式放行少量目录

systemd ProtectSystem 三档只读范围

图1:ProtectSystem 三档只读范围,strict 建立最完整的默认只读基线。

strict 并不等于完整沙箱。它主要约束文件系统写入,不能代替普通 Unix 权限、SELinux 或 AppArmor,也不阻止程序连接只读目录中的 Unix socket。若服务仍拥有重新挂载所需的特权,部分限制还可能被撤销,因此高权限服务还应收紧 CAP_SYS_ADMIN 或挂载相关系统调用。

迁移前先列出真实写入点

很多服务启动失败,不是 ProtectSystem=strict 本身有问题,而是旧配置隐含了散落写入:程序目录旁的临时文件、/etc 下的自动生成配置、任意位置的 PID 文件、日志和缓存。迁移前把写入点按用途分组:

  • 持久状态:数据库、队列、索引,通常放到 /var/lib/myapp。
  • 日志:若程序自己写日志文件,通常放到 /var/log/myapp;若写 journal,则未必需要日志目录。
  • 缓存:可重建内容放到 /var/cache/myapp。
  • 运行时数据:socket、PID、临时状态放到 /run/myapp。
  • 配置:通常应只读;如果程序会自动改配置,应重新评估设计,而不是直接放行整个 /etc。

旧服务可能只有以下宽松配置。它依赖用户和文件权限,但没有为服务建立只读文件系统视图:

[Service]
# 旧配置只指定运行用户,文件系统仍有大量潜在写入面
User=myapp
Group=myapp
ExecStart=/usr/local/bin/myapp

新写法:默认只读,只开放可写岛

如果目录已经由软件包或部署工具创建,可以直接用 ReadWritePaths= 放行。路径必须是绝对路径;一个指令可列出多个路径,也可以重复出现。

[Service]
# 先把服务视角下的整个文件系统设为默认只读
ProtectSystem=strict

# 只恢复应用状态和自有日志目录的写权限
ReadWritePaths=/var/lib/myapp /var/log/myapp

# 前缀 - 表示该路径不存在时不让服务启动失败
ReadWritePaths=-/run/myapp

ReadWritePaths= 只改变服务命名空间中的挂载可写性,不会创建目录,也不会绕过文件所有者、组、模式位、ACL 或强制访问控制。目录存在但归 root 所有时,以 User=myapp 运行的进程仍可能收到 Permission denied。

ProtectSystem strict 中的可写岛

图2:默认只读文件系统中的可写岛,放行路径仍受 Unix 权限和底层挂载状态约束。

若这些目录完全属于当前服务,更推荐由 systemd 创建和维护。下面的配置会为服务准备对应用途的目录,并让它们从 ProtectSystem=strict 的只读效果中排除:

[Service]
# 以严格只读作为文件系统基线
ProtectSystem=strict

# 由 systemd 创建目录并按服务身份管理所有权
StateDirectory=myapp
LogsDirectory=myapp
CacheDirectory=myapp
RuntimeDirectory=myapp

# 服务只写上述托管目录,不再单独放行整个 /var
User=myapp
Group=myapp
ExecStart=/usr/local/bin/myapp

对应的常见路径分别是 /var/lib/myapp、/var/log/myapp、/var/cache/myapp 和 /run/myapp。这种写法把目录创建、所有权和生命周期放回 unit 配置,更适合新服务。只有现有目录布局无法调整,或必须写入某个外部挂载点时,再使用 ReadWritePaths=。

ReadWritePaths 不能覆盖哪些限制

组合配置时最容易误判的是“写入放行”并不等于“保证可写”。至少还有四层约束:

  1. 底层超级块只读:如果文件系统本身以只读方式挂载,ReadWritePaths= 不能把它变成可写。
  2. Unix 权限:服务用户仍要拥有目标目录的写权限。放行挂载属性不会自动执行 chown。
  3. 其他沙箱选项:InaccessiblePaths=、ProtectHome=、绑定挂载等设置可能对同一路径施加更严格限制。
  4. 命名空间能力:相关限制依赖文件系统命名空间支持;某些容器或用户级服务环境可能无法按系统服务的方式生效。

特别注意:不要先用 InaccessiblePaths=/data 隐藏整棵树,再期望用 ReadWritePaths=/data/myapp 恢复其中子目录。不可访问映射比只读更严格,不能靠内部可写路径重新打开。需要“隐藏大部分、显示少量”时,应重新设计目录或使用更适合的绑定挂载方案。

用 drop-in 完成低风险迁移

不要直接改发行版提供的 unit 文件。使用 drop-in 可以保留升级兼容性,也便于快速回滚。

# 创建或编辑服务的本地覆盖配置
sudo systemctl edit myapp.service

# 检查合并后的 unit 内容,确认没有被其他 drop-in 覆盖
systemctl cat myapp.service

先加入 ProtectSystem=full 观察服务是否还在修改 /etc,再迁移到 strict 只是可选的运维策略,不是必须步骤。如果已经有完整的写入清单,可以直接启用 strict。关键是每次只开放已确认的业务目录,不要在失败后不断扩大白名单。

修改后先做 unit 语法检查,再重载和重启:

# 验证 unit 文件语法与引用关系,不启动服务
sudo systemd-analyze verify /etc/systemd/system/myapp.service

# 让 PID 1 重新读取配置并重启目标服务
sudo systemctl daemon-reload
sudo systemctl restart myapp.service

# 查看启动失败原因和本次启动日志
sudo systemctl status myapp.service --no-pager
sudo journalctl -u myapp.service -b --no-pager

如果主 unit 位于发行版目录,systemd-analyze verify 也可以指向完整 unit 文件,随后通过 systemctl cat 检查实际合并结果。验证命令能发现语法和依赖问题,但不会替代真实写入测试。

同时做正向和负向写入测试

只确认服务能启动不够。至少要验证“允许写的目录能写”和“未允许的目录确实不能写”。可以用临时 unit 先验证系统对这组属性的支持:

# 准备允许写入的测试目录,普通权限仍需正确
sudo install -d -m 0755 /tmp/myapp-write

# /etc 写入应失败,而白名单目录写入应成功
sudo systemd-run --wait --pipe \
  -p ProtectSystem=strict \
  -p ReadWritePaths=/tmp/myapp-write \
  /bin/sh -c 'touch /etc/myapp-should-fail; touch /tmp/myapp-write/myapp-should-pass'

对正式服务,最好让应用自己的健康检查覆盖数据库落盘、日志轮转、socket 重建和升级后的首次写入。还要确认它不能修改可执行文件、系统配置和其他服务的数据目录。systemd-analyze security myapp.service 可以帮助观察整体暴露面,但评分不是功能测试,也不是安全保证。

迁移清单

  • 服务写入点是否已经按状态、日志、缓存和运行时数据分类?
  • 是否优先使用 StateDirectory= 等托管目录,而不是放行整个 /var?
  • 现有路径是否存在,服务用户是否确实拥有普通写权限?
  • 目标文件系统的底层挂载是否可写?
  • ProtectHome=、InaccessiblePaths= 等设置是否与放行路径冲突?
  • 是否完成 unit 语法检查、启动测试、允许路径写入测试和禁止路径负向测试?
  • 服务是否仍保留能够撤销挂载限制的高权限能力?
  • 回滚时是否只需删除 drop-in 并执行 daemon-reload 与 restart?

最终判断标准很简单:服务的正常数据、日志和运行时文件都能写入明确的自有目录;除此之外的系统路径在服务命名空间中保持只读;即使某个业务进程被利用,攻击者也不能轻易把改动扩散到系统配置、程序文件或其他服务的数据。

常见问题

ReadWritePaths 会自动创建目录吗?

不会。目录可能不存在时可用 - 前缀忽略缺失,但真正需要长期使用的目录更适合用 StateDirectory=、LogsDirectory= 等指令创建。

配置 ReadWritePaths 后为什么仍然 Permission denied?

先检查底层文件系统是否只读,再检查服务用户、目录所有权、模式位、ACL、SELinux 或 AppArmor。该指令只恢复命名空间中的可写挂载属性,不会绕过这些权限。

ProtectSystem=strict 后 /tmp 一定只读吗?

通常 strict 会覆盖整个层级,但若同时启用 PrivateTmp=,服务会得到独立且可写的 /tmp 和 /var/tmp。因此要根据完整 unit 配置判断,而不是只看一行。

用户级 service 能直接使用这些配置吗?

依赖文件系统命名空间的沙箱功能在用户管理器中通常受限;在支持非特权用户命名空间且配合 PrivateUsers=true 时,部分设置才可能工作。生产加固前应在目标发行版和运行环境中实测。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Python sqlite3.Connection.blobopen 怎么增量读写大字段Python sqlite3.Connection.blobopen 怎么增量读写大字段
上一篇
Python sqlite3.Connection.blobopen 怎么增量读写大字段
kazumi开源项目怎么参与?主仓库、规则仓库与版本支持说明
下一篇
kazumi开源项目怎么参与?主仓库、规则仓库与版本支持说明
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    325次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    382次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    376次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    342次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    167次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码