Linux sudo 权限管理详解
Linux 服务器有一个非常基本的安全原则:
root 密码不应该随意交出去。
原因不只是“root 权限太大”。
root 的 UID 是 0,拥有几乎不受普通 Unix 权限模型限制的系统控制能力。如果多个人共同使用 root,问题首先不是某个人会不会误操作,而是原本清晰的身份边界消失了:
1 | Alice ─┐ |
最后系统看到的都是 root。谁执行了操作、谁应该拥有哪些权限、某个人离开后怎样单独回收权限,这些问题都会变得困难。
Debian 官方的 Debian Reference 明确提醒不要共享 root 密码;Ubuntu 的默认设计更进一步,默认禁用 root 账户直接使用密码登录,系统管理主要通过 sudo 完成。
但生产环境还有另一个现实问题:
普通用户确实经常需要执行只有高权限才能完成的操作。
怎么办?
这正是理解 sudo 的起点。
一、需要高权限时,我们到底有哪些选择
Linux 上需要执行管理操作,大致有几种思路:
| 方式 | 本质 | 权限粒度 | 适合场景 |
|---|---|---|---|
| 直接 root 登录 | 直接使用 UID 0 | 最大 | 救援、初始化等特殊场景 |
su - root |
切换到另一个完整身份 | 很粗 | 临时切换账户环境 |
| 加入管理员组 | 获得完整 sudo 管理能力 | 很大 | 真正的系统管理员 |
| sudoers 精确授权 | 委派指定高权限能力 | 可精确控制 | 运维、开发、DBA 等 |
这里最需要区分的是 su 和 sudo。

而且 sudo 的目标身份并不一定是 root。例如:
1 | sudo -u postgres psql |
在 sudoers 策略允许的前提下,这条命令表示以 postgres 身份执行 psql。-u 用于指定目标用户;如果未指定,sudo 默认目标用户通常是 root。
sudo 官方手册也明确说明,它允许被授权用户以超级用户或其他用户身份运行命令;
需要注意的是:-u 用于指定目标用户,未指定时目标用户通常才是 root。
因此,严格来说:
sudo 不是一个“临时变成 root”的命令,而是一套权限委派机制。
这句话是理解后面所有 sudoers 规则的基础。
二、sudo 之前,先理解 Linux 的用户身份
sudo 的规则最终都是围绕“谁能干什么”展开的,所以在理解 sudo 之前,必须先搞清楚 Linux 怎么认识一个用户。

在传统 Unix DAC 权限模型下,Linux 进行文件和资源权限判断时,核心依据是 UID、GID 和附加组;用户名 alice 主要是方便人类识别的一层映射。
本地用户相关信息通常分别保存在 /etc/passwd、/etc/shadow 和 /etc/group 中,可通过 id alice 快速查看用户的实际身份与组关系。
但企业服务器还可能通过 LDAP、Active Directory、SSSD 等提供身份,因此实际排查用户时:
getent passwd alice通常比:grep alice /etc/passwd更加完整。
既然 Linux 最终是通过 UID、GID 和组关系来识别用户,那么接下来的问题就是:一个新的用户被创建时,这些身份信息是如何建立起来的?
这正是 useradd 这类用户管理工具要完成的工作。它不仅仅是“增加一个用户名”,还会按照系统配置为用户分配 UID、GID,并设置家目录、登录 Shell 等账户属性。
例如:
1 | useradd -m -s /bin/bash alice |
其中,-m 表示创建用户的家目录,-s /bin/bash 表示明确指定登录 Shell。
不过,这两个参数都不是 useradd 创建用户时的必选项。如果不显式指定,useradd 会结合发行版和当前系统配置采用相应的默认值。
例如:
1 | useradd alice |
本身就是合法命令。useradd 会结合命令参数以及 /etc/default/useradd、/etc/login.defs 等系统默认设置决定用户属性。换句话说,真正的问题不是:
-m、-s必须写吗?
而是:你愿意依赖当前系统默认值,还是希望把 HOME 和 Shell 明确声明出来?
对于自动化脚本和跨发行版部署,我更倾向于显式表达关键属性;对于人工管理,则应遵循发行版自己的管理习惯。
不同发行版在用户创建工具、默认配置以及推荐的管理方式上并不完全相同。尤其是 RHEL 与 Debian/Ubuntu,在日常创建普通用户时采用的管理入口和使用习惯存在明显差异。
下面分别看看 RHEL、Ubuntu 和 Debian 中,创建普通用户时应该如何选择和使用这些工具。
(一)RHEL:useradd 是标准账户管理入口
RHEL 9 官方文档直接使用:
1 | useradd alice |
创建普通用户,并使用:
1 | id alice |
验证结果。也就是说,useradd alice 本身就是 RHEL 官方认可的正常用法。
如果你明确要求 HOME 和 Bash,也可以写:
1 | useradd -m -s /bin/bash alice |
这里的重点不是“RHEL 必须写 -m -s”,而是显式覆盖系统默认配置。
(二)Ubuntu / Debian:adduser 是 useradd 之上的友好封装
Ubuntu 和 Debian 都可以直接使用:
1 | useradd alice |
创建用户,但在日常人工管理普通用户时,两者通常更推荐:
1 | adduser alice |
这里有一个很容易产生误解的地方:**adduser 和 useradd 并不是同一个命令,也不是软链接关系。**
useradd 是更底层的用户管理工具,负责按照参数和系统默认配置创建账户;而 adduser 是 Debian 系提供的上层管理工具,它会调用 useradd、groupadd、usermod 等底层工具,并按照 Debian/Ubuntu 的账户管理策略完成更多初始化工作。
可以简单理解为:

