☰
Linux权限模型精讲:UID、sudo与setuid的边界
2026/10/11 12:42:17 网站建设 项目流程

1. 先搞懂权限到底在“管”什么

很多人对 Linux 权限的理解停留在“root 是老大,普通用户是弟弟”这个层面,真到了写脚本、部署服务、排查问题的时候,被各种 Permission denied 砸得晕头转向。比如你在自己目录里明明有写权限,却改不了/etc/hosts;你明明能跑ps,却 kill 不掉别人的进程;你明明装了 Nginx,启动时却报bind() to 0.0.0.0:80 failed。这些现象背后的逻辑,全在 Linux 的权限模型里。

1.1 UID 才是权限判断的唯一标准

root这个名字本身没有任何特殊之处,内核不认名字,只认数字。每个用户都对应一个唯一的用户 ID,也就是 UID,root的 UID 是0。打开终端敲一行命令就能确认:

$ id uid=1000(zhang) gid=1000(zhang) groups=1000(zhang),4(adm),27(sudo)

uid=1000就是你的身份标识。为什么常见发行版里普通用户都是从 1000 开始?因为 0 是 root,1 到 999 一般预留给系统服务创建的虚拟用户(比如sshd、www-data、nginx),这些虚拟用户的作用是让服务进程以最小权限运行,避免被攻破后直接拿到 root 权限。1000 开始的才是真正的登录用户。

内核在判断“你能不能访问这个文件”时,拿的就是进程当前的 UID 和 GID。你执行ls、cat、touch等任何命令,最终都会调用系统调用,内核第一步先看你的 UID 是几,再看文件名或目录名上的权限位,然后才决定放行还是拒绝。这个判断过程是硬编码在内核里的,用户空间再折腾也没法绕过。

1.2 内核眼中的权限:rwx 三元组怎么匹配

文件权限位大家应该不陌生,ls -l输出里那十个字符:

-rw-r--r-- 1 root root 1234 May 10 10:30 sample.conf

第一个字符表示类型(-普通文件,d目录,l符号链接),后面九个字符是三组rwx:

  • 第一组rw-:文件属主(owner)的权限
  • 第二组r--:文件所属组(group)成员的权限
  • 第三组r--:其他人(other)的权限

读(r)是 4,写(w)是 2,执行(x)是 1,所以-rw-r--r--换算成数字就是644。内核检查权限时是按顺序匹配的:如果进程的 UID 等于文件的属主 UID,就用属主权限那一组;否则看进程的 GID 是否在文件的属组里,在的话用属组权限;都不匹配才落到 others。

这里有个容易踩坑的点:权限匹配是“命中即止”,不是“取三者并集”。假设你是文件属主,但你的组同时也在文件的属组里,属主的rw-已经命中了,内核根本不会再看属组的权限。所以不要想着“我既是 owner 又是 group,两边权限加起来能用”,没这回事。

另外,目录的rwx和文件不完全一样。目录的r是能列出目录内容,w是能在目录里创建和删除条目,x是能进入目录并访问其中的文件。很多人遇到“明明文件是 777,我却打不开”的问题,往往卡在目录的x权限缺了。

2. root 的“特权区”:这些事普通用户真的做不了

搞清楚 UID 机制之后,再来看 root 到底特权在哪里,以及为什么这些操作必须交给 root。核心原因概括成一句话:影响系统整体状态、影响其他用户的操作,必须由 root 来执行。普通用户被限制,本质上不是 Linux 故意为难人,而是为了防止一个用户轻易破坏所有人的环境。

2.1 改系统配置:/etc 是分界线

/etc目录存放的是全局配置文件,比如/etc/apt/sources.list、/etc/ssh/sshd_config、/etc/hosts、/etc/environment。这些文件影响的是整台机器上所有用户的运行环境,如果每个用户都能改,系统早就乱套了。

看下权限就明白:

$ ls -l /etc/hosts /etc/shadow /etc/passwd -rw-r--r-- 1 root root 221 May 10 10:30 /etc/hosts -rw-r----- 1 root shadow 851 May 10 10:30 /etc/shadow -rw-r--r-- 1 root root 2635 May 10 10:30 /etc/passwd

