17.4 权限提升工具
sudo 和 doas 通过配置文件定义权限规则,使特定用户无需 root 密码即可执行特权操作,并提供操作日志。
思考题
即使是管理员,在无需特权操作时也应当限制自己的权限。
你认为这种权限限制设计的缺点是什么?

17.4.1 权限提升工具配置文件结构
以下是本节介绍的权限提升工具配置文件路径总结:
/usr/local/etc/
├── doas.conf # doas 配置文件
├── doas.conf.sample # doas 示例配置文件
├── sudoers # sudo/sudo-rs 主配置文件
└── sudoers.d/ # sudo 附加配置目录
├── username # 用户名配置文件
└── wheel # wheel 组配置文件17.4.2 doas
doas 是从 OpenBSD 移植的命令行工具,可作为类 Unix 系统中 sudo 的替代方案。
doas 的作者是 Ted Unangst,首次随 OpenBSD 5.8 引入。用户可通过 doas 以提升的权限执行命令(通常以 root 身份)。与 sudo 不同,doas 强调配置简洁与安全性,专注于精简的权限委派,避免庞杂的配置选项。
使用时可将 sudo 直接替换为 doas,二者在基本使用场景下功能等价。doas 支持以下选项:
| 选项 | 说明 |
|---|---|
nopass | 免密码执行 |
keepenv / setenv | 环境变量控制(继承或设置) |
args | 命令参数限制 |
nolog | 不记录日志 |
persist | 密码缓存(仅 Port security/opendoas 支持) |
doas 不具备 sudo 的插件架构和 LDAP 集成等高级特性。
17.4.2.1 安装 doas
- 使用 pkg 安装:
# pkg install doas- 或者使用 Ports 安装:
# cd /usr/ports/security/doas/
# make install clean17.4.2.2 使用 doas 管理共享权限
用户 user2 只需创建文件 /usr/local/etc/doas.conf 并写入:
permit nopass user2 as root即可允许用户 user2 在执行 doas 时无需输入密码即可获取 root 权限。如果不需要 doas 免密码,移除 nopass 即可。
警告
nopass选项虽然便捷,但存在安全风险:如果该用户账户被攻破,攻击者无需任何额外凭证即可获得 root 权限。建议仅在受信任的单用户桌面环境使用nopass,多用户或服务器环境应要求密码认证。
对于 wheel 组用户,则写入以下行即可:
permit nopass :wheel安装并配置好 doas 之后,即可执行特权命令:
$ doas ee /etc/rc.conf17.4.2.3 doas 示例配置文件
示例配置文件位于 /usr/local/etc/doas.conf.sample。
简要注释如下:
# Sample file for doas
# Please see doas.conf manual page for information on setting
# up a doas.conf file.
# Permit members of the wheel group to perform actions as root.
permit :wheel # 允许 wheel 组成员 doas
# Same without having to enter the password
permit nopass :wheel # 允许 wheel 组成员 doas,但免密码
# Permit user alice to run commands as a root user.
permit alice as root # 允许用户 alice doas
# Permit user bob to run programs as root, maintaining
# environment variables. Useful for GUI applications.
permit keepenv bob as root # 允许用户 bob doas,并继承用户 bob 的环境变量,图形用户界面程序需要,但会降低安全性(参见查看安装后信息)
# Permit user cindy to run only the pkg package manager as root
# to perform package updates and upgrades.
permit cindy as root cmd pkg args update # 仅允许用户 cindy 执行 pkg update
permit cindy as root cmd pkg args upgrade # 仅允许用户 cindy 执行 pkg upgrade
# Allow david to run id command as root without logging it
permit nolog david as root cmd id # 允许 david 以 root 身份运行 id 命令且不记录日志17.4.3 sudo
sudo 的初始作者是 Bob Coggeshall 和 Cliff Spencer(原始版本,约 1980 年),面向 4.1BSD。sudo 的主要重写和维护者是 Todd C. Miller。
sudo 的 sudoers 配置语法复杂度较高,手册页逾 1000 行,容易配置错误导致安全漏洞;时间戳文件位于 /var/run/sudo/ts/,若文件系统已满可能无法正常工作;sudo -i 在某些 shell 环境下可能不会完整加载环境变量。
sudo 允许管理员配置更严格的系统命令访问,并提供日志记录功能。
17.4.3.1 安装 sudo
- 使用 pkg 安装:
# pkg install sudo- 或者使用 Ports 安装:
# cd /usr/ports/security/sudo/
# make install clean17.4.3.2 使用 sudo 进行共享管理
sudo 配置文件由若干小节组成,支持深度自定义。
17.4.3.2.1 配置普通用户使用 sudo
未配置的普通用户执行 sudo 将提示错误 xxx Is Not in the Sudoers File. This Incident Will Be Reported。
需要在 sudoers 配置文件中加入一行以解决该问题。编辑 /usr/local/etc/sudoers 文件,找到 root ALL=(ALL:ALL) ALL 这一行,在其下方添加一行:
实际普通用户 ALL=(ALL:ALL) ALL随后保存并退出。用户“实际普通用户”将能够使用 sudo,提示输入密码时输入该用户密码即可,无需 root 用户密码。
17.4.3.2.2 授予特定用户执行特定命令的权限
在下面的示例中,Web 应用运维人员 user1 需要启动、停止和重启 Web 应用程序 webservice。要授予该用户相关权限,请在 /usr/local/etc/sudoers 文件末尾新增一行:
user1 ALL=(ALL) /usr/sbin/service webservice *随后用户 user1 即可通过以下命令启动 webservice:
$ sudo /usr/sbin/service webservice start上述配置仅对单个用户(user1)授权了 webservice 服务,但大多数组织会有 Web 团队负责相关管理。仅需一行配置,即可将权限赋予整个组。以下步骤将先创建 web 组,再向其中添加用户,最终使组内所有成员都具备管理该服务的能力:
# pw groupadd -g 6001 -n webteam再使用 pw 命令将所需用户 user1 添加至 webteam 组:
# pw groupmod -m user1 -n webteam最后,在 /usr/local/etc/sudoers 文件中写入以下行,即可让 webteam 组的所有成员都有权限管理 webservice:
%webteam ALL=(ALL) /usr/sbin/service webservice *17.4.3.2.3 sudo 普通用户和组免密码
授权使用 sudo 的用户只需输入自身密码即可完成认证。相比 su,sudo 的优点在于:su 需要输入 root 密码并授予完整的 root 权限,而 sudo 可按用户或组进行细粒度权限控制。要允许 webteam 组成员免密码管理该服务,可改为:
%webteam ALL=(ALL) NOPASSWD: /usr/sbin/service webservice *类似地,如果希望用户 user1 免密码,则改为:
user1 ALL=(ALL) NOPASSWD:ALL如果希望 wheel 组中的用户也免密码,则如下:
%wheel ALL=(ALL) NOPASSWD:ALL百分号 % 表示组,不加 % 表示普通用户。
17.4.3.3 参考文献
- Sudo. A Brief History of Sudo[EB/OL]. [2026-03-26]. https://www.sudo.ws/about/history. sudo 官方简史。
17.4.4 sudo-rs
sudo-rs 是 Rust 编写的 sudo 与 su 实现,以安全为导向,具备内存安全性。
17.4.4.1 与 sudo 不共存的安装方案
安装 sudo-rs 前必须先卸载 sudo,该方式下两者不能共存。
- 使用 pkg 安装:
# pkg install sudo-rs- 或者使用 Ports 安装:
# cd /usr/ports/security/sudo-rs/
# make install clean提供了 sudo、visudo 和 sudoedit 命令。
visudo 以安全方式编辑 sudoers 文件,类似于 vipw(8)。它锁定 sudoers 文件防止同时编辑,执行基本有效性检查,并在安装前检查语法错误。若发现语法错误,会显示行号并提示“What now?”,用户可选择 e(重新编辑)、x(退出不保存)或 Q(强制保存,有风险)。编辑器选择由 SUDO_EDITOR、VISUAL、EDITOR 环境变量决定,默认为 /usr/bin/vi。
FreeBSD 的 visudo 来自 sudo 软件包(非基本系统),与 Linux 版本来自同一上游源代码,基本兼容。
17.4.4.2 与 sudo 共存的安装方案
系统中同时存在 sudo 与 sudo-rs。
- 使用 pkg 安装:
# pkg install sudo-rs-coexist- 还可以通过 Ports 来安装:
# cd /usr/ports/security/sudo-rs/
# make -V FLAVORS # 查看有哪些 FLAVORS
default coexist
# make FLAVOR=coexist install clean # 指定安装共存版本的 sudo-rs提供了 sudo-rs、visudo-rs 和 sudoedit-rs 命令。
17.4.4.3 sudo-rs 示例配置文件
配置文件位于:/usr/local/etc/sudoers。
## sudoers file.
## sudoers 文件。
##
## This file MUST be edited with the 'visudo' command as root.
## 此文件必须通过 root 身份使用 visudo 命令编辑。
## Failure to use 'visudo' may result in syntax or file permission errors
## that prevent sudo from running.
## 不使用 visudo 可能导致语法或权限错误,从而使 sudo 无法运行。
##
## See the sudoers man page for the details on how to write a sudoers file.
## 如何编写 sudoers 文件的详细说明请参阅 sudoers 的手册页(man sudoers)。
## Defaults specification
## 默认设置规范
##
## Preserve editor environment variables for visudo.
## 为 visudo 保留编辑器相关的环境变量。
## To preserve these for all commands, remove the "!visudo" qualifier.
## 若希望对所有命令都保留这些变量,请移除“!visudo”限定。
Defaults!/usr/local/sbin/visudo env_keep += "SUDO_EDITOR EDITOR VISUAL"
## 仅对 /usr/local/sbin/visudo 命令保留环境变量 SUDO_EDITOR、EDITOR、VISUAL。
##
## Use a hard-coded PATH instead of the user's to find commands.
## 使用硬编码的 PATH 查找命令,而不是使用用户的 PATH。
## This also helps prevent poorly written scripts from running
## arbitrary commands under sudo.
## 这也有助于防止编写不当的脚本在 sudo 下执行任意命令。
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
## 为 sudo 指定固定的安全 PATH 路径。
##
## Uncomment if needed to preserve environmental variables related to the
## FreeBSD pkg utility and fetch.
## 如果需要保留与 FreeBSD pkg 工具和 fetch 相关的环境变量,请取消注释。
# Defaults env_keep += "PKG_CACHEDIR PKG_DBDIR FTP_PASSIVE_MODE"
## 示例:保留 pkg 缓存/数据库路径与 FTP 被动模式变量。
##
## Additionally uncomment if needed to preserve environmental variables
## related to portupgrade
## 另外,如需保留与 portupgrade 相关的环境变量,请取消注释。
# Defaults env_keep += "PORTSDIR PORTS_INDEX PORTS_DBDIR PACKAGES PKGTOOLS_CONF"
## 示例:保留 Ports 树、索引、数据库、二进制包与工具配置等变量。
##
## You may wish to keep some of the following environment variables
## when running commands via sudo.
## 通过 sudo 运行命令时,可能需要保留以下某些环境变量。
##
## Locale settings
## 本地化设置(语言/地区)
# Defaults env_keep += "LANG LANGUAGE LINGUAS LC_* _XKB_CHARSET"
## 示例:保留语言与 LC_* 等本地化相关变量。
##
## X11 resource path settings
## X11 资源路径设置
# Defaults env_keep += "XAPPLRESDIR XFILESEARCHPATH XUSERFILESEARCHPATH"
## 示例:保留与 X 应用资源搜索路径相关变量。
##
## Desktop path settings
## 桌面相关路径设置
# Defaults env_keep += "QTDIR KDEDIR"
## 示例:保留 Qt/KDE 的安装路径变量(旧版 Qt3/KDE3 时代变量,现代系统已不再使用)。
##
## Allow sudo-run commands to inherit the callers' ConsoleKit session
## 允许 sudo 运行的命令继承调用者的 ConsoleKit 会话。
# Defaults env_keep += "XDG_SESSION_COOKIE"
## 示例:保留 XDG 会话 cookie(ConsoleKit 时代的变量)。
##
## Uncomment to enable special input methods. Care should be taken as
## this may allow users to subvert the command being run via sudo.
## 取消注释可启用特殊输入法。但需谨慎,这可能允许用户绕过 sudo 所运行的命令。
# Defaults env_keep += "XMODIFIERS GTK_IM_MODULE QT_IM_MODULE QT_IM_SWITCHER"
## 示例:保留输入法相关环境变量。
##
## Uncomment to disable "use_pty" when running commands as root.
## 取消注释可在以 root 运行命令时禁用“use_pty”。
## Commands run as non-root users will run in a pseudo-terminal,
## not the user's own terminal, to prevent command injection.
## 以非 root 用户运行的命令将使用伪终端,而非用户的原始终端,以防止命令注入。
# Defaults>root !use_pty
## 对 root 目标用户禁用 use_pty(示例,默认注释)。
##
##
## User privilege specification
## 用户权限规范
##
root ALL=(ALL:ALL) ALL
## root 可在所有主机上,以任意(用户:组)身份运行任意命令(需要认证)。
## Uncomment to allow members of group wheel to execute any command
## 取消注释以允许 wheel 组成员执行任意命令。
# %wheel ALL=(ALL:ALL) ALL
## 示例:授予 wheel 组与 root 类似的 sudo 权限(需密码)。
## Same thing without a password
## 同上,但无需输入密码。
# %wheel ALL=(ALL:ALL) NOPASSWD: ALL
## 示例:wheel 组成员可无密码执行任意命令。
## Uncomment to allow members of group sudo to execute any command
## 取消注释以允许 sudo 组成员执行任意命令。
# %sudo ALL=(ALL:ALL) ALL
## 示例:授予 sudo 组以任意(用户:组)身份执行任意命令的权限(需密码)。
## Uncomment to allow any user to run sudo if they know the password
## of the user they are running the command as (root by default).
## 取消注释可允许任意用户在知道目标用户(默认为 root)密码的情况下运行 sudo。
# Defaults targetpw # Ask for the password of the target user
## 使用目标用户密码进行认证(而非调用者的密码)。
# ALL ALL=(ALL:ALL) ALL # WARNING: only use this together with 'Defaults targetpw'
## 所有用户均可以任意身份运行任意命令(危险!仅与 Defaults targetpw 搭配使用)。
## Read drop-in files from /usr/local/etc/sudoers.d
## 从 /usr/local/etc/sudoers.d 读取附加配置片段(drop-in)。
@includedir sudoers.d
## 包含相对目录 sudoers.d(相对于 /usr/local/etc),读取其中的配置文件。17.4.4.4 测试
$ sudo su
[sudo: authenticate] Password: # 此处不显示输入内容,无掩码提示
#17.4.5 附录:通过 mac_do 权限提升
参考文献:
- K Rin. FreeBSD MAC 簡單介紹[EB/OL]. [2026-03-26]. https://sandb0x.tw/a/FreeBSD_MAC_簡單介紹. 以中文简要介绍 FreeBSD 强制访问控制框架的基本概念与配置方式。
- FreeBSD Project. mac_do(4)[EB/OL]. [2026-03-26]. https://man.freebsd.org/cgi/man.cgi?query=mac_do&sektion=4. MAC 策略 do 模块手册页。
- OpenBSD. doas -- execute commands as another user[EB/OL]. [2026-04-17]. https://man.openbsd.org/doas.1. doas 权限提升工具手册页。
17.4.6 课后习题
- 查阅 FreeBSD 中
mac_do模块的源代码,分析其通过 Mandatory Access Control 实现权限提升的机制,梳理其与sudo/doas在权限模型上的本质差异。 - 查阅 FreeBSD Ports 中
doas工具的源代码实现,分析其配置解析、规则匹配和环境变量处理的设计,评估其在简单性与安全性之间的权衡策略。 - 修改
doas配置中的keepenv选项,记录其对环境变量继承行为的影响,分析不同环境变量(如PATH、HOME)在权限提升场景下的安全风险。