两者的关系更不是软连接关系
Debian 官方文档将 useradd 明确定义为 low level utility,而 adduser 则是面向管理员的更友好前端。Ubuntu 同样沿用了这一设计。
因此,在 Ubuntu 和 Debian 上,useradd 并不是不能用,而是 adduser 更符合这两个发行版日常人工管理普通用户的习惯;需要精确控制或自动化时,useradd 依然非常常用。
三、sudo 的核心:认证、授权和管理员组
现在再看:
1 | sudo systemctl restart nginx |
sudo 为什么通常要求 Alice 输入 Alice 自己的密码,而不是 root 密码?
因为这里其实包含两个完全不同的问题。

所以可以把 sudo 的核心逻辑记成一句话:认证机制回答“你是谁”,sudoers 决定“你能干什么”。
这也是 sudo 比共享 root 密码更合理的地方。Alice、Bob、Tom 都使用自己的身份和认证凭据,但可以分别得到不同的管理权限。
接下来就容易理解所谓的 sudo 组或 wheel 组了。
很多人潜意识里认为:sudo 组或者 wheel 组天生就是管理员组。
其实不是。
Linux 用户组本身并不知道什么叫 sudo。
组名本身并不会产生 sudo 权限。在本文讨论的本地 sudo 配置中,真正决定用户能否获得 sudo 权限的,是 sudoers 中的授权规则。

既然 sudo 或 wheel 组的管理员能力来自 sudoers 中的授权规则,那么不同发行版之间的差异,本质上就是默认使用哪个用户组,以及 sudoers 预置了怎样的授权规则。下面分别看 RHEL、Ubuntu 和 Debian 的默认做法。
(一)RHEL:wheel 只是 sudoers 授权的管理员组
RHEL 默认采用 wheel 作为管理员组,sudoers 中典型规则是:
1 | %wheel ALL=(ALL) ALL |
然后把用户加入 wheel:
1 | usermod -aG wheel alice |
这是 RHEL 9 官方文档给出的标准方式。
(二)Ubuntu:通过 sudo 组获得完整管理权限
Ubuntu 默认使用 sudo 组,安装器创建的第一个管理员账号通常就是这个组的成员。
可以使用:
1 | sudo adduser alice sudo |
也可以使用底层通用命令:
1 | sudo usermod -aG sudo alice |
(三)Debian:通常同样使用 sudo 组
Debian 也通常使用 sudo 组。Debian 安装器还有一个值得注意的设计:如果安装过程中不给 root 设置密码,root 账户会被禁用,同时安装 sudo,并允许第一个普通用户通过 sudo 完成系统管理。
已有用户可以使用:
1 | adduser alice sudo |
或者:
1 | usermod -aG sudo alice |
这里特别注意:
1 | usermod -aG sudo alice |
中的:
1 | -a = append |
-G 设置的是附加组列表。如果少掉 -a:
1 | usermod -G sudo alice |
用户原来属于、但此次没有列出的其他附加组会被移除。当前 usermod(8) 手册对此定义得非常明确。
因此 -aG 不是一个需要死记的组合,而应该理解成:
1 | -G 重新指定附加组 |
修改完成后使用:
1 | id alice |
id alice 可以确认账户数据库中的组成员关系已经更新,但已经存在的登录会话不会自动获得新的附加组身份。因此用户加入 sudo 或 wheel 组后,通常需要退出并重新登录,再执行 id 或 sudo -l 验证实际权限。
但这里还有一个更重要的问题:
把 Alice 加入 sudo 或 wheel,通常已经给了她非常大的管理权限。
如果 Alice 只是负责 nginx,那么给她完整管理员能力显然过头了。
这才进入 sudo 最重要的部分:最小权限。
四、真正读懂 sudoers:不要背语法,问四个问题
例如,假设 Alice 只负责 web01.example.com 这台 Web 服务器,并且只允许她以 root 身份重启 nginx,可以配置:
1 | alice web01.example.com=(root:root) /usr/bin/systemctl restart nginx.service |
按照 sudoers 的权限结构,可以拆成:

