CentOS 8 创建 sudo 用户的三大核心陷阱与正确流程

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 里本质是三步闭环:

  1. 建用户 adduser useradd );
  2. 设密码 passwd );
  3. 授策略 (激活 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 时,它并非简单打开文件,而是:

  1. 创建 /etc/sudoers 的临时副本(如 /etc/sudoers.tmp );
  2. $VISUAL vi 打开该副本;
  3. 保存退出时, 自动调用 sudoers 语法检查器 visudo -c
  4. 仅当检查通过( syntax OK ),才将临时文件原子性地覆盖原文件;
  5. 若语法错误,直接拒绝保存,原文件毫发无损。

我见过太多人绕过 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 ] 清晰得多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值