我个人不太建议在所有环境里都直接开启 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 sudoFreeBSD 通过 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 密码。