// 安全研究 · 2025-04-24

doas:OpenBSD 的极简权限提升工具

一、什么是 doas

doas(全称为 dedicated openbsd application subexecutor)是一个类 Unix 系统中的权限提升工具,由 OpenBSD 开发者 Ted Unangst 于 2015 年创建,随 OpenBSD 5.8 版本首次发布。它的设计目标是替代历史悠久但日益臃肿的 sudo,在保留核心功能的前提下,以极简的代码和配置实现同样的权限委托能力。

与 sudo 相比,doas 的核心优势在于简洁性和安全性。sudo 的代码库超过 20 万行 C 代码,包含插件架构、LDAP 集成、主机感知配置等大量高级功能;而 doas 的核心代码仅约 500 行,专注于“让普通用户以另一个用户身份运行命令”这一单一任务。 这种“做减法”的设计哲学直接来源于 OpenBSD 项目一贯的安全理念:更少的代码意味着更少的 bug,更小的攻击面意味着更高的安全性。

doas 与 sudo 架构对比

flowchart TB
    subgraph SUDO["sudo 架构"]
        S1["sudoers 文件<br/>复杂语法"]
        S2["插件系统<br/>LDAP/SSSD"]
        S3["日志审计<br/>visudo 验证"]
        S4["代码量: ~200,000+ 行"]
        S1 --> S2 --> S3 --> S4
    end

    subgraph DOAS["doas 架构"]
        D1["doas.conf<br/>简洁规则"]
        D2["无插件<br/>纯 C 实现"]
        D3["syslog 日志<br/>-C 语法检查"]
        D4["代码量: ~500 行"]
        D1 --> D2 --> D3 --> D4
    end

    SUDO -->|"功能重叠 95%"| DOAS
    DOAS -->|"攻击面更小"| SUDO

二、诞生背景与设计哲学

2.1 为什么需要 doas

Ted Unangst 创建 doas 的动机源于对 sudo 的复杂性和默认配置的不满。sudo 的配置文件 sudoers 语法复杂,即便是经验丰富的系统管理员也时常需要查阅文档;而 sudo 的默认配置在某些场景下过于宽松,增加了潜在的安全风险。

更重要的是,sudo 庞大的代码库使其成为安全审计的噩梦。2021 年曝光的 Baron Samedit 漏洞(CVE-2021-3156) 就是一个典型案例:这是一个存在于 sudo 中的堆缓冲区溢出漏洞,影响从 1.8.2 到 1.9.5p1 的几乎所有主流版本,攻击者无需任何认证即可将权限提升至 root。 而 doas 由于代码量极小、功能精简,天然避开了这类复杂漏洞。

2.2 OpenBSD 的安全哲学

doas 的设计深受 OpenBSD 安全哲学影响:

  • 默认拒绝(Deny by Default):空配置文件意味着所有请求都被拒绝
  • 代码简洁(Less Code = Less Bugs):~500 行 vs ~200,000+ 行
  • 可审计性(Auditable):任何人都能在几小时内通读全部源码
  • 最小权限原则(Principle of Least Privilege):精确到命令和参数的权限控制
graph TB
    subgraph PHIL["OpenBSD 安全哲学"]
        P1["默认拒绝<br/>Deny by Default"]
        P2["代码简洁<br/>Less Code = Less Bugs"]
        P3["可审计性<br/>Auditable"]
        P4["最小权限<br/>Principle of Least Privilege"]
    end

    subgraph IMPL["doas 实现"]
        I1["空配置 = 全部拒绝"]
        I2["~500 行 C 代码"]
        I3["无插件/无 LDAP"]
        I4["精确命令匹配<br/>cmd + args"]
    end

    PHIL --> IMPL
    I2 -->|对比 sudo ~200,000+ 行| S["更小的攻击面"]
    S -->|CVE-2021-3156<br/>Baron Samedit| V["sudo 历史漏洞<br/>doas 不受影响"]

三、安装与部署

3.1 OpenBSD(原生内置)

OpenBSD 5.8 及更高版本已预装 doas,无需额外安装。系统安装后,只需配置 /etc/doas.conf 即可使用。

3.2 Linux 发行版

在 Linux 上,doas 通常以 OpenDoas(由 Duncaen 维护的移植版本)的形式提供:

