跳转至

15 · 安全边界与恢复

Omarchy 有意给予脚本很大能力:安装包、修改 /etc、控制会话、执行插件。安全性不来自“没有危险能力”,而来自把授权、信任和可恢复性放在明确边界上。

权限边界

flowchart TD
    Action[用户动作] --> Need{需要 root?}
    Need -->|否| User[普通用户命令]
    Need -->|是/有终端| Sudo[sudo + 可见认证]
    Need -->|是/GUI| Polkit[pkexec / Shell polkit agent]
    Sudo --> Root[root entrypoint 再检查 EUID]
    Polkit --> Root
    Root --> System[/etc · pacman · systemd system]

CLI 的 requires-sudo 元数据只是帮助提示;真正边界由 sudo/pkexec 和目标脚本 EUID 检查共同执行。反方向同样重要:用户 provisioning 明确拒绝 root,防止 home 文件归属污染。

信任边界一览

输入 默认信任 防线
package 中的脚本/default 发行方代码 pacman/keyring、固定所有权
用户 hook/Lua 用户主动授权代码 只在用户配置路径、明确执行点
第三方 Shell 插件 未信任直到 enable 保留 ID、路径校验、禁 symlink、确认/diff
远程主题 只当数据 staging allow/deny、禁代码文件、不跟 symlink
menu action 配置即代码 shipped/user 路径分离,guard 不作为权限
migration 发行方增量代码 顺序、严格 Bash、成功 marker

主题与插件采用不同策略是合理的:插件的目的就是执行 QML 代码,所以需要显式 enable;主题的目的只是视觉数据,因此不应获得代码执行能力。

更新前快照

omarchy-snapshot create 枚举所有 Snapper config,为每个创建带当前 Omarchy 版本描述的 number snapshot,再执行 cleanup。若 Snapper 安装却没有任何 config,它明确失败,而不是安静返回成功制造“已经可恢复”的假象。

restore 交给 limine-snapper-restore,把恢复入口连接到 boot 环境。快照是更新前的重要恢复点,但不是数据库式原子事务:home、外部磁盘、固件和运行中云服务仍可能在覆盖范围外。

Factory reset:两阶段破坏性流程

Factory reset 只支持由 Quattro installer 创建了 @factory Btrfs 基线的机器;从旧版升级到 Quattro 的机器没有可信 factory snapshot,当前实现会拒绝。

阶段一:运行系统中 staging

  1. 强确认,要求输入 reset;
  2. 验证 Btrfs @ 与 @factory;
  3. 克隆 fresh writable root 为 next;
  4. 准备 provisioning units/markers;
  5. 加密安装临时 re-key 并嵌入一次性自动解锁 key;
  6. 重建 UKI/boot entry;
  7. 验证 Limine 中每个 UKI 的 BLAKE2 hash;
  8. 只在所有 staging 完成后安排下次启动切换。

阶段二:下次 boot 早期清理

finish service 在 home.mount / var-log.mount 之前运行:删除旧 root,重建空 @home 与 @log,修复 /.snapshots 子卷,trim,最后清 pending marker 并进入新 owner provisioning。

stateDiagram-v2
    [*] --> Confirmed
    Confirmed --> FactoryClone
    FactoryClone --> BootVerified
    BootVerified --> WipePending
    WipePending --> Reboot
    Reboot --> EarlyFinish
    EarlyFinish --> HomeLogRecreated
    HomeLogRecreated --> Provisioning
    Provisioning --> [*]
    EarlyFinish --> RetryNextBoot: 失败保留 wipe-pending

失败时保留 wipe-pending,阻止 provisioning 在半清理机器上创建新用户。这里 marker 是安全闸门,不只是进度记录。

Factory reset 不等于取证级擦除

未加密磁盘上的删除与 fstrim 不能保证不可恢复;LUKS 改 passphrase 也不更换 volume key。源码明确建议高保证场景使用 blkdiscard + 重装或 cryptsetup reencrypt。

机器身份也属于敏感状态

reset 不只删 home,还处理:

  • /etc/ssh/ssh_host_*;
  • NetworkManager connections、iwd、Tailscale;
  • fingerprint enrolments;
  • SDDM autologin/state;
  • machine-id;
  • 旧 boot identity 与 UKI hash。

卖出机器时只删账号会留下网络凭据、host keys 和设备身份,这就是系统级 reset 必须比 rm -rf /home 更广的原因。

锁屏与 polkit 为什么在 Shell

Lock view 与 polkit agent 是 first-party service plugins,启动即加载。它们提供 UI,但认证事实仍交给 PAM/polkit。Shell 不应自行实现密码验证或把认证 token 持久化。

睡眠锁由 user systemd 服务配合会话状态,解决“合盖/睡眠竞态中 UI 尚未锁定”的时序,而不是仅依靠按键调用锁屏。

故障隔离的共同手法

  • update/theme/shell restart 使用互斥锁;
  • staging 完整后再原子切换;
  • marker 只在成功后写;
  • 第三方 namespace 不能覆盖 first-party;
  • package update 前创建快照;
  • reboot/cutover 前验证启动参数和 UKI hash;
  • 破坏性跨 boot 流程失败时保持 pending,以便重试而非继续。

关键源码索引

下一章分析风险最高也最完整的一次状态转换:16 · Quattro 一次性升级器。