所以这条规则表达的是:
用户 alice 只在
web01.example.com这台服务器上,可以以root:root身份执行/usr/bin/systemctl restart nginx.service。
其中,WHAT 不只是“命令名称”,而是完整的命令规格。它通常还可以继续拆分为:
1 | WHAT |
因此,与其把 COMMAND 和 ARGUMENTS 看成两个与 WHO、WHERE、RUNAS 并列的顶层字段,不如理解为:它们共同组成了 sudoers 中的 WHAT,也就是“允许做什么”。
五、生产环境如何正确配置 sudo
理解 sudoers 的规则以后,真正落到生产环境,原则其实很简单:
不要先问“怎样给用户 sudo 权限”,而要先问“这个用户完成工作到底需要哪些权限”。
如果用户确实需要完整的系统管理能力,可以按照发行版的默认方式加入 wheel 或 sudo 管理组;但如果只是需要重启某个服务、执行某个备份脚本或者完成特定维护操作,更合理的方式是通过 sudoers 进行精确授权。
例如,Alice 只需要重启 nginx:
1 | alice ALL=(root:root) /usr/bin/systemctl restart nginx.service |
相比直接把 Alice 加入管理员组,这条规则只开放完成任务所需要的能力,权限边界明显更小。
(一)优先使用 /etc/sudoers.d/ 管理业务规则
sudo 的主配置文件是:
1 | /etc/sudoers |
但在生产环境中,不建议长期把所有业务授权都堆在这个文件中。
更容易维护的方式是:
1 | /etc/sudoers.d/ |
例如给 Alice 配置 nginx 管理权限:
1 | visudo -f /etc/sudoers.d/alice-nginx |
-f 是指定要编辑的 sudoers 文件,而不是默认编辑
/etc/sudoers。
然后写入:
1 | alice ALL=(root:root) /usr/bin/systemctl restart nginx.service |
RHEL 官方同样建议新增规则优先放入 /etc/sudoers.d/,这样既方便独立维护,也能降低直接修改 /etc/sudoers 带来的风险。
需要注意,传统 sudo 的 @includedir 机制会忽略文件名中包含 . 的文件以及以 ~ 结尾的文件,因此文件名使用:
1 | alice-nginx |
通常比:
1 | alice-nginx.conf |
更稳妥。
(二)修改 sudoers,优先使用 visudo
不要直接执行:
1 | vi /etc/sudoers |
更推荐:
1 | visudo |
修改独立规则文件则使用:
1 | visudo -f /etc/sudoers.d/alice-nginx |
visudo 的价值不只是“换了一个编辑器”,而是它会在保存配置时进行语法检查,并对 sudoers 文件进行必要的编辑保护。
配置完成后,还可以再次检查:
1 | visudo -c |
sudoers 一旦出现语法错误,可能直接导致 sudo 无法正常使用,因此这里不值得为了省几秒钟而冒险。
(三)授权完成后,一定要验证最终结果
配置完成,不等于工作结束。
首先切换到实际用户,查看它最终获得了哪些 sudo 权限:
1 | sudo -l |
然后测试应该允许的命令:
1 | sudo /usr/bin/systemctl restart nginx.service |
还应该继续测试一个不应该被允许的操作,例如:
1 | sudo /usr/bin/systemctl restart ssh.service |
理想结果应该是:
1 | 允许的命令 → 成功 |
这一步很重要。
因为权限控制真正需要验证的并不只是:“业务命令能不能执行?”还包括:“权限有没有超出原来的设计范围?”
(四)谨慎使用 NOPASSWD
有些自动化脚本、CI/CD 或定时任务无法进行交互式密码输入,这时可能会看到:
1 | alice ALL=(root:root) NOPASSWD: /usr/bin/systemctl restart nginx.service |
NOPASSWD 并不是保存了 root 密码,而是表示:这条匹配的 sudo 规则执行时不要求再次完成交互式认证。
因此:
1 | NOPASSWD: /usr/bin/systemctl restart nginx.service |
和:
1 | NOPASSWD: ALL |
完全不是一个风险等级。
生产环境真正应该遵循的是:
如果确实需要 NOPASSWD,那么后面的 WHAT 应该限制得更加严格,而不是更加宽松。
(五)精确授权时,避免再开放完整的高权限 Shell
前面一直强调 sudo 的价值在于:不必把完整的 root 权限交给用户,而是只授权完成工作所需要的具体操作。
例如 Alice 只负责 nginx,可以配置:
1 | alice ALL=(root:root) /usr/bin/systemctl restart nginx.service |
这条规则的权限边界非常明确:
1 | Alice |
Alice 可以重启 nginx,但不能因为这条规则就任意修改系统配置、创建用户或者执行其他 root 命令。
这正是 sudo 最小权限设计的意义。
但如果同一个用户还被允许执行:
1 | sudo -i |
或者:
1 | sudo bash |
情况就完全不同了。
这类命令最终都会启动一个以高权限身份运行的交互式 Shell。进入这个 Shell 以后,用户后续执行的命令不再需要每次重新写:
1 | sudo ... |
例如:
1 | sudo -i |
此时权限边界已经从只允许执行某一条命令,扩大成可以在 root Shell 中执行大量高权限操作
因此需要区分两个完全不同的授权目标。
如果用户本身就是系统管理员,业务要求就是让他进行完整的系统维护,那么允许:sudo -i
并不一定有问题。
但如果设计目标原本是:Alice 只允许重启 nginx。
那么再允许 Alice 获得完整 root Shell,就会破坏前面设计的最小权限边界。
所以这里真正的最佳实践不是:不要使用 sudo -i。
而是:当业务目标是最小权限和精确授权时,应避免同时向该用户开放完整的高权限 Shell;如果用户确实需要完整系统管理能力,则应把它作为另一种管理员授权模式单独设计。
这也是为什么生产环境设计 sudo 权限时,需要先明确一个问题:
这个用户需要的是“几个特定的 root 能力”,还是“完整的系统管理员能力”?
两者不应该混在同一套权限设计里。
(六)需要授权多条命令时:使用 Cmnd_Alias 进行能力分组
实际生产环境中,一个用户需要的高权限操作往往不止一条。
例如负责 nginx 运维的用户,可能同时需要:
1 | 重启 nginx |
如果命令数量很少,可以直接在 sudoers 规则中使用逗号列出:
1 | alice ALL=(root:root) /usr/bin/systemctl restart nginx.service, \ |
但随着授权命令越来越多,这种写法会逐渐变得难以阅读和维护。
sudoers 为此提供了 Cmnd_Alias,也就是命令别名。它可以先把一组相关命令定义成一个逻辑上的“能力集合”,然后再把这个集合授权给用户。
例如:
1 | Cmnd_Alias NGINX_MGMT = /usr/bin/systemctl restart nginx.service, \ |
可以理解为:
1 | NGINX_MGMT |
Red Hat 官方文档本身也采用这种方式。例如在 RHEL 的 SAP HANA 高可用配置文档中,Red Hat 使用多个 Cmnd_Alias 定义允许执行的特定高权限命令,然后将这些命令别名一次性授权给指定用户。这说明 Cmnd_Alias 并不是一种特殊技巧,而是 sudoers 用来组织多条命令的标准机制。
需要注意的是:
Cmnd_Alias主要解决的是“规则组织和维护”问题,它本身并不会自动缩小权限。
真正决定权限边界的,仍然是别名中定义的每一条 COMMAND 以及对应的 OPTIONS / ARGUMENTS。
因此不要因为已经使用 Cmnd_Alias,就在里面随意写:
1 | Cmnd_Alias NGINX_MGMT = /usr/bin/systemctl |
这实际上仍然给出了非常宽泛的 systemctl 使用能力。
更合理的是明确到业务真正需要的动作:
1 | Cmnd_Alias NGINX_MGMT = /usr/bin/systemctl restart nginx.service, \ |
多个用户需要同一组权限时,再结合用户组
如果只有 Alice 需要这组能力,可以直接授权:
1 | alice ALL=(root:root) NGINX_MGMT |
但如果 Alice、Bob、Tom 都负责 Web 运维,再给每个人复制一份相同规则就不够优雅了。
这时更适合建立一个业务角色组,例如:
1 | groupadd webops |
然后在 sudoers 中直接针对整个组授权:
1 | Cmnd_Alias NGINX_MGMT = /usr/bin/systemctl restart nginx.service, \ |
其中:
1 | %webops |
表示 sudoers 中的 Linux 用户组 webops。
RHEL 默认的:
1 | %wheel ALL=(ALL) ALL |
本质上采用的就是同一种“针对用户组授权”的机制:%wheel 表示 wheel 组成员,只不过 RHEL 默认赋予 wheel 的权限非常大。
sudo 权限管理应从“逐用户、逐命令授权”逐步演进为“用户 → 业务角色组 → Cmnd_Alias 能力集合 → 具体命令与参数”的角色化最小权限模型。