Debian/Ubuntu:

sudo apt update
sudo apt install doas

Arch Linux:

sudo pacman -S opendoas

Fedora:

sudo dnf install doas

NixOS:

security.doas.enable = true;

Alpine Linux:

apk add doas

从源码编译(适用于所有平台):

git clone https://github.com/Duncaen/OpenDoas.git
cd OpenDoas
make
sudo make install

3.3 FreeBSD

pkg install doas
# 或从 Ports
cd /usr/ports/security/doas && make install clean

3.4 macOS

macOS 上可通过 MacPorts 安装,或从源码手动编译:

sudo port install opendoas
graph LR
    O["OpenBSD 5.8+<br/>原生内置"] --> P["OpenDoas<br/>Linux/macOS 移植"]
    O --> F["FreeBSD Ports<br/>security/doas"]
    P --> A["Arch Linux<br/>pacman -S opendoas"]
    P --> D["Debian/Ubuntu<br/>apt install doas"]
    P --> R["Fedora/RHEL<br/>dnf install doas"]
    P --> N["NixOS<br/>security.doas"]
    P --> L["Alpine Linux<br/>apk add doas"]
    F --> B["Ansible<br/>become_method: doas"]
    P --> B
    style O fill:#e1f5fe
    style P fill:#fff3e0

四、配置文件详解

4.1 基本语法

doas 的配置文件位于 /etc/doas.conf(Linux 上部分发行版也支持 /usr/local/etc/doas.conf),其语法灵感来源于 OpenBSD 的包过滤配置 pf.conf,极其直观。

通用语法格式:

permit|deny [options] identity [as target] [cmd command [args ...]]
  • permit / deny:允许或拒绝
  • options:可选修饰符
  • identity:用户名或 :组名(组名前加冒号)
  • as target:目标用户(默认 root)
  • cmd command:限制可执行的命令
  • args ...:限制命令参数(必须完全匹配)

4.2 常用配置选项

选项 说明
nopass 无需密码直接执行
persist 认证成功后缓存密码(一段时间内免密)
keepenv 保留当前环境变量
setenv {VAR=value ...} 设置/覆盖特定环境变量
nolog 不记录日志
cmd 限制只允许执行的命令
args 限制命令参数

4.3 配置示例

示例 1:允许 wheel 组成员以 root 身份执行任何命令(最常用)

permit persist keepenv :wheel

示例 2:允许特定用户免密执行

permit nopass alice as root

示例 3:允许组内用户执行特定命令(最小权限原则)

# 允许 wheel 组用户免密重启 nginx 服务
permit nopass :wheel as root cmd /usr/sbin/service args nginx restart

# 允许 plugdev 组用户运行 smartctl
permit nopass :plugdev as root cmd /usr/bin/smartctl

示例 4:允许用户以其他非 root 用户身份执行

permit nopass bob as postgres

示例 5:保留环境变量(适合图形化程序)

permit keepenv nopass alice

示例 6:设置特定 PATH

permit setenv {PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin} :wheel

4.4 配置文件权限

配置文件必须属于 root,且权限严格限制:

chown root:root /etc/doas.conf
chmod 0400 /etc/doas.conf

4.5 语法检查

doas -C /etc/doas.conf && echo "config ok" || echo "config error"

也可以测试特定命令的权限:

doas -C /etc/doas.conf cat
# 输出 permit 或 deny
flowchart TD
    A["doas 请求到达"] --> B{"读取 doas.conf<br/>逐行匹配"}
    B --> C{"规则类型?"}
    C -->|permit| D{"选项检查"}
    C -->|deny| E["拒绝执行"]
    D --> F{"身份匹配?"}
    F -->|用户/组匹配| G{"目标用户?"}
    F -->|不匹配| H["继续下一规则"]
    H --> B
    G -->|as root| I{"命令限制?"}
    G -->|as 其他用户| I
    I -->|cmd 指定| J{"参数匹配?"}
    I -->|无限制| K["允许执行"]
    J -->|args 匹配| K
    J -->|不匹配| E
    K --> L{"密码选项?"}
    L -->|nopass| M["直接执行"]
    L -->|persist| N["缓存认证"]
    L -->|默认| O["要求密码"]
    O --> P["验证通过?"]
    P -->|是| M
    P -->|否| E

