0%

Linux sudo 权限管理详解

Linux sudo 权限管理详解

Linux 服务器有一个非常基本的安全原则:

root 密码不应该随意交出去。

原因不只是“root 权限太大”。

root 的 UID 是 0,拥有几乎不受普通 Unix 权限模型限制的系统控制能力。如果多个人共同使用 root,问题首先不是某个人会不会误操作,而是原本清晰的身份边界消失了:

1
2
3
Alice ─┐
Bob ─┼──→ root(UID 0)──→ 系统
Tom ─┘

最后系统看到的都是 root。谁执行了操作、谁应该拥有哪些权限、某个人离开后怎样单独回收权限,这些问题都会变得困难。

Debian 官方的 Debian Reference 明确提醒不要共享 root 密码;Ubuntu 的默认设计更进一步,默认禁用 root 账户直接使用密码登录,系统管理主要通过 sudo 完成。

但生产环境还有另一个现实问题:

普通用户确实经常需要执行只有高权限才能完成的操作。

怎么办?

这正是理解 sudo 的起点。

一、需要高权限时,我们到底有哪些选择

Linux 上需要执行管理操作,大致有几种思路:

方式 本质 权限粒度 适合场景
直接 root 登录 直接使用 UID 0 最大 救援、初始化等特殊场景
su - root 切换到另一个完整身份 很粗 临时切换账户环境
加入管理员组 获得完整 sudo 管理能力 很大 真正的系统管理员
sudoers 精确授权 委派指定高权限能力 可精确控制 运维、开发、DBA 等

这里最需要区分的是 susudo

而且 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
2
useradd alice
passwd alice

创建普通用户,并使用:

1
id alice

验证结果。也就是说,useradd alice 本身就是 RHEL 官方认可的正常用法。

如果你明确要求 HOME 和 Bash,也可以写:

1
2
useradd -m -s /bin/bash alice
passwd alice

这里的重点不是“RHEL 必须写 -m -s”,而是显式覆盖系统默认配置。

(二)Ubuntu / Debian:adduser 是 useradd 之上的友好封装

Ubuntu 和 Debian 都可以直接使用:

1
useradd alice

创建用户,但在日常人工管理普通用户时,两者通常更推荐:

1
adduser alice

这里有一个很容易产生误解的地方:**adduseruseradd 并不是同一个命令,也不是软链接关系。**

useradd 是更底层的用户管理工具,负责按照参数和系统默认配置创建账户;而 adduser 是 Debian 系提供的上层管理工具,它会调用 useraddgroupaddusermod 等底层工具,并按照 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 中的授权规则。

既然 sudowheel 组的管理员能力来自 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
2
-a = append
-G = supplementary groups

-G 设置的是附加组列表。如果少掉 -a

1
usermod -G sudo alice

用户原来属于、但此次没有列出的其他附加组会被移除。当前 usermod(8) 手册对此定义得非常明确。

因此 -aG 不是一个需要死记的组合,而应该理解成:

1
2
-G    重新指定附加组
-aG 在原附加组基础上追加

修改完成后使用:

1
id alice

id alice 可以确认账户数据库中的组成员关系已经更新,但已经存在的登录会话不会自动获得新的附加组身份。因此用户加入 sudowheel 组后,通常需要退出并重新登录,再执行 idsudo -l 验证实际权限。

但这里还有一个更重要的问题:

把 Alice 加入 sudowheel,通常已经给了她非常大的管理权限。

如果 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
2
3
4
5
6
7
WHAT

├── COMMAND
│ 执行哪个程序?

└── OPTIONS / ARGUMENTS
允许使用哪些选项和参数?

因此,与其把 COMMANDARGUMENTS 看成两个与 WHOWHERERUNAS 并列的顶层字段,不如理解为:它们共同组成了 sudoers 中的 WHAT,也就是“允许做什么”。

五、生产环境如何正确配置 sudo

理解 sudoers 的规则以后,真正落到生产环境,原则其实很简单:

不要先问“怎样给用户 sudo 权限”,而要先问“这个用户完成工作到底需要哪些权限”。

如果用户确实需要完整的系统管理能力,可以按照发行版的默认方式加入 wheelsudo 管理组;但如果只是需要重启某个服务、执行某个备份脚本或者完成特定维护操作,更合理的方式是通过 sudoers 进行精确授权。

例如,Alice 只需要重启 nginx:

1
alice ALL=(root:root) /usr/bin/systemctl restart nginx.service

相比直接把 Alice 加入管理员组,这条规则只开放完成任务所需要的能力,权限边界明显更小。