/etc/hosts是 644,other有读权限,所以普通用户可以 cat 查看,但没有写权限。/etc/shadow是 640,属组是shadow组,普通用户连读都读不了——因为里面存的是密码哈希,任何用户能读取,就意味着拿哈希去做离线破解成为可能,所以 Linux 把它锁得死死的。

实际操作中,普通用户想改/etc下的文件,唯一正规途径是通过sudo临时获得 root 权限,而不是直接把文件改成 777。把重要系统文件开成 777 属于非常危险的操作,等于告诉任何能登录这台机器的用户:你们随便改吧。很多系统就是这么被搞坏的。

2.2 用户、密码、组的管理

创建用户、删除用户、修改其他用户的密码,这些操作同样只有 root 能做。原因很直白:用户账户信息是系统级别的资源,新建一个用户意味着它会出现在/etc/passwd里,拥有自己的家目录,能登录系统。如果普通用户有权限加账户,等于任何人都能给自己开一个后门。

常见的命令权限对比:

命令root普通用户说明
useradd可用不可用创建用户
userdel可用不可用删除用户
usermod -aG sudo可用不可用修改用户所属组
passwd可修改任何人密码只能修改自己的密码改别人的会提示权限不足
passwd -l可用不可用锁定账户

这里有个有意思的点:普通用户运行passwd不报权限错误,是因为passwd命令本身是 setuid 的(后面细说),但它内部做了判断,只允许你修改自己的密码。你要是执行passwd zhangsan,它很直接地回你一句:Only root can specify a user name.所以“你有权限执行这个命令”不等于“你能操作全部功能”,程序逻辑也在做第二层把关。

2.3 服务状态与开机自启

管理 system 级别的服务是 root 的另一个专属领域。用systemctl stop nginx、systemctl restart docker、systemctl enable redis这些命令时,普通用户通常会收到:

Failed to stop nginx.service: Access denied

为什么?因为服务是全局资源,Nginx 是给整台机器提供服务的进程,不是属于某个用户的私有进程。任何用户都能关停它,会造成全站不可用;任何用户都能 enable 一个服务,开机自启是影响所有用户启动过程的全局状态。所以 systemd 默认把系统服务的管理权限收归 root(管理员通过 polkit 规则可以放行一部分操作,但默认就是不让你动)。

对应的,用户级服务是另一套体系。普通用户可以用systemctl --user start xxx.service管理自己的服务单元,这些服务配置放在~/.config/systemd/user/,只对自己生效,不需要 root。这是一个非常重要且容易被忽略的权限边界。

2.4 挂载、网卡、内核参数与安全策略

这几类操作在“只有 root 能做”清单里属于重量级选手,因为它们直接影响系统稳定性和安全:

  • 挂载文件系统(mount):mount /dev/sdb1 /mnt/data需要 root,因为挂载影响的是全局文件系统视图,错误操作可能导致系统崩溃或数据不可访问。普通用户即使对/mnt/data有写权限,也执行不了挂载动作。现代 Linux 用 capabilities(比如CAP_SYS_ADMIN)来管理这个能力,root 默认全部拥有,普通用户则没有。
  • 修改网卡配置:ip addr add、ip link set eth0 up这类操作普通用户不行。不过很多桌面环境通过 NetworkManager 提供了授权机制,让普通用户可以连接 Wi-Fi、打开热点,这相当于系统特别放行了一个子集,而不是把完整网络管理权交给用户。
  • 内核参数调整:sysctl -w net.ipv4.ip_forward=1修改的是运行时内核参数,影响包转发行为,必须 root。/proc/sys/下几乎所有文件都是 root 可写,普通用户只读。
  • 防火墙与安全策略:iptables、nftables、ufw这些工具操作的是内核防火墙规则,涉及整机的网络进出安全,默认只有 root 能执行。还有一个容易被忽略的点:切换 SELinux 状态(setenforce 0)也是 root 专权,因为一关就关了整台机器的强制访问控制,影响面太大。
  • 绑定 1024 以下端口:普通用户跑 Web 服务默认监听 80 或 443 会报Permission denied,这不是文件权限,而是内核要求绑定特权端口需要CAP_NET_BIND_SERVICE能力。普通用户解决思路通常是让 Nginx 以 root 启动监听 80,worker 进程再降权,或者干脆用反向代理转发到 8080。