这里尤其需要避免一个常见错误:
“需要的高权限命令比较多”并不等于“需要所有高权限”。
因此,即使用户需要十几条管理命令,也不应该为了省事直接改成:
1 | alice ALL=(root:root) ALL |
正确思路仍然是把完成业务所需要的命令明确列出来,再通过 Cmnd_Alias 和用户组把这些权限组织好。
一句话总结:
少量命令直接列举,同类命令较多使用
Cmnd_Alias,多个用户拥有相同职责时再结合用户组进行角色化授权;命令再多,也不意味着应该直接授权ALL。
六、常见误区与最佳实践
理解 sudo 最容易出现的问题,并不是命令记不住,而是把它理解成了一个简单的“root 前缀”。
下面几个误区尤其值得注意。
误区一:sudo 就是临时变成 root。
不准确。sudo 的本质是根据授权策略,以指定用户或组身份执行命令;root 只是最常见的目标身份。
误区二:加入 sudo 或 wheel 组就是获得了一点管理员权限。
通常不是。发行版默认给这类管理组配置的往往是非常宽泛的 sudo 权限,实际已经接近完整系统管理员。
误区三:只限制命令路径就等于实现了最小权限。
还不够。
例如:
1 | /usr/bin/systemctl |
和:
1 | /usr/bin/systemctl restart nginx.service |
授权范围完全不同。
设计 sudoers 时,不仅要看 COMMAND,还要继续关注 OPTIONS / ARGUMENTS,以及程序本身还能产生哪些能力。
误区四:NOPASSWD 只是为了少输一次密码。
不是。它实际取消了这条匹配规则执行时的认证要求。如果账号被控制,对应的高权限能力也可以被直接调用,因此应该只用于明确、受控的自动化场景。
误区五:配置能用,就代表 sudoers 配对了。
也不对。
一条权限规则不仅需要证明:
1 | 应该执行的命令能够执行 |
还应该证明:
1 | 不应该执行的命令确实执行不了 |
这才算真正完成权限验证。
如果把整篇文章最终压缩成一套生产环境 sudo 权限设计方法,可以记成下面这条链路:

归根到底,sudo 最值得掌握的并不是:
1 | sudo command |
这条命令本身,而是它背后的权限设计思想:
保留每个用户自己的身份,只把完成工作所必需的高权限能力切出来,再通过明确的规则进行授权、验证、审计和回收。
这才是 sudo 在生产环境中真正的价值。