五、工作流程

sequenceDiagram
    participant U as 普通用户
    participant D as doas
    participant C as doas.conf
    participant T as 目标用户(root)

    U->>D: doas command
    D->>C: 读取配置规则
    C-->>D: 匹配 permit/deny
    alt 规则允许
        D->>D: 验证密码(可选)
        D->>D: 设置环境变量
        D->>T: 切换 UID/GID
        T-->>U: 执行命令并返回结果
    else 规则拒绝
        D-->>U: doas: Operation not permitted
    end

六、doas vs sudo:详细对比

特性 doas sudo
代码量 ~500 行(OpenBSD 原生) ~200,000+ 行
配置文件 /etc/doas.conf,语法简洁 /etc/sudoers,语法复杂
配置验证 doas -C visudo
插件系统 无 有(LDAP、SSSD 等)
主机感知配置 无 有
密码缓存 persist 选项(OpenBSD 原生/Linux 移植版) 默认支持
日志 syslog 自定义日志 + syslog
命令参数限制 cmd + args,精确匹配 更灵活但复杂
环境变量控制 keepenv / setenv 丰富的环境变量配置
适用场景 个人桌面、小型服务器 大型企业基础设施

6.1 何时选择 doas?

  • 个人桌面/笔记本:配置简单,日常 doas pacman -Syu 足够
  • OpenBSD 系统:原生支持,无需额外安装
  • 安全敏感环境:更小的攻击面,代码可审计
  • 追求简洁的运维:不想记忆 sudoers 的复杂语法

6.2 何时仍需 sudo?

  • 需要 LDAP/AD 集成 的企业环境
  • 多主机共享配置 的集群环境
  • 需要复杂审计和策略 的合规场景
  • 团队已熟悉 sudo 且迁移成本过高

七、注意事项与最佳实践

7.1 nopass 的安全风险

nopass 选项虽然方便,但如果用户账户被攻破,攻击者可直接获得 root 权限。建议仅在受信任的单用户桌面环境使用,多用户或服务器环境应要求密码认证。

7.2 cmd + args 的精确匹配陷阱

doas 要求命令和参数完全匹配。例如:

# 配置
permit nopass :wheel as root cmd /usr/sbin/service args nginx onestart

# 以下执行会失败
$ doas service nginx onestart
doas: Operation not permitted  # 因为路径不匹配!

# 正确执行
$ doas /usr/sbin/service nginx onestart

7.3 OpenDoas 的已知问题

Linux 上的 OpenDoas 移植版存在一个与 TTY hijacking(TIOCSTI) 相关的未修复安全漏洞(CVE-2023-28339),该问题在 OpenBSD 原生版本中不存在。 如果你对此类攻击面敏感,建议:

  • 在 Linux 内核中禁用 TIOCSTI
  • 或考虑使用 sudo-rs 等替代方案

7.4 从 sudo 平滑迁移

可以在安装 doas 后创建一个别名,让肌肉记忆逐渐过渡:

# ~/.bashrc 或 ~/.zshrc
alias sudo='doas'

八、生态系统与工具集成

8.1 Ansible 支持

Ansible 原生支持 doas 作为 become_method:

# ansible.cfg
[privilege_escalation]
become = true
become_method = doas
become_user = root

8.2 相关工具

  • doasedit:一个包装脚本,允许用 doas 编辑需要 root 权限的文件
  • polkit-fakesudo:用 polkit 模拟 sudo 行为的包装器

九、总结

doas 是 OpenBSD 哲学在权限管理领域的完美体现——用 5% 的代码实现 95% 的功能。它不是 sudo 的完全替代品,但对于绝大多数个人用户和小型系统而言,doas 提供了恰到好处的功能与无与伦比的安全性。

正如 Ted Unangst 所说,他的目标是让工具“更简单,无论从用户角度还是开发者角度”。 在一个安全漏洞层出不穷的时代,doas 证明了“少即是多”的设计原则依然有效。如果你正在管理 OpenBSD 系统,或者只是厌倦了 sudoers 的复杂性,不妨给 doas 一个机会。


参考资源

原文 https://blog.csdn.net/2301_79518550/article/details/146442208