3. 普通用户的“自留地”:这些事不用 root 也能干

root 能干的事很多,但不代表普通用户处处受制。恰恰相反,一个设计良好的系统里,普通用户在自己的一亩三分地里拥有相当完整的控制权。你要是理解了这块边界,日常工作效率会明显提升。

3.1 自己的文件几乎是“完全控制”

在自己的家目录里,你是文件属主,同时对自己 home 目录有完整 rwx 权限,所以几乎可以做任何操作:创建文件、删除文件、修改文件内容、改权限(chmod)、改属主(chown 自己文件为其他用户不行,但改自己文件权限可以)、移动和重命名。

有一个关键细节:删除文件的权限,看的是“所在目录”的写权限,而不是文件本身的写权限。只要你对某个目录有写权限,就能删除这个目录下任何文件(包括属主是 root 的文件)。比如你写了个脚本传到了/tmp/x.sh,它属于 root,但只要/tmp目录允许你写,你就能删掉它。这也是为什么/tmp上会设置 sticky bit 来保护用户之间的文件——drwxrwxrwt里的t表示:目录允许大家写,但只有文件属主、目录属主和 root 才能删除他人的文件。

3.2 用户级的一切:配置、服务、定时任务

普通用户能完全掌控自己用户空间下的东西,很多东西根本不需要 sudo:

  • 用户配置:~/.bashrc、~/.profile、~/.gitconfig、~/.ssh/config、~/.local下的软件安装,这些都属于个人环境,不需要 root。
  • 用户级 systemd 服务:systemctl --user enable/start/stop xxx,配置在~/.config/systemd/user/,不影响别人,不需要 root。
  • 定时任务:crontab -e编辑自己的 crontab,crontab -l查看自己的任务列表,操作的是/var/spool/cron/crontabs/下专属自己的文件,而/etc/crontab是系统级的,只有 root 能改。
  • 进程管理:你能启动、终止自己的进程。kill 1234时,内核会检查目标进程的属主 UID 和你当前的 UID 是否一致,一致就放行,不一致就报Operation not permitted。所以普通用户完全可以把卡死的自己启动的进程 kill 掉,但不能 kill 别人的进程。
  • 用户级日志查看:journalctl --user -f查看系统为你这个用户收集的日志,不需要 root。想看系统全量日志,一般要加入adm或systemd-journal组。

3.3 系统信息查看与基础网络工具

系统很多信息是只读的、全局可见的,普通用户均可使用:

命令/操作说明
ls、cat、less查看有读权限的文件内容
ps aux、top、htop查看所有进程列表(但信号限制见上文)
df -h、free -h查看磁盘空间和内存使用
uname -a、lscpu、lsblk查看内核版本、CPU 信息、块设备列表
ping、curl、wget、ssh、scp常规网络访问,作为客户端完全可用

一个常见误区是“普通用户不能看日志、不能看进程”。实际上ps aux能看到所有进程的命令行,这个信息是全局开放的;dmesg在较新内核上默认也允许普通用户读取。保护的重点不在“看不看得见”,而在“能不能改”。

3.4 哪些“硬件”普通用户其实也能碰

有些设备在 Linux 里被设计成“组内可用”,普通用户加入对应组就能访问,不需要 root:

  • 串口设备/dev/ttyUSB0、/dev/ttyACM0:通常是dialout组或uucp组
  • 音频设备:一般是audio组,桌面用户默认就在
  • 摄像头、视频设备:video组
  • USB 存储设备:桌面环境通常通过 udisks 自动挂载,普通用户可挂载自己的 U 盘到/media/用户名/xxx

这就是为什么开发嵌入式板卡时,手册经常要求你执行sudo usermod -aG dialout $USER。加组后重新登录,你的进程附带组身份 GID 匹配上设备的属组,内核就放行设备访问,不需要 root。理解这个机制之后,遇到“开发板连不上”的问题,第一反应应该是检查ls -l /dev/ttyUSB0看属组是什么,而不是上来就sudo chmod 777。

4. 边界为什么不是一堵墙:sudo 与 setuid 的巧妙设计

如果 root 和普通用户之间是一堵彻底的墙,系统就没法用了。日常运维、装软件、改配置都需要临时获得提权能力,所以 Linux 设计了两个关键机制:sudo 和 setuid。它们既不是开放所有权限,也不是拒绝所有请求,而是把 root 的能力“按需切碎”。

