Linux权限模型精讲:UID、sudo与setuid的边界

发布时间:2026/10/11 12:42:22
Linux权限模型精讲:UID、sudo与setuid的边界
1. 先搞懂权限到底在“管”什么很多人对 Linux 权限的理解停留在“root 是老大普通用户是弟弟”这个层面真到了写脚本、部署服务、排查问题的时候被各种 Permission denied 砸得晕头转向。比如你在自己目录里明明有写权限却改不了/etc/hosts你明明能跑ps却 kill 不掉别人的进程你明明装了 Nginx启动时却报bind() to 0.0.0.0:80 failed。这些现象背后的逻辑全在 Linux 的权限模型里。1.1 UID 才是权限判断的唯一标准root这个名字本身没有任何特殊之处内核不认名字只认数字。每个用户都对应一个唯一的用户 ID也就是 UIDroot的 UID 是0。打开终端敲一行命令就能确认$ id uid1000(zhang) gid1000(zhang) groups1000(zhang),4(adm),27(sudo)uid1000就是你的身份标识。为什么常见发行版里普通用户都是从 1000 开始因为 0 是 root1 到 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是 644other有读权限所以普通用户可以 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 能做”清单里属于重量级选手因为它们直接影响系统稳定性和安全挂载文件系统mountmount /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_forward1修改的是运行时内核参数影响包转发行为必须 root。/proc/sys/下几乎所有文件都是 root 可写普通用户只读。防火墙与安全策略iptables、nftables、ufw这些工具操作的是内核防火墙规则涉及整机的网络进出安全默认只有 root 能执行。还有一个容易被忽略的点切换 SELinux 状态setenforce 0也是 root 专权因为一关就关了整台机器的强制访问控制影响面太大。绑定 1024 以下端口普通用户跑 Web 服务默认监听 80 或 443 会报Permission denied这不是文件权限而是内核要求绑定特权端口需要CAP_NET_BIND_SERVICE能力。普通用户解决思路通常是让 Nginx 以 root 启动监听 80worker 进程再降权或者干脆用反向代理转发到 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编辑自己的 crontabcrontab -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_timeout15 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那么任何用户运行它时进程的有效 UIDeuid会切换为文件属主的 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 临时变成 0root所以它能写入/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 us、chmod gs、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 文件给其他用户不能修改自己没有权限文件的属性换 rootsudo执行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 aptFailed 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 和普通用户的边界问题时你很快就能判断出问题出在哪个环节了。