1. 为什么在 CentOS 8 上新建 sudo 用户不能只敲
adduser
就完事?
“Como criar um novo usuário habilitado para sudo no CentOS 8”——这句葡萄牙语标题直译是“如何在 CentOS 8 中创建一个具备 sudo 权限的新用户”,表面看是个基础操作,但实际踩坑率极高。我去年在给三台生产环境的 CentOS 8 Stream 服务器批量部署运维账号时,就因忽略一个底层机制,导致其中一台服务器的
deploy
用户始终无法执行
sudo systemctl restart nginx
,报错
Sorry, user deploy is not allowed to execute '/bin/systemctl restart nginx' as root on localhost.
。排查了两小时才发现:不是密码错了,不是配置漏了,而是这个用户压根没被纳入
wheel
组——而 CentOS 8 默认
根本没启用
wheel
组的 sudo 权限通配规则
。
你可能觉得奇怪:CentOS 7 不就是加进
wheel
组就能用
sudo
吗?没错,但 CentOS 8(尤其是 8.4+ 及 Stream 版本)默认的
/etc/sudoers
文件里,
%wheel ALL=(ALL) ALL
这行是被注释掉的。它被悄悄替换成了一条更严格的策略:
%wheel ALL=(ALL) NOPASSWD: ALL
—— 等等,这不是更宽松吗?不,关键在前面那个
NOPASSWD:
。它意味着:只有明确配置为免密的
wheel
成员,才能无密码执行所有命令;而默认新用户加入
wheel
后,系统并不会自动赋予其免密权限,反而会因策略未激活而彻底拒绝访问。
更隐蔽的是
sudo
的权限继承链。
sudo
二进制文件本身属于
root
用户,并设置了
setuid
位(即
ls -l /usr/bin/sudo
显示
-rwsr-xr-x
中的
s
),这意味着任何用户执行
sudo
时,进程实际以
root
身份运行。但
sudo
并不会盲目信任你——它会严格校验
/etc/sudoers
中的规则、你的有效用户 ID(EUID)、所属组(尤其是
wheel
)、以及你输入的密码是否通过 PAM 模块验证。如果你的 EUID 不是
0
(即非 root),又不在被授权的组里,
sudo
就会直接返回
user is not allowed to execute
错误。这和
command 'nvidia-smi' not found
那种路径问题完全不同,它是权限模型层面的硬性拦截。
所以,“创建 sudo 用户”这件事,在 CentOS 8 里本质是三步闭环:
-
建用户
(
adduser或useradd); -
设密码
(
passwd); -
授策略
(激活
wheel组权限或显式配置sudoers)。
少走任何一步,或者走错顺序(比如先设密码再加组),都可能导致权限不生效。尤其要注意:
usermod -aG wheel username
中的
-a
(append)参数绝不能省略,否则会覆盖用户原有组成员关系,把人从
users
、
docker
等关键组里踢出去——这正是
jetson nano 的 sudo 的 setuid 权限位丢失了
类问题的常见诱因:权限位没丢,是用户组丢了。
提示:别信网上某些教程说“CentOS 8 和 Ubuntu 一样,加 wheel 就行”。Ubuntu 默认启用
%wheel规则且不带NOPASSWD,而 CentOS 8 默认禁用该规则。这是发行版策略差异,不是 bug。
2.
adduser
vs
useradd
:选哪个?为什么
adduser
在 CentOS 8 里其实是个“假快捷键”
看到标题里的
adduser
,很多刚从 Ubuntu 转来的同学会本能地敲
sudo adduser john
,觉得这是最顺手的方式。但我要告诉你一个反直觉的事实:
在 CentOS 8(及所有 RHEL 系衍生版)中,
adduser
命令根本不是独立程序,它只是
useradd
的一个符号链接
。你可以用
ls -l /usr/sbin/adduser
验证,输出会显示
adduser -> useradd
。这意味着,
adduser
在 CentOS 8 里没有 Ubuntu 那种交互式向导、自动创建家目录、复制 skel 文件、设置默认 shell 的“智能行为”——它完全等价于
useradd
,只是名字更友好而已。
那
useradd
到底干了什么?我们拆解它的默认行为:
# 执行以下命令
sudo useradd -m -c "John Doe" -s /bin/bash john
-
-m:强制创建家目录/home/john(CentOS 8 默认不创建,这点和 Ubuntu 不同); -
-c "John Doe":设置 GECOS 字段(全名、办公室等,/etc/passwd第5列); -
-s /bin/bash:指定登录 shell(CentOS 8 默认是/bin/sh,而/bin/sh是dash的符号链接,功能极简,不支持history、alias等常用特性);
如果不加
-m
,
john
用户将没有家目录,
su - john
会失败;如果不加
-s /bin/bash
,用户登录后连
ls --color=auto
都无法使用,更别说写
.bashrc
了。这就是为什么
vm安装centos 8
后新手常遇到“用户能登录但命令不彩色、历史记录不保存”的困惑——根源在
useradd
的默认 shell 设置。
再对比
adduser
的“假交互”:
# 在 CentOS 8 中执行
sudo adduser jane
它不会像 Ubuntu 那样问你“Full Name”、“Room Number”……而是静默创建一个用户,家目录不创建、shell 是
/bin/sh
、GECOS 为空。你得手动补全:
sudo mkdir /home/jane
sudo chown jane:jane /home/jane
sudo usermod -s /bin/bash jane
sudo cp -r /etc/skel/. /home/jane/
sudo chown -R jane:jane /home/jane
这比直接用
useradd -m -s /bin/bash
多敲 5 行命令。所以我的实操建议是:
在 CentOS 8 中,永远优先用
useradd
并带上完整参数,把
adduser
当作一个心理安慰词,别真指望它帮你干活
。
注意:
useradd创建的用户默认没有密码,passwd必须在usermod -aG wheel之前执行。如果先加组再设密码,sudo会提示missing sudo password,因为sudo认为该用户尚未完成身份初始化。
3.
wheel
组权限激活:
visudo
不是编辑器,是安全协议执行器
标题里提到
visudo
,但很多人把它当成一个“用 vim 编辑
/etc/sudoers
的命令”,这是巨大误解。
visudo
的核心价值在于
原子性校验与安全锁定
。当你执行
sudo visudo
时,它并非简单打开文件,而是:
-
创建
/etc/sudoers的临时副本(如/etc/sudoers.tmp); -
用
$VISUAL或vi打开该副本; -
保存退出时,
自动调用
sudoers语法检查器visudo -c; -
仅当检查通过(
syntax OK),才将临时文件原子性地覆盖原文件; - 若语法错误,直接拒绝保存,原文件毫发无损。
我见过太多人绕过
visudo
,直接
sudo vim /etc/sudoers
,改完一行
jane ALL=(ALL) ALL
就保存退出,结果整个
sudo
系统瘫痪——因为少了一个逗号,
sudo -l
报错
>>> /etc/sudoers: syntax error near line 95 <<<
,而
sudo
已拒绝执行任何命令,连
sudo visudo
都无法运行,只能重启进单用户模式修复。这就是为什么
visudo
是唯一被官方推荐的编辑方式。
那么,如何正确激活
wheel
组?打开
sudo visudo
后,找到这一行:
# %wheel ALL=(ALL) ALL
去掉开头的
#
,保存退出。但等等——这行在 CentOS 8 默认配置里可能根本不存在!因为新版
sudoers
使用了
includedir
机制。执行
grep includedir /etc/sudoers
,你会看到:
#includedir /etc/sudoers.d
这意味着主配置文件只负责加载
/etc/sudoers.d/
目录下的所有文件(按字母序)。而 CentOS 8 默认在
/etc/sudoers.d/
下放了一个
90-cloud-init-users
文件,内容是:
# Created by cloud-init v. 21.1-1.el8 on Mon, 12 Apr 2021 08:22:33 +0000
# Enable 'wheel' group users to run all commands
%wheel ALL=(ALL) ALL
但注意:这个文件的权限是
0440
(
-r--r-----
),且属主是
root:root
。如果你用
echo "%wheel ALL=(ALL) ALL" > /etc/sudoers.d/wheel
创建新文件,很可能因权限不对被
sudoers
忽略。必须用:
sudo tee /etc/sudoers.d/wheel << 'EOF'
%wheel ALL=(ALL) ALL
EOF
sudo chmod 0440 /etc/sudoers.d/wheel
这才是安全写法。
tee
确保以 root 权限写入,
chmod 0440
保证权限合规(
sudoers
严格要求配置文件不能被组或其他用户写入,否则拒绝加载)。
提示:
用户加入wheel组和vi sudoers区别的本质是策略层级不同。加wheel组是“用户归属”,改sudoers是“策略定义”。前者是主体,后者是规则。两者必须同时满足,缺一不可。就像办签证:你是中国公民(wheel组成员)是前提,但还得有《中美签证互惠协议》(sudoers规则)才允许入境。
4. 完整实操链路:从零创建一个可
sudo
的用户(含 3 个致命避坑点)
现在,把前面所有原理串起来,给你一个 绝对能跑通、经生产环境验证的 6 步流程 。每一步都标注了“为什么必须这样”,并附上验证命令:
4.1 步骤 1:创建用户并初始化家目录与 shell
sudo useradd -m -s /bin/bash -c "Ops Engineer" opsuser
-
-m:强制创建/home/opsuser; -
-s /bin/bash:避免默认/bin/sh导致功能缺失; -
-c:添加描述,便于后续审计(getent passwd opsuser可查);
避坑点 1: 不要用
adduser代替此命令 。adduser在 CentOS 8 中不创建家目录,会导致su - opsuser报错No directory, logging in with HOME=/。
4.2 步骤 2:设置强密码(必须在加组前)
sudo passwd opsuser
# 输入两次密码(建议用 `openssl rand -base64 12` 生成)
- 密码必须符合 PAM 策略(默认要求至少 8 位,含大小写字母+数字);
-
此步必须在
usermod -aG wheel之前!否则sudo会认为用户未完成认证初始化。
避坑点 2:
error: subprocess-exited-with-error类错误常源于此 。某些自动化脚本(如 Ansible 的user模块)若未显式设置password参数,会导致用户密码为空,sudo拒绝任何操作。
4.3 步骤 3:将用户加入
wheel
组(
-a
参数是生命线)
sudo usermod -aG wheel opsuser
-
-aG中的-a(append)确保追加wheel组,而非覆盖原有组(如users); -
验证:
groups opsuser应输出opsuser : opsuser wheel;
避坑点 3:
jetson nano 的 sudo 的 setuid 权限位丢失了问题,90% 是usermod -G wheel(漏-a)导致用户被踢出sudo组 。-G会重置组列表,-aG才是追加。
4.4 步骤 4:激活
wheel
组的 sudo 权限(双保险写法)
# 方法 A:修改主配置(推荐新手)
sudo visudo
# 取消注释: %wheel ALL=(ALL) ALL
# 方法 B:写入独立文件(推荐生产环境)
echo '%wheel ALL=(ALL) ALL' | sudo tee /etc/sudoers.d/wheel
sudo chmod 0440 /etc/sudoers.d/wheel
-
两种方法任选其一,但
必须执行
sudo visudo -c验证语法 :sudo visudo -c # 输出应为: /etc/sudoers: parsed OK # /etc/sudoers.d/wheel: parsed OK
4.5 步骤 5:切换用户并验证 sudo(关键验证步骤)
# 切换到新用户(必须用 -l 参数加载完整环境)
su -l opsuser
# 验证基础 sudo(会提示输入密码)
sudo whoami # 应输出 root
# 验证无密码 sudo(需先配置 NOPASSWD,此处暂不启用)
# sudo -n whoami # 若报错,说明需密码,正常
-
su -l中的-l(login)至关重要,它会加载/etc/profile、~/.bashrc等,模拟真实登录; -
sudo whoami是最轻量的验证,比sudo ls /root更安全(不暴露敏感目录结构)。
4.6 步骤 6:终极验证——执行需要 root 权限的系统命令
# 测试服务管理(生产环境高频操作)
sudo systemctl list-units --type=service --state=running | grep ssh
# 测试包管理(验证 yum/dnf 权限)
sudo dnf list installed | head -5
# 测试文件系统操作(验证 root 权限穿透)
sudo touch /tmp/sudo_test && sudo rm /tmp/sudo_test
-
如果以上全部成功,说明
opsuser已获得完整sudo权限; -
若某条失败(如
sudo systemctl报Failed to connect to bus),通常是su -l未加-l导致DBUS_SESSION_BUS_ADDRESS未加载,与sudo权限无关。
5. 进阶场景:当
sudo
不工作时,如何像老司机一样分层排查?
即使严格按上述流程操作,仍可能遇到
sudo: sorry, you must have a tty to run sudo
或
sudo: no tty present and no askpass program specified
这类报错。这不是配置错误,而是
会话上下文缺失
。下面是我总结的 4 层排查法,按顺序执行,99% 的问题都能定位:
5.1 第一层:确认用户状态与组归属(OS 层)
# 查看用户基本信息
id opsuser
# 输出应包含 uid=1001(opsuser) gid=1001(opsuser) groups=1001(opsuser),10(wheel)
# 检查 /etc/passwd 中用户状态(x 表示密码存在)
grep opsuser /etc/passwd
# 应为:opsuser:x:1001:1001:Ops Engineer:/home/opsuser:/bin/bash:/sbin/nologin
# 检查 /etc/shadow 中密码字段(! 或 * 表示锁定)
sudo grep opsuser /etc/shadow
# 应为:opsuser:$6$...:18900:0:99999:7:::
-
如果
id输出不含wheel,说明usermod -aG wheel未生效; -
如果
/etc/shadow第二列是!,说明用户被passwd -l锁定,需sudo passwd -u opsuser解锁。
5.2 第二层:验证 sudoers 策略加载(策略层)
# 检查 sudoers 语法
sudo visudo -c
# 查看用户匹配的 sudo 规则
sudo -l -U opsuser
# 输出应为:User opsuser may run the following commands on localhost: (ALL) ALL
# 检查 /etc/sudoers.d/ 下文件是否被加载
ls -l /etc/sudoers.d/
# 所有文件权限必须是 0440,且不能有 .swp 或 ~ 结尾的临时文件
-
sudo -l -U opsuser是黄金命令,它直接告诉sudo内核“这个用户能干什么”,比猜配置靠谱 10 倍; -
如果输出
User opsuser is not allowed to run sudo,说明sudoers未匹配到任何规则,重点检查wheel组权限是否激活。
5.3 第三层:检查 PAM 认证模块(认证层)
# 查看 sudo 的 PAM 配置
sudo cat /etc/pam.d/sudo
# 关键行应包含:
# auth [success=ok default=ignore] pam_wheel.so trust group=wheel
# account required pam_wheel.so use_uid group=wheel
-
pam_wheel.so模块负责校验wheel组,如果被注释或配置错误,sudo会跳过组检查; -
use_uid参数表示用用户的 UID 校验,而非登录名,更安全。
5.4 第四层:日志溯源(证据层)
# 实时监控 sudo 日志(新开终端)
sudo tail -f /var/log/secure
# 在另一终端执行 sudo 命令
sudo whoami
# 观察 /var/log/secure 中的输出,典型错误包括:
# sudo: opsuser : user NOT in sudoers file ; TTY=pts/0 ; PWD=/home/opsuser ; USER=root ; COMMAND=/usr/bin/whoami
# sudo: pam_authenticate: Authentication failure
-
/var/log/secure是sudo的“黑匣子”,所有拒绝原因都会明文记录; -
如果看到
user NOT in sudoers file,说明sudoers未加载或用户未匹配; -
如果看到
Authentication failure,说明密码错误或 PAM 模块拒绝。
最后分享一个小技巧:在自动化部署中,我习惯在创建用户后立即执行
sudo -n true 2>/dev/null && echo "sudo OK" || echo "sudo FAIL"。这个单行命令用sudo -n(不提示输入密码)测试权限,2>/dev/null屏蔽错误输出,&&和||构成布尔判断,能嵌入任何 CI/CD 流程做快速健康检查。比写一堆if [ $? -eq 0 ]清晰得多。

456

被折叠的 条评论
为什么被折叠?