4.1 sudo:把 root 的能力切碎按需授权

sudo的工作原理可以理解成:系统通过/etc/sudoers文件定义了一份“授权清单”,里面明确写了哪些用户、在哪些主机上、能以哪些身份、执行哪些命令。比如常见配置:

zhang ALL=(ALL:ALL) ALL

意思是用户zhang可以在任何终端上,以任何用户身份,执行任何命令。这相当于把一个完整的 root 权限给了zhang,但他每次都要经过 sudo 验证并留下审计日志。

更精细的写法是把权限限定到具体命令:

zhang ALL=(root) /usr/bin/apt, /usr/bin/systemctl

这样zhang只能用 root 身份跑 apt 和 systemctl,其他 sudo 操作都会被拒绝。这种“最小化授权”思路在团队服务器管理里很实用:让开发人员能重启服务、装包,但不给他们改用户密码、改网卡配置的权力。

一个常见的误解是:“sudo 之后,我就是 root 了。”严格说,是当前进程临时以 root(或指定的其他用户)身份运行,但它受 sudoers 规则限制,能执行的命令是固定的。而且sudo -l可以随时查看你自己有哪些授权:

$ sudo -l Matching Defaults entries for zhang: env_reset, timestamp_timeout=15 User zhang may run the following commands on this host: (ALL : ALL) ALL

出问题的时候,先跑这个命令,比瞎猜“为什么我不能 sudo”高效得多。另外,sudo 有日志审计,/var/log/auth.log里记录了每次 sudo 的时间、用户、执行的命令,这对定位操作事故非常有帮助。

4.2 setuid:为什么 /usr/bin/passwd 让用户能改密码

setuid 是 Linux 里另一个提权机制,但它不像 sudo 那样基于用户清单,而是基于文件上的一个特殊权限位。一个可执行文件如果设置了 setuid(权限位里的s),那么任何用户运行它时,进程的有效 UID(euid)会切换为文件属主的 UID。

举个例子,passwd命令:

$ ls -l /usr/bin/passwd -rwsr-xr-x 1 root root 68208 May 10 10:30 /usr/bin/passwd

注意属主的rwx里出现的是rws而不是rwx,这个s就是 setuid 位。普通用户执行passwd时,进程的 euid 临时变成 0(root),所以它能写入/etc/shadow,从而能修改密码哈希。但程序逻辑提醒自己:只能改当前真实 UID 对应的用户密码,改别人就直接拒绝。

setuid 机制本质上是一种受控的提权漏洞,它依赖程序自身逻辑严密。这也是为什么系统里每个 setuid 二进制都是安全审计的重灾区——一旦某个 setuid 程序存在缓冲区溢出,攻击者拿到的就是 root shell。所以现代系统在尽量减少 setuid 程序的数量,改用 capabilities 或者其他机制。

4.3 特殊权限位:SUID、SGID、Sticky Bit

除了 setuid,还有两个同样重要的特殊权限位,建议一并搞明白:

  • SUID (4):前面说的passwd就是例子。只对可执行文件有效,效果是让进程以文件属主身份运行。
  • SGID (2):对可执行文件,进程以文件属组身份运行;对目录,新创建的文件的属组自动继承目录属组。这个特性在团队共享目录场景下特别好用,比如一个项目目录属组设为devteam,并加上 SGID,则组内任何成员创建的文件的属组都会自动是devteam,文件就可以在组内共享编辑了。
  • Sticky Bit (1):对目录生效,/tmp就是典型的drwxrwxrwt。它限制的是“删除权限”:在 sticky 目录里,即使你有写权限,也只能删除自己拥有的文件,不能删别人的。没有它,任何用户都能在/tmp里互相删文件,临时目录会变成战场。

设置方法分别是chmod u+s、chmod g+s、chmod +t,或者用数字组合chmod 4755、chmod 2755、chmod 1777。排查时如果看到一个文件权限是-rwsr-xr-x,就说明有 setuid 在生效,需要重点审查它为什么会需要这个位。

5. 权限报错秒排查:常见问题速查表

