FreeBSD 普通用户如何使用 sudo 并验证 root 密码

在 FreeBSD 上安装 sudo、把普通用户加入 wheel 组,并配置 sudo 执行时验证 root 密码,而不是验证普通用户自己的密码。

我个人不太建议在所有环境里都直接开启 sudo。如果只是自己的服务器,很多时候使用 su - 切换到 root 管理系统,权限边界会更清楚。

不过 sudo 也有它的价值:可以保留命令审计记录,也可以只授权指定用户或指定命令。本文记录 FreeBSD 上普通用户使用 sudo 的配置方法,并重点说明一种更符合隔离思路的做法:普通用户执行 sudo 时验证 root 密码,而不是验证普通用户自己的密码。

报错含义

如果普通用户执行 sudo 时看到:

gary is not in the sudoers file.
This incident has been reported to the administrator.

意思是当前用户 gary 没有被 sudoers 授权,所以不能通过 sudo 执行需要管理员权限的命令。

后续操作需要先用 root 用户完成。可以直接登录 root,也可以从普通用户切换:

su -

这里输入的是 root 密码,不是普通用户密码。

安装 sudo

如果系统还没有安装 sudo,先安装:

pkg install sudo

FreeBSD 通过 pkg 安装的 sudo,配置文件通常是:

/usr/local/etc/sudoers

不要直接用普通编辑器修改这个文件,应该使用 visudo,它会在保存前检查语法,避免 sudoers 写错后导致 sudo 无法使用。

把用户加入 wheel 组

FreeBSD 默认用 wheel 组表示允许进行管理员操作的用户范围。先确认你的普通用户是否已经在 wheel 组:

id gary

如果输出里已经有 wheel,就不需要重复添加。否则用下面的命令添加:

pw groupmod wheel -m gary

gary 换成你的实际用户名。

也可以用下面的命令确认 wheel 组成员:

pw groupshow wheel

用 visudo 修改配置

打开 sudoers:

visudo

如果你想指定用 vim 打开:

EDITOR=vim visudo

在默认配置里,你会看到类似下面几段:

root ALL=(ALL:ALL) ALL

## Uncomment to allow members of group wheel to execute any command
# %wheel ALL=(ALL:ALL) ALL

## Same thing without a password
# %wheel ALL=(ALL:ALL) NOPASSWD: ALL

## Uncomment to allow members of group sudo to execute any command
# %sudo ALL=(ALL:ALL) ALL

## 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).
# Defaults targetpw  # Ask for the password of the target user
# ALL ALL=(ALL:ALL) ALL  # WARNING: only use this together with 'Defaults targetpw'

## Uncomment to show on password prompt which users' password is being expected
# Defaults passprompt="%p's password:"

方案一:wheel 用户使用 sudo,验证自己的密码

这是最常见的 sudo 配置。只需要放开 wheel 组这一行:

%wheel ALL=(ALL:ALL) ALL

这种方式下,wheel 组内的普通用户执行 sudo 时,输入的是自己的用户密码。

如果你的目标只是让普通管理员用户正常使用 sudo,到这里就可以了。但它的安全含义是:普通用户密码一旦泄露,攻击者也可能获得 sudo 提权机会。

方案二:wheel 用户使用 sudo,验证 root 密码

如果你想保留 sudo 的审计和授权能力,同时又希望密码隔离得更清楚,可以让 sudo 验证目标用户密码。默认目标用户是 root,也就是执行 sudo 时输入 root 密码。

推荐配置如下:

Defaults targetpw
Defaults passprompt="%p's password:"

%wheel ALL=(ALL:ALL) ALL

这三行的含义是:

  • Defaults targetpw:要求输入目标用户的密码。执行 sudo pkg install bash 时,目标用户默认是 root,所以输入 root 密码。
  • Defaults passprompt="%p's password:":让密码提示明确显示当前需要输入谁的密码,例如 root's password:
  • %wheel ALL=(ALL:ALL) ALL:只允许 wheel 组成员使用 sudo 执行命令。

配置后,新开一个普通用户终端测试:

sudo ls /root

如果提示变成:

root's password:

说明已经改成验证 root 密码。

不建议开启 ALL ALL

默认配置里还有这一行:

# ALL ALL=(ALL:ALL) ALL

这行不要开启。

虽然注释里说它可以配合 Defaults targetpw 使用,但它的含义是:系统里任何用户都可以尝试通过输入目标用户密码来使用 sudo。如果机器上存在服务账号、临时账号或低权限账号,这会扩大攻击面。

更稳妥的做法是:只给 wheel 组授权。

推荐保持:

Defaults targetpw
Defaults passprompt="%p's password:"
%wheel ALL=(ALL:ALL) ALL

同时保持下面这一行继续注释:

# ALL ALL=(ALL:ALL) ALL

不建议免密 sudo

sudoers 里还有一行:

# %wheel ALL=(ALL:ALL) NOPASSWD: ALL

这表示 wheel 组成员执行任何 sudo 命令都不需要输入密码。除非你非常明确知道自己在做什么,否则不建议开启。

如果确实要给某个用户配置免密,也应该尽量只针对特定命令,而不是给全部命令免密。例如:

gary ALL=(root) NOPASSWD: /usr/sbin/service nginx restart

注意 sudoers 里的命令必须使用绝对路径。可以用 which 查询命令路径:

which service

更细的命令白名单

如果只希望普通用户执行少数管理命令,可以用命令白名单,而不是给完整 root 权限。

例如允许 gary 重启和重载 nginx:

Cmnd_Alias NGINX_CMDS = /usr/sbin/service nginx restart, /usr/sbin/service nginx reload

gary ALL=(root) NGINX_CMDS

如果想让这个白名单仍然验证 root 密码,可以继续保留:

Defaults targetpw
Defaults passprompt="%p's password:"

这样 gary 只能执行白名单里的命令,不能随意执行所有 root 命令。

防翻车提醒

修改 sudoers 时建议保留一个已经登录的 root 终端,不要急着关闭。

保存 sudoers 后,另开一个普通用户终端测试:

sudo -l
sudo ls /root

确认能正常提示并通过验证后,再关闭原来的 root 终端。这样即使 sudoers 写错,也还能在 root 终端里重新执行 visudo 修复。

推荐配置总结

如果你的目标是“普通用户可以使用 sudo,但执行时必须验证 root 密码”,最终建议是:

Defaults targetpw
Defaults passprompt="%p's password:"

%wheel ALL=(ALL:ALL) ALL

同时确认:

# ALL ALL=(ALL:ALL) ALL
# %wheel ALL=(ALL:ALL) NOPASSWD: ALL

这套配置的效果是:只有 wheel 组成员有资格使用 sudo,并且执行 sudo 命令时输入的是 root 密码。