
直接上手干活之前先聊一个我踩了无数次坑才悟出来的道理Linux权限管理不是“会不会敲命令”的问题而是“能不能把整个系统的访问边界画清楚”的问题。很多人觉得chmod 777一了百了结果某天日志被删、配置被改、服务起不来顺着排查到最后才发现是权限给了不该给的人。这篇东西我打算从用户、组、文件权限模型、特殊权限位、ACL、sudo 这几个最常用的维度拆开讲再配合实操过程和问题排查记录尽量让刚接触的人能跟着走一遍也让有经验的人能查漏补缺。1. 用户与用户组权限的基本单元1.1 三类用户角色先分清再动手Linux 里的用户不是只有“管理员”和“普通用户”两种严格分下来至少三类超级用户 root、系统用户、普通用户。这个分类不是形式主义而是早期 Unix 设计留下的惯例到今天依然影响着几乎所有权限判断逻辑。超级用户 root 是系统里唯一“不受权限约束”的账户它的 UID 固定为 0。不管文件权限位怎么设root 都能读、能写、能执行甚至能绕过目录的写权限直接删除文件这是很多人第一次被坑到怀疑人生的地方。所以生产环境里第一条铁律就是能不登 root 就不登 root能不用 root 跑服务就不用 root 跑服务。系统用户是一类特殊存在它们通常没有登录 Shell也没有 home 目录存在的意义是让某个守护进程以自己的身份运行。比如常见的 nginx 用户、mysql 用户、postgres 用户它们的 UID 一般小于 1000不同发行版标准略有差异。为什么非要单独建一个用户跑服务因为服务一旦被攻破攻击者拿到的权限就是该用户的权限而不是所有用户的权限。如果所有服务全用 root 跑一个漏洞就可能让整个机器沦陷。普通用户就是我们日常登录系统、跑代码、操作文件的主体UID 一般从 1000 开始分配。普通用户之间默认是隔离的你的 home 目录别人进不去别人的文件你改不了。这种“最小权限原则”是整个 Linux 权限体系的底层逻辑。判断一个用户属于哪一类最简单的方式是查/etc/passwd。每一行冒号分隔的字段里第三个字段是 UID第六个字段是家目录第七个字段是登录 Shell。看到/sbin/nologin或者/usr/sbin/nologin基本可以断定是系统用户。注意修改用户 UID 不是一件可以随意操作的事因为 UID 一旦变化文件系统里所有属于该用户的文件属主显示都会乱掉。更稳妥的做法是先确认该用户没有持有重要文件再执行usermod -u否则排查起来非常头疼。1.2 用户组批量授权的最小集合单用户授权只能解决“点对点”的问题一旦涉及多人协作逐条授权就会变成灾难。用户组就是用来解决这个问题的抽象层把若干用户加入同一用户组再给这个组分配文件权限组内所有用户自动继承。理解用户组要抓住一个重点每个用户在/etc/passwd里记录了一个“主组”primary group。用户创建文件时文件的属组默认就是用户的主组。除了主组之外用户还可以通过usermod -aG加入多个“附加组”secondary group / supplementary group。主组和附加组在权限判断上基本同等对待但需要注意一个用户在同一时刻只能有一个主组却可以同时属于很多附加组。组操作的常用命令我列一下方便对照操作命令示例创建组groupaddgroupadd devteam查看组信息groupadd与其他命令组合 /getent groupgetent group devteam将用户加入组usermod -aGusermod -aG devteam zhangsan从组中移除用户gpasswd -dgpasswd -d zhangsan devteam修改主组usermod -gusermod -g devteam zhangsan查看用户所属组groupsgroups zhangsan删除组groupdelgroupdel devteam写这段的时候我要特别强调一个我亲眼见过的典型事故某同事执行usermod -G devteam zhangsan注意这里用的是大写G结果把 zhangsan 从所有附加组里踢了出来只留下了 devteam。原因很简单大写-G是“覆盖附加组列表”的语义要用-aG表示追加。所以我的习惯是凡是涉及给用户加组的操作一律写完整的usermod -aG不加-a宁可停下来先确认。组设计上有一个经验不要照搬部门结构建组而是按“职责”和“工作空间”建组。比如“web-deploy”组负责发布/data/www“data-science”组负责访问/data/analysis“devops”组才有 sudo 权限。这样即使有人离职只需要把他从相关组里移除而不需要重新配置一堆文件权限。2. 文件权限模型读、写、执行背后的逻辑2.1 权限位的划分藏着一套二进制算法每个 Linux 文件都有一组权限位用ls -l看就是最前面那十个字符比如-rw-r--r--。第一个字符表示文件类型-普通文件d目录l符号链接后面九个字符分为三组每组三个第一组是属主owner权限第二组是属组group权限第三组是其他用户others权限每一组里三个字符依次是r读、w写、x执行。有权限就显示对应字母没有权限就用-占位。很多人只记命令不记原理遇到复杂场景就开始蒙圈。这里我给你一个必须吃透的映射关系字符二进制位八进制值含义r1004读取文件内容 / 列出目录条目w0102修改文件内容 / 在目录内增删文件x0011执行文件 / 进入目录或访问目录内文件所以rwx就是 7r-x就是 5rw-就是 6。chmod 755的含义是属主 7rwx、属组 5r-x、其他用户 5r-x这是一个非常通用的文件权限组合。chmod 644则是属主 6rw-、属组 4r--、其他用户 4r--适合普通配置文件。真正容易出错的地方在目录权限上。目录的“读”权限指的是能列出目录里有哪些文件名目录的“写”权限指的是能在目录里创建、删除、重命名文件目录的“执行”权限指的是能否“穿过”这个目录去访问里面的子文件。很多人只给目录设置了r--以为能看就行结果访问里面的文件时报 permission denied就是因为缺少x权限——你连“进入目录”的资格都没有光能“看到名字”毫无意义。所以处理目录权限时我总结的口诀是读权限用来看目录清单执行权限用来进入目录写权限用来增删文件。三者需要组合使用单独给其中任何一个都会遇到诡异的现象。比如--x可以进入目录但无法列出文件你只知道里面有个文件但不知道名字r--能看到文件名但无法进入也无法访问任何内容rw-能看能改文件名但同样进不去。2.2 特殊权限位SUID、SGID、Sticky 的实战意义普通 rwx 权限之外还有三个特殊位它们不常用但一旦用错就是安全漏洞。这三个位分别是 SUIDSet User ID、SGIDSet Group ID和 Sticky粘滞位。SUID 用数字表示是 4在属主的x位上显示为s。它的作用是当用户执行带 SUID 的程序时进程的有效用户 ID 会临时变成该程序属主的 UID而不是当前登录用户的 UID。最经典的例子是/usr/bin/passwd它属于 root普通用户执行它时能以 root 权限修改/etc/shadow里的密码。如果没有 SUID普通用户不可能直接修改系统密码文件。但反过来说如果某个 root 属主的程序被植入了后门SUID 就等于递了一把万能钥匙给攻击者。所以我排查系统时find / -perm -4000这类搜索 SUID 文件的命令是必跑清单里的一项。SGID 用数字表示是 2在属组的x位上显示为s。它对可执行文件的作用和 SUID 类似只是替换的是有效组 ID。更重要的场景是目录如果一个目录设置了 SGID那么所有在这个目录里新建的文件其属组会自动继承父目录的属组而不是当前用户的主组。这个特性非常适合团队共享目录它省去了每次新建文件后还要手动chgrp的麻烦。我在配置/data/shared这类协作目录时几乎总会执行chmod gs /data/shared。Sticky 位用数字表示是 1在其他用户的x位上显示为t。它只对目录有意义设置了 Sticky 的目录里除了 root只有文件属主本人或者目录属主才能删除或重命名文件其他人即使有目录写权限也动不了别人的文件。你看/tmp就是最典型的例子任何人都能往里面写文件但普通用户删不掉别人创建的文件。对于共享目录来说设置 Sticky 几乎是必须的否则任何有写权限的人都可以乱删别人的正在跑的任务产物。三个特殊位的组合数字是把 4、2、1 直接加在普通权限前面比如chmod 1777 /tmp表示 Sticky 位 所有人都能读写的权限。需要特别留意的是如果你只想设置特殊位不想动原有的普通权限更应该用符号模式比如chmod us、chmod gs、chmod t这样可以避免误把权限从 755 改成 4777。使用数字模式时很容易顺手写成chmod 4755去给某个程序加 SUID但如果你本意只是加 SGID这就会埋下隐藏的越权风险。2.3 umask每个新建文件权限背后的“隐形操盘手”很多初学者不理解一个问题为什么我用touch test.txt创建出来的文件是-rw-r--r--而不是-rwxr-xr-x原因就在 umask 这个东西上。umask 是一个“默认权限掩码”它决定了每个新建文件或目录的默认权限。文件默认最大权限是 666没有执行位因为新建文件默认不给执行权限目录默认最大权限是 777。实际创建出来的权限 最大权限减去 umask 中对应的位。比如当前 umask 是022新文件的权限 666 - 022 644即rw-r--r--新目录的权限 777 - 022 755即rwxr-xr-x如果 umask 是027新文件 666 - 027 640新目录 777 - 027 750理解了这个逻辑就能解释很多“莫名其妙”的情况。比如你在某台机器上创建文件一ls -l发现属组有写权限、其他用户没有任何权限那说明这台机器的 umask 很可能被设成了007之类的值。这类设置常见于团队共享的开发环境目的是防止其他组的人看到本组成员创建的文件。查看当前 umask 直接执行umask修改当前会话的 umask 执行umask 022。如果想全局生效可以把设置写进/etc/profile或/etc/bashrc但要注意这会影响所有用户改之前务必确认团队对权限的预期是否一致。常见的坑是有人在某个服务启动脚本里写了一句umask 000结果这个服务生成的日志文件全变成 666敏感信息直接裸奔在系统上。我见过太多次因为日志权限过宽导致的意外泄露事故。3. 实操从创建用户到完成权限配置3.1 创建用户并设置密码策略不能只敲一条 useradd创建用户看着简单实际工作里需要考虑的点不少。我以一个真实场景为例某公司要新来一个后端开发他需要访问/data/app下的代码目录同时需要通过 sudo 重启某个 Web 服务但不需要登录 root。第一步是创建用户。这里我习惯用useradd -m -s /bin/bash zhangsan-m表示同时创建 home 目录-s指定登录 Shell。如果不加-m很多发行版默认不会自动创建 home 目录之后你还要手动mkhomedir_helper平白多一个步骤。第二步是设置密码。passwd zhangsan会交互式要求输入两次密码。但要注意如果是自动化脚本建议用chpasswd脚本方式例如echo zhangsan:初始密码 | chpasswd并且要求 zhangsan 第一次登录后立刻改密码。生产环境上我通常还会加一条过期策略chage -d 0 zhangsan这句命令强制用户在下次登录时先修改密码。不要小看这个步骤很多人创建完用户后忘记给初始密码设置过期时间结果初始密码一直有效成为团队里公开的秘密。类似的策略统一写在login.defs或chage命令中配合使用即可。第三步是加入附加组。用户需要访问/data/app如果这个目录的属组是devteam就执行usermod -aG devteam zhangsan如果你希望他还能通过 sudo 执行特定命令把它加入wheel或sudo组也可以但更精细的做法是在 sudoers 里单独配置命令白名单。这一步我会在后续 3.4 节单独展开。提示用户加入新组后如果这个用户当前已经登录了他需要重新登录一次才能获取新的附加组权限。这个坑其实挺常见的本人在当前会话里执行sudo失败并不是权限配错了而是会话里的组缓存还没刷新。最简单的验证方法是执行id看看当前的组列表。3.2 调整属主和属组chown 与 chgrp 的组合用法用户创建好了接下来就是给文件或目录指定属主和属组。最常用的命令是chown。它的语法非常直观chown 属主:属组 文件路径。比如chown zhangsan:devteam /data/app/project这条命令会把/data/app/project的属主改成 zhangsan属组改成 devteam。如果你只想改属主不想动属组可以写chown zhangsan /data/app/project。如果你只想改属组可以写chown :devteam /data/app/project。对于目录有一个非常重要的选项-R表示递归修改。但是我要泼一盆冷水除非你非常确定目录里的每个文件都应该是同一属主属组否则不要轻易加-R。一个安全稳妥的习惯是先确认目录结构再针对性地改。给你一个反面例子某次我为了图省事对/data/www直接执行chown -R deploy:web /data/www结果把里面一个被监控着的日志文件的属主也改了监控脚本因为日志文件属主变化而报错折腾了半小时才定位到。现在我的习惯是加-R之前先执行find /data/www -maxdepth 1 -type f -ls这类命令大概扫一眼目录内容心里有数再动手。chgrp命令则专门用来修改属组chgrp -R devteam /data/shared。很多人觉得有chown :devteam了就没必要记chgrp但我两个都会用因为语义上chgrp更明确别人阅读脚本时一眼就能看出“这里只改了组没改文件属主信息”降低误操作概率。3.3 ACL 精细授权当传统权限不够用的时候传统权限模型只有属主、属组、其他三类对于多用户多角色的复杂协作场景它很快就卡住了。比如/data/project属主是 zhangsan属组是 devteam现在来了一个咨询顾问他既不是 devteam 的成员也不需要进入 devteam却要临时读取项目文档。按照老办法我只能把他加到 devteam 里这等于给了他对整个项目的所有权限风险扩大了。这时候就应该用 ACL。ACLAccess Control List访问控制列表允许你在传统权限体系之上为指定用户或组单独配置读写执行权限不需要改变文件的属主或者属组。它的使用前提是文件系统挂载时开启了 ACL 支持绝大多数 Linux 发行版默认已经开启。设置用户 ACL 的语法是setfacl -m u:wangwu:r-- /data/project/README.md这条命令的意思是给用户 wangwu 单独设置对 README.md 的只读权限。如果还要给某个组加权限就写成setfacl -m g:consultants:r-x /data/project查看文件当前的 ACL 信息用getfaclgetfacl /data/project/README.md你会看到除了常规的owner:zhangsan、group:devteam之外多了一行user:wangwu:r--。这就是 ACL 存在的意义它在不改变文件属主属组的情况下给指定主体单独开了一扇门。ACL 的递归处理也有讲究。给目录递归设置 ACL 时通常要同时配合“默认 ACL”setfacl -R -m u:wangwu:r-- /data/project setfacl -R -d -m u:wangwu:r-- /data/project第二行的-d是设置默认 ACL表示这个目录以后新建的文件也自动继承同样的 ACL。这个设计非常像 SGID 目录的效果。我踩过的坑是第一次只用了-R没加-d后来新创建的文档完全没有对顾问的读取权限顾问反复找我确认是不是权限系统坏了。设置默认 ACL 之后这个问题才彻底解决。移除 ACL 用setfacl -x或-b。-x后面跟具体的用户或组比如setfacl -x u:wangwu /data/project/README.md-b是把文件的所有 ACL 项全部清掉恢复到传统权限模式。注意ACL 权限并不是无限叠加的。权限判断的默认顺序是先看当前进程的用户是不是文件的属主是就看属主权限如果不是再看有没有针对这个用户的 ACL 项再没有就看属组权限最后才看其他用户权限。理解这个顺序才能解释某些“权限好像对不上”的诡异场景。3.4 sudo 权限配置不是把用户拉进 sudo 组就万事大吉sudo 是 Linux 日常操作里用得最多的提权工具。但正因为常用很多人把 sudo 权限当成“一键配置”把用户加入 sudo 组然后万事大吉。这种做法在小团队内部勉强可行但只要机器数量一多、人员一复杂很快就会出现权限失控。我的建议是尽量用/etc/sudoers.d/下的独立文件做精细规则而不是直接编辑主配置文件/etc/sudoers。独立文件的好处是职责清晰删除也方便。配置一个允许某个用户重启特定服务的规则可以这样写zhangsan ALL(root) NOPASSWD: /usr/bin/systemctl restart nginx这一行的含义是用户 zhangsan 可以在任何主机上以 root 身份执行systemctl restart nginx这条命令并且不需要输入密码。如果你希望必须输入密码把NOPASSWD:去掉即可。还有一种常见需求是要允许某个用户以某个特定身份运行命令比如让运维执行systemctl管理某个服务。更细的写法是zhangsan ALL(root) /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx注意sudo 对命令的白名单匹配是“整条命令”匹配的。/usr/bin/systemctl restart nginx这个条目不会让你执行systemctl restart mysql或者systemctl stop nginx。如果你想支持好几个服务就得把命令都列出来。这个特性既是限制也是保护用的时候千万别图省事写一个泛化的模糊匹配。配置完成后我刚接手系统时的第一件事永远是执行visudo -c做语法检查。生产环境上千万不要跳步直接改/etc/sudoers一旦语法错误sudo 直接罢工后续救场的痛苦难以言表。sudo 权限排查也很有讲究。用户反馈“sudo 用不了”先看他是普通用户还是 root再看他在/etc/sudoers或相关独立文件里的条目最后看命令是否在 PATH 里。另一个常见问题是用户在sudo -u切换到别的用户时环境变量不是目标用户的环境导致跑脚本时报找不到命令。我自己就踩过这样的坑脚本里用了一个自定义的JAVA_HOME直接sudo ./run.sh时一切正常改成sudo -u appuser ./run.sh后突然说 java 找不到最后才发现appuser的.bashrc里根本没有设置JAVA_HOME。现在统一在脚本开头用绝对路径或者显式export环境变量问题就绝迹了。4. 常见问题排查与避坑实录4.1 权限明明给了为什么还是 Permission denied这类问题是权限管理里出现频率最高的一类。排查不要只看文件本身权限要顺着路径一层层看。比如用户访问/data/project/README.md那么/、/data、/data/project这三个目录的权限都会被检查。目录的r、w、x缺哪个都不行尤其是x权限没有它就无法穿过该目录。我经常做一件事逐层执行ls -ld /data /data/project。如果中间某一层是dr-x------或者drw-------那问题大概率就出在这一层。去年我帮一个同事排查“无法访问共享代码仓库”的问题文件权限全是 755ACL 也加了最后发现竟然是/data目录本身的权限是 700其他用户根本进不去。这种问题只看最深层文件权限的话排查到天亮都找不出来。还有一种情况是文件系统挂载时带了noexec选项导致目录下所有可执行文件都无法运行。解决方案是重新挂载把noexec去掉比如mount -o remount,exec /data这类问题通常不是权限体系的问题而是挂载参数问题。遇到“权限没问题但执行报错”多往挂载选项这边想一想。4.2 文件可以被写入却删除不了先查目录写权限这个问题很多人第一次遇到时非常困惑。ls -l显示的明明是-rw-rw-r--当前用户属于属组按道理可以写文件但执行rm时却提示 Operation not permitted。原因是删除文件不是看文件本身的权限而是看文件所在目录的写权限。文件本身的写权限只代表你能修改文件内容不代表你能删除或重命名这个文件。你拥有的只是那个文件条目的修改权而操作目录条目需要目录的写权限。排查方法是ls -ld /data/project/检查属组的写权限位是否存在。如果目录权限是drwxr-xr-x那组内用户只能进入目录无法创建或删除文件。再加上如果目录设置了 Sticky 位即使是文件属主以外的用户也不能删除你的文件。被 Sticky 位坑过的人也很多。/tmp目录虽然任何人可写但普通用户无法删除他人的临时文件这和“目录可写就可以删文件”的直觉是相悖的。理解 Sticky 位的语义之后这类报错立刻就能定位。4.3 sudo 执行找不到命令但直接执行却可以这个问题常见于用了自定义编译安装的软件。以某运维同学为例他明明执行sudo nginx -t时可以正常运行但部署到另一台机器后变成sudo: nginx: command not found。原因是 sudo 默认的secure_path变量很保守通常只包含/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin等系统目录。而用户自己安装到/opt/nginx/sbin/nginx的二进制sudo 的 PATH 里根本没有。解决办法有两种。第一种是每次都用绝对路径sudo /opt/nginx/sbin/nginx -t。第二种是修改 sudoers 里的secure_path把这个目录加进去。我个人更倾向于第一种因为改全局secure_path的影响面较大一旦加错目录还可能引入安全隐患。另外要注意sudo -u切换用户后的 PATH 问题和上面原理一致。目标用户环境里没设置某个变量sudo 就能找到命令反而会让我怀疑是不是有什么可疑路径被注入到了secure_path。4.4 权限排查流程图式的速查思路虽然平时操作时不用画图但为了快速定位我在脑子里始终有一个固定的排查顺序确认执行者身份和所属组id、groups。查看文件属主、属组、权限位ls -ld。逐层确认路径目录的权限尤其是x位。用getfacl查看是否有 ACL 项影响判断。确认文件系统挂载选项是否有限制。检查特殊权限位SUID/SGID/Sticky是否造成干扰。几乎 90% 的权限问题都能靠这六步定位。特别是当你脑子里同时处理多个项目、多套环境时这个顺序能避免东一榔头西一棒子的低效排查。4.5 写权限、执行权限错付后的恢复思路权限配错的时候应迅速把入口堵住。比如某团队共享目录被误设成chmod -R 777表面上每个人都能操作实际上任何一个普通用户都能删掉别人的代码、篡改配置文件。遇到这种问题我一般这样处理先把目录改回合理权限比如chmod -R 755 /data/shared再用chgrp把属组改回团队组必要时配合 SGID Sticky 目录 ACL 的组合方案做到既对团队成员开放又保持文件属主不可被随意删除。还要强调一点对于权限被误改过的目录不要迷信“改回权限就万事大吉”。需要检查是否有多余的 ACL 残留是否有非预期属主的文件是否在深层次子目录里藏了带 SUID 的可执行文件。我在一次事故复盘时发现误操作不仅改了根目录权限连某个子目录里一个很久之前的备份脚本都被改成了 777里面写着数据库明文密码。扫描清理过程整整花了一下午教训非常深刻。5. 用户权限管理的扩展思考5.1 用时间和网络维度限制权限说完文件权限和 sudo 配置我想再补充一个经常被忽略的维度权限控制不应该只考虑“谁能访问”还要考虑“什么时候能访问”和“从哪里访问”。比如某个合作方工程师只在每周三到周五远程接入那他的账户就可以配合相关配置在登录服务端限制访问时段同时还可以从网络层限制他的来源 IP 地址。这类设置听起来复杂其实系统级配置只是几行文本的事。我维护的机器里凡是外包或临时人员一律走这种“时间窗 来源 IP”的限定方式账号到期前完成回收。这不是为了搞等级制度而是把权限按最小必要原则落到位。5.2 权限审计看不见比不给更危险权限审计是很多人不想碰但又绕不开的活。我给自己定了一个规矩每季度至少检查一次机器的关键目录权限、SUID 文件列表、sudo 规则列表。具体就三件事找出系统里所有 UID 为 0 的账户root 及其他潜在超级用户找出所有-perm -4000和-perm -2000的文件检查/etc/sudoers.d/下所有新加的文件是否符合预期这一步不需要额外安装工具定期执行几条命令就可以完成。权限管理的核心不是配一次就永久有效而是要让它始终跟实际情况对齐。团队人员变动、机器角色调整都会导致原来的配置逐渐失去合理性。跑审计的意义就是不断修正失配项属于典型的“花小力气省大力气”。6. 写在最后权限管理的心态问题做 Linux 权限管理这些年我最大的体会是它不像写代码那样有立即反馈的“成就感”更像是在维护一栋大楼的门禁系统。你永远不会因为所有门都锁对了而被表扬但只要有一扇门忘记锁后续的麻烦就会铺天盖地地涌过来。所以我的工作习惯是三个坚持坚持最小权限也就是默认不给权限用到哪开哪坚持变更记录每次chmod、chown、usermod都记录下“谁在什么时间出于什么原因改了配置”坚持定期审计光有配置没有审计等于纸上谈兵。文章最后分享一个实战小技巧如果你实在拿不准某个权限调整会不会影响现有服务先在一台测试机上复现同样的目录结构用同一套配置操作一遍再观察 10 分钟。很多时候生产环境出问题都是因为省掉了这一步直接在生产上做验证。这个习惯帮我躲过了至少五次潜在事故你也可以试试。