权限问题的报错信息很有迷惑性,不同情况给出的提示不一样,排查的思路也不一样。我把日常运维和开发中碰到最多的几类列个表,方便大家直接对照。

报错信息发生场景原因解决方案
Permission denied打开别人的文件或进入别人的目录文件权限或目录缺少 x 权限ls -l查看属主属组,确认自己的 UID/GID 与权限匹配
Operation not permitted对文件执行 chown、chmod 保护位普通用户不能 chown 文件给其他用户;不能修改自己没有权限文件的属性换 root(sudo)执行
Authentication failuresudo 输入密码失败用户可能在密码输错,或用户不在 sudo 组用id查看是否在 sudo 组;root 权限下执行usermod -aG sudo 用户名
user is not in the sudoers file执行 sudo 时用户根本没被写入 sudoersroot 编辑/etc/sudoers添加用户
cannot open lock file /var/lib/dpkg/lock普通用户执行 apt 系操作只有 root 能写系统包管理器的锁文件改用sudo apt
Failed to stop xxx.service: Access deniedsystemctl 管理系统服务普通用户无系统服务管理权加 sudo;或改用systemctl --user管理自己的服务
socket permission deniedDocker 命令报权限问题/var/run/docker.sock属主是 root:docker把自己加入 docker 组后重登录
bind: permission denied服务启动绑定 80/4431024 以下端口需要 root 权限配置好 cap_net_bind_service,或用 root 监听后降权,或换端口

5.1 排查权限问题的标准三步

遇到权限问题,我的习惯是按下面三步走,基本能定位九成的问题:

第一步:看自己的身份。执行id,确认 UID、GID、附加组,重点看有没有意外变化。之前踩过坑是用户用newgrp切换了主组之后,访问原来主组的文件反而不行了。

第二步:看目标的权限。执行ls -l 目标路径,逐位拆解权限串,把 owner、group、other 三组和你的 UID/GID 做匹配,确认到底有没有对应权限。这里最容易翻车的是忽略目录权限,所以我会顺手namei -l /var/lib/data/file把路径上每层的权限都打出来。

第三步:查授权。如果文件权限没问题,那问题很可能出在 sudo 授权或系统服务管理策略上。执行sudo -l看看自己有哪些 sudo 能力,再考虑是 polkit 规则拦了你还是 sudoers 没写你。

这套流程看起来简单,但很多老手解决问题时靠的就是准确的判断顺序,而不是瞎试。报错信息先看清楚,匹配权限三元组时冷静一点,大部分问题 30 秒内能定位。

6. 关于权限设计的一点经验与建议

最后聊几句我在实际使用中积累的经验和踩坑记录。权限管理的核心不是“能不能拿到 root”,而是“该用什么身份做什么事”。我自己见过太多人习惯性sudo chmod 777解决文件访问问题,短期是方便了,但长期积累下来,系统里到处都是权限失控的隐患。

一个值得养成的习惯是:先试着用普通用户完成操作,实在不行再考虑提权,提权时尽量用 sudo 限定命令而不是直接切 root shell。比如要改 Nginx 配置,sudo vim /etc/nginx/nginx.conf比sudo -i再 vim 更安全,因为权限影响面小,而且有审计日志。

另一个经验是关于用户组的利用。Linux 的权限体系里,组(group)是非常好用的中间层。与其反复给单个人授权,不如创建一个共享组,把相关用户加进去,然后用 SGID 目录让组内文件自动共享。比如建一个/srv/projects目录,属组设为projectteam,加上 SGID 位,组内所有成员创建的文件自动属于projectteam,互相之间协作就不需要动 root 权限。这种方法在团队开发环境里非常实用。

还有一点,遇到权限问题不要只盯着文件本身,要往目录层、挂载层、能力层多想想。比如普通用户明明对某个目录有写权限,却创建不了文件,可能是这个目录所在的文件系统挂载时带了ro参数,也可能是目录上有 ACL 规则,甚至可能是磁盘满了。ls -l只是第一层,df -h、mount | grep ...、getfacl都要会用。

Linux 权限体系说复杂也复杂,但核心逻辑其实特别朴素:谁的身份匹配谁的一组权限,影响全局的归 root,影响个人的归自己。把这个等式记在心里,遇到 Root 和普通用户的边界问题时,你很快就能判断出问题出在哪个环节了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询