(一)优先使用 /etc/sudoers.d/ 管理业务规则

sudo 的主配置文件是:

1
/etc/sudoers

但在生产环境中,不建议长期把所有业务授权都堆在这个文件中。

更容易维护的方式是:

1
2
3
4
5
/etc/sudoers.d/
├── ops
├── dba
├── backup
└── nginx

例如给 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
2
允许的命令     → 成功
不允许的命令 → 被拒绝

这一步很重要。

因为权限控制真正需要验证的并不只是:“业务命令能不能执行?”还包括:“权限有没有超出原来的设计范围?”

(四)谨慎使用 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
2
3
4
5
6
Alice

sudoers 检查

只允许
systemctl restart nginx.service

Alice 可以重启 nginx,但不能因为这条规则就任意修改系统配置、创建用户或者执行其他 root 命令。

这正是 sudo 最小权限设计的意义。

但如果同一个用户还被允许执行:

1
sudo -i

或者:

1
sudo bash

情况就完全不同了。

这类命令最终都会启动一个以高权限身份运行的交互式 Shell。进入这个 Shell 以后,用户后续执行的命令不再需要每次重新写:

1
sudo ...

例如:

1
2
3
4
5
6
7
8
9
sudo -i

获得 root Shell

systemctl restart nginx
useradd test
vim /etc/ssh/sshd_config
rm ...
...

此时权限边界已经从只允许执行某一条命令,扩大成可以在 root Shell 中执行大量高权限操作

因此需要区分两个完全不同的授权目标。

如果用户本身就是系统管理员,业务要求就是让他进行完整的系统维护,那么允许:sudo -i

并不一定有问题。

但如果设计目标原本是:Alice 只允许重启 nginx。

那么再允许 Alice 获得完整 root Shell,就会破坏前面设计的最小权限边界。

所以这里真正的最佳实践不是:不要使用 sudo -i

而是:当业务目标是最小权限和精确授权时,应避免同时向该用户开放完整的高权限 Shell;如果用户确实需要完整系统管理能力,则应把它作为另一种管理员授权模式单独设计。

这也是为什么生产环境设计 sudo 权限时,需要先明确一个问题:

这个用户需要的是“几个特定的 root 能力”,还是“完整的系统管理员能力”?

两者不应该混在同一套权限设计里。

(六)需要授权多条命令时:使用 Cmnd_Alias 进行能力分组

实际生产环境中,一个用户需要的高权限操作往往不止一条。

例如负责 nginx 运维的用户,可能同时需要:

1
2
3
重启 nginx
重新加载 nginx
检查 nginx 配置

如果命令数量很少,可以直接在 sudoers 规则中使用逗号列出:

1
2
3
alice ALL=(root:root) /usr/bin/systemctl restart nginx.service, \
/usr/bin/systemctl reload nginx.service, \
/usr/sbin/nginx -t

但随着授权命令越来越多,这种写法会逐渐变得难以阅读和维护。

sudoers 为此提供了 Cmnd_Alias,也就是命令别名。它可以先把一组相关命令定义成一个逻辑上的“能力集合”,然后再把这个集合授权给用户。

例如:

1
2
3
4
5
Cmnd_Alias NGINX_MGMT = /usr/bin/systemctl restart nginx.service, \
/usr/bin/systemctl reload nginx.service, \
/usr/sbin/nginx -t

alice ALL=(root:root) NGINX_MGMT

可以理解为:

1
2
3
4
5
6
7
8
NGINX_MGMT

├── restart nginx.service
├── reload nginx.service
└── nginx -t


授权给 alice

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
2
Cmnd_Alias NGINX_MGMT = /usr/bin/systemctl restart nginx.service, \
/usr/bin/systemctl reload nginx.service

多个用户需要同一组权限时,再结合用户组

如果只有 Alice 需要这组能力,可以直接授权:

1
alice ALL=(root:root) NGINX_MGMT

但如果 Alice、Bob、Tom 都负责 Web 运维,再给每个人复制一份相同规则就不够优雅了。

这时更适合建立一个业务角色组,例如:

1
2
3
4
5
groupadd webops

usermod -aG webops alice
usermod -aG webops bob
usermod -aG webops tom

然后在 sudoers 中直接针对整个组授权:

1
2
3
4
5
Cmnd_Alias NGINX_MGMT = /usr/bin/systemctl restart nginx.service, \
/usr/bin/systemctl reload nginx.service, \
/usr/sbin/nginx -t

%webops ALL=(root:root) NGINX_MGMT

其中:

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 在生产环境中真正的价值。