Linux权限管理实战:从用户组、chmod到特殊权限位与文件属性 拿 Linux 权限这块来说很多用了一两年服务器的朋友停留在“chmod 777 解决问题”的阶段。文件删不掉777。网站跑不起来777。结果安全一塌糊涂被人拖库了还不知道怎么进来的。我自己刚接手服务器那会儿也这么干过后来在某台机器上被植入了挖矿脚本排查了大半夜才意识到权限这一课必须补扎实。这篇文章我就把用户、用户组、权限和文件属性这几个东西串起来讲一遍。不会只讲命令怎么敲我会把每个命令背后的机制、为什么这么设计、实际生产环境里怎么配合使用以及我踩过的那些坑一并交代清楚。适合刚入门、以及对权限管理还没有形成系统认知的读者。1. 权限模型的核心设计为什么 Linux 要分用户、用户组和“其他人”1.1 三类身份的划分逻辑Linux 是一个多用户操作系统多用户的含义是同时有多个人通过账号在同一台机器上工作。如果大家都能读、能写任何文件系统早就乱套了。所以内核引入了一个非常朴素但又极其有效的模型对每个文件系统只关心三类身份——文件的属主User、文件的属组Group、以及除这之外的所有人Others。这个模型做到了什么它把“谁能碰这个文件”分解成三个优先级明确的判断先看你是不是属主再看你是不是属于属组都不是你就是“其他人”。判断顺序一旦确定权限就非常清晰。实际使用中一个文件通常由某个用户创建创建者自动成为属主同时继承创建者当前所属的主组作为属组这就形成了最初的权限矩阵。举个例子某位同事用useradd创建的账号dev01他提交的代码仓库里.env配置文件属主是dev01属组是dev01权限是644属主读写、组只读、其他只读。如果后来你把另一个同事加到dev01组里他就能读.env但如果你只给了他普通用户身份他连看一眼的资格都没有。这种“按组分配访问资格”的思路隔离了不同业务线、不同项目的敏感性。1.2 权限位的真实含义读、写、执行不只是三个字母rwx三个权限位字面上分别是 read读、write写、execute执行。但落到不同类型的对象上它们的具体效果差异很大。对普通文件来说“读”意味着可以查看文件内容“写”意味着可以修改文件内容“执行”意味着可以把它当作程序运行。这三个权限是相互独立的你有读权限不一定有写权限有写权限不一定能执行。一个典型的例子是脚本文件必须同时具备读和执行权限才能正常跑起来因为解释器要先读脚本内容然后再执行它。对目录来说语义就完全不同了。目录的“读”权限决定的是你能不能列出目录里有哪些文件ls的权限“写”权限决定的是你能不能在这个目录里新建文件、删除文件、重命名文件“执行”权限决定的是你能不能进入这个目录cd的权限。这个区别最容易让新手栽跟头你看到目录是rwxr-x---以为有r就能进去其实没有x你根本cd不进去ls也会报 Permission denied。我强烈建议你把目录的“写”权限当成危险动作来看。因为目录的写权限一旦开放给某个用户他可以删除目录里任何他本不该碰的文件——前提是这些文件的属主没有额外的保护措施。这也是后面要讲“粘滞位”sticky bit的原因。注意对目录而言写权限管的是“目录内部的清单变更”不是“修改某个文件的内容”。想改文件内容看的是文件本身的权限。2. 用户与用户组管理从创建到切换的完整实操2.1 用 useradd 创建一个“干净”的用户很多人喜欢直接useradd test一把梭然后发现登录不上去或者目录结构不对。原因是useradd在多数发行版里的默认行为是不创建家目录、不设置密码、不开 shell。这是出于安全考虑的刻意设计——系统想要的是一个“最小化账号”。但作为日常使用者我们往往需要的是可以登录、有独立工作环境的用户。在实际生产环境里我常用的命令是这样一段useradd -m -d /home/dev02 -s /bin/bash -G wheel,devteam -u 2001 dev02拆开解释一下-m同时创建家目录。-d指定家目录路径通常跟-m搭配使用。-s指定登录 shell工作账号一般选/bin/bash。-G附加组账号可以属于多个组主组之外的都是附加组。这里把wheelsudo 组和devteam业务组都加进去了。-u手动指定 UID。这一步容易被忽略但如果你要对接 NFS、备份系统或数据库授权UID 不稳定会产生很大的麻烦。创建完之后别忘记设置密码否则这个账号还是登不进去passwd dev02它会交互式地让你输入两次密码。如果你是想在自动化脚本里批量设置也可以写成这样echo Dev02Passw0rd | passwd --stdin dev02个人经验密码政策一定要提前定好长度至少在 12 位以上混合大小写、数字和特殊符号。我在某次演练中见过用字典跑两分钟就把一台测试机的弱口令拿下的情况密码这东西真的是系统安全的第一道门。2.2 用户组管理groupadd 与 usermod 的正确用法组的作用是为一组用户提供共同的授权边界。创建组很简单groupadd devteam groupadd ops创建完之后你想把一个用户加入某个组可以用usermod -aG devteam dev02注意这个-a参数。它是 append 的意思表示“追加附加组”。如果没有-ausermod -G devteam dev02会把你之前设置的所有附加组全部清掉只保留devteam一个。很多时候组莫名其妙丢了就是因为忘了写-a。还有一个常见需求把某个用户从组里移出去。这个没有独立的usermod -dG之类参数你需要把想要的组重新列一遍usermod -G devteam dev02这样 dev02 仍然属于 devteam但会被从其它附加组移除。如果你想看用户当前属于哪些组用id dev02 groups dev02id会详细显示 uid、gid 和 groups 列表groups更简洁一些直接列组名。这两个命令在排查“为什么这个用户能访问某文件”时非常高效我建议每次用户报告“我没权限访问某某目录”时第一步就是id看清他到底在哪些组里。2.3 主组和附加组的区分以及实际应用场景每个用户有且只有一个主组primary group但可以有多个附加组supplementary groups。主组决定的是用户新建文件时文件默认的属组是哪一组附加组决定的是用户还能以哪些组的身份去访问资源。举例来说让dev02的主组是dev01同名组附加组包含devteam和wheel。当他创建一个新文件时ls -l显示的属组是他的主组dev01。如果项目目录是devteam组拥有他访问这些目录时是以附加组成员身份进入的读写权限以devteam组权限来判定。这样既保持了个人文件的默认隔离又能协同访问项目资源。生产环境里最常见的协作场景是 Web 项目部署目录。新建/var/www/project属组设为webgroup目录权限为2775关于 setgid 位下文详述把运维同事和前端的账号统统加入webgroup。这样大家都能在项目目录里写代码但你在家目录的个人文件别人仍然看不到。这就是主组作为“私域隔离”、附加组作为“协作桥梁”的组合拳。3. 权限管理核心chmod、chown、umask 与特殊权限位3.1 chmod 的两种模式字符模式与数字模式chmod大概是 Linux 里出现频率最高的命令之一。它支持两种写法字符模式和数字模式。字符模式的写法是chmod ux file、chmod g-w file、chmod or file直接对某一身份的某个权限做增删。优势是直观、好记特别适合在敲错的边缘试探时使用。例如chmod ux deploy.sh chmod go-w config.yaml表示给属主增加执行权限同时去掉组和其他人的写权限。数字模式则是把rwx当作二进制编码读4、写2、执行1每位相加得到一个 0 到 7 的数。三个数字分别对应属主、属组、其他人。chmod 750 file表示属主全部权限4217、属组读加执行415、其他人没有权限0。数字模式非常适合批量操作和脚本编写因为它编译器友好、无歧义。我个人在写部署脚本时几乎只用数字模式配合注释说明每个数字的含义。字符模式适合交互式快速调整数字模式适合可复现的自动化两者结合使用对你的效率和安全意识提升都有帮助。下面这张对照表建议保存下来数字权限含义组合解读7rwx读写执行完整权限6rw-读写修改内容5r-x读执行查看并运行4r--只读仅查看0---无权限完全不可访问目录常用755每个人都能进只有属主能写、750组内合作且对外隔离、2775组内协作加 setgid下文细讲。文件常用644用户可写其他人只读、640属主可写组内可读其他不开放。3.2 chown 与 chgrp属主和属组的精确调整创建文件后默认属主是当前用户默认属组是当前用户的主组。但实际业务里文件经常需要“易主”尤其是网站需要由 nginx 或 php-fpm 账号读取而代码更新又由开发账号执行的时候。chown dev02:devteam /data/app/config.yaml这条命令把属主改成dev02属组改成devteam。如果只改属主可以写成chown dev02 /data/app/config.yaml只改属组可以写成chgrp devteam /data/app/config.yamlchown也能一次递归处理目录下所有内容chown -R dev02:devteam /data/app/关于递归我的经验是“能不用 -R 就不用 -R”。因为在大型代码库里一次性把所有文件的所有权都改了后面想恢复某个特定目录的属主会非常痛苦。更合理的做法是先chown根目录再用find精确调整需要特殊处理的子目录。比如chown dev02:devteam /data/app find /data/app/cache -type f -exec chown www-data:www-data {} \;这样既保证了整体归属正确又保留了特殊目录的独立权限。曾经见过一位同事对整个目录chown -R 某普通用户把一些系统需要读的文件也改了导致服务间歇性调用失败排查了整整一天。3.3 umask决定“新建文件默认权限”的那把尺子每次新建文件或目录系统都会根据 umask 计算默认权限。默认情况下文件的基础权限是666目录的基础权限是777。umask 的作用是“扣掉”这些基础权限中的某些位。比如 umask 是022那么新文件的权限是666 - 022 644新目录是777 - 022 755。umask 的022意味着“组和其他人都去掉写权限”。如果 umask 是027新文件是640新目录是750这样其他组就完全不可见。查看当前 umask直接执行umask临时修改umask 027永久修改需要写进用户的 shell 配置文件比如/etc/profile、~/.bashrc、~/.profile。不同发行版惯用配置文件略有差异但思路一致。我建议在服务器上统一把创建用户的 umask 设置成027而不是默认的022。这么做的理由是默认022会让其他用户对所有新文件具有读权限对于包含密钥、账单、配置数据的目录来说风险太大。而027保证了“组内可读、组外无法访问”兼顾协作与安全。踩过的坑是某次因为 umask 是022我生成的一个私钥文件居然能被同机器上另一个低权限用户读走还好及时发现才没有造成泄露。提示修改 umask 后需要重新登录或者执行source ~/.bashrc才会在当前 Shell 中生效。脚本里如果依赖新权限最好在脚本开头显式umask 027避免依赖环境变量。3.4 特殊权限位SUID、SGID、Sticky Bit普通的 rwx 只是基础Linux 还有三个特殊权限位理解它们你在权限管理上才算真正进阶。SUID4当可执行文件设置了 SUID任何用户运行该文件时进程的有效用户 ID 会临时变成文件属主的 UID。最常见的例子是/usr/bin/passwd平时普通用户要修改自己的密码但密码存储在/etc/shadow里这个文件普通用户根本没有权限访问。有了 SUIDpasswd命令执行期间就临时拥有 root 的权限从而能修改/etc/shadow。看到这里你应该明白SUID 是“提权”种子选手。日常运维中尽量不要给普通文件设置 SUID。检查系统中有哪些 SUID 文件可以用find / -perm -4000 -type f 2/dev/null如果发现意外的 SUID 文件多半是被人动了手脚赶紧查。SGID2SGID 的含义分两种情况。对文件来说运行时会继承文件的属组 ID这个场景较少用到。对目录来说SGID 的效果非常实用任何在此目录下新建的文件其属组会自动继承目录的属组而不是创建者的主组。这就是团队协作目录的标准配置。典型设置chmod 2775 /var/www/project这样即使dev02的主组是dev02只要/var/www/project的属组是webgroup且设置了 SGID他在目录里新建的文件会自动归入webgroup组内所有人都能按组权限读写。这比每次手工chgrp要靠谱得多。Sticky Bit1粘滞位一般用于目录。设置了粘滞位的目录里面的文件虽然允许大家写入但只有文件属主、目录属主和 root 能删除或重命名。最典型的就是/tmp权限显示为drwxrwxrwt最后的t就是粘滞位。如果没有它任何人都能删除/tmp下其他用户创建的临时文件系统会乱套。设置粘滞位chmod t /data/shared # 或者用数字模式 chmod 1777 /data/shared看到1777前面的1代表粘滞位后面777表示全员可读可写可进入。这也是共享目录的常见姿势大家可以写入自己的文件但谁也不能乱删别人的文件。4. 文件属性与不可变标志chattr 和 lsattr 的进阶玩法4.1 查看文件属性的 lsattrLinux 的文件系统层还有一个“属性”的概念这是独立于 rwx 权限之外的。rwx 权限控制的是“谁能访问、能否修改内容”而文件属性控制的是更底层的操作比如“是否允许修改元数据、是否允许追加、是否允许删除”。常用的属性标志包括aappend only文件只能追加写入不能截断、不能删除。iimmutable不可变。设置了i属性后任何人包括 root都不能修改、删除、重命名该文件除非先移除i属性。eextent 格式标志一般表示文件正在使用 extent 映射这属于正常的现代文件系统标志不用管。u删除时保留数据允许恢复但需要文件系统层面支持。查看文件当前属性lsattr /etc/ssh/sshd_config输出类似----i--------e-- /etc/ssh/sshd_config其中i表示设置了不可变属性。4.2 用 chattr 加固关键文件chattr用来修改文件属性。最高频的用法是给配置文件和脚本加ichattr i /etc/ssh/sshd_config chattr i /etc/sudoers加了i之后即使某个进程或攻击者拿到了 root 权限也没办法直接篡改这些文件。这在防篡改场景下非常有价值比如曾经有人入侵你的机器替换了sshd_config来开后门。如果这个文件加了i属性替换操作会直接报错后门就留不下了。想解除保护chattr -i /etc/ssh/sshd_config追加日志文件时用achattr a /var/log/secure这样日志只能追加不能被清理、不能被人篡改历史记录。不过要提醒一下chattr在很多容器环境、部分网络文件系统上是不生效的。比如在 Docker 容器里的 overlayfs 上操作某些层级的文件可能会提示不支持的标志。另外chattr i也不能滥用在业务频繁写入的文件上否则服务会直接报“Permission denied”。我见过有人给一个应用目录整个加i导致进程无法写日志业务瞬间全挂。正确姿势是只给静态配置、密钥文件、关键启动脚本加锁不给动态写入的目录加锁。4.3 文件属性与权限的协作思路文件属性与 rwx 权限是两层防御。rwx 权限面向“用户身份”做控制文件属性面向“文件操作”做控制。攻击者即使突破第一层拿到了文件写权限第二层的i属性仍然能拦住篡改。我在加固服务器时的标准流程是关键的配置文件sshd_config、sudoers、rc.local、系统级 service 文件先收紧权限到600或640再使用chattr i锁定。这样即使某个服务账号被攻破了也没法轻易替换配置。当然自己后续要改配置时记得先chattr -i再修改改完再加回i。这一步很容易忘建议在操作规范里写明。另外一个实用组合给日志文件加a属性同时配置日志轮转logrotate。轮转时如果日志文件有a属性通常会导致 rename 失败。解决办法是在 logrotate 脚本的 prerotate 里先chattr -a轮转结束后再chattr a。这些细节真实项目中几乎都会遇到提前准备好能省不少时间。5. 常见问题与排查技巧实录5.1 为什么用户能列出文件名却打不开文件这个典型场景特别容易让人困惑。当我碰到“ls 能列出目录内容但读取某个具体文件却提示 Permission denied”的情况时会沿着两点去排查第一查文件本身的权限。ls -l看是不是600、640等。有很多配置文件为了防止泄露默认就是属主可读。如果你不是属主、也不在属组里那当然读不了。第二查目录的权限链。访问一个文件的路径比如/data/secret/app/config.yaml实际上要经过/、/data、/data/secret、/data/secret/app每一层目录的x权限。中间任何一层少了x最终都无法真正触达目标文件即使文件本身的权限是777。排查这类问题我习惯一步步往上走namei -l /data/secret/app/config.yamlnamei会列出路径上每一级目录的权限一目了然。这个命令用的人不多但在权限列队排查时非常高效。5.2 为什么设置了 SUID 却不执行挂载选项可能拦着你有些场景下你给某个二进制设置了 SUID但实际运行的时候却没有生效进程还是以普通用户身份在跑。原因多半是文件系统挂载选项里有nosuid。特别在多分区服务器、容器环境、云盘挂载环境中安全默认值往往就是nosuid。排查方式是用mount或findmnt查看挂载选项mount | grep /data findmnt /data如果确实带了nosuid那 SUID 位再怎么写也没用。这时候你有两个选择重新挂载去掉nosuid不太推荐因为你等于为了兼容一个高风险特性而弱化了整体安全或者重新设计方案不让业务依赖 SUID。比如把需要执行特殊操作的服务改成用 sudo 规则管理而不是依赖 SUID 二进制。5.3 文件删不掉先看属性和权限碰到“文件明明在我自己的目录下但我删除它却提示 Operation not permitted”这类问题第一反应应该不是chmod 777而是先lsattr看有没有隐藏属性。我之前遇到过一次典型的排障过程一个临时脚本删不掉权限是664当前用户是属主目录权限也正常。折腾了半天后来lsattr一看文件被加了i属性。之所以出现是因为前面测试时用chattr i保护过它后来忘了解除。解除后立刻就能删除chattr -i file.sh rm -f file.sh另一种常见情况是粘滞位目录下的其他人文件。你想删除/tmp下另一个用户创建的临时文件虽然/tmp是1777但你既不是文件属主也不是目录属主没有 root 权限就是删不掉。这是设计使然不是故障。5.4 ACL传统权限不够用时怎么办传统权限只有“属主、属组、其他人”三组。如果想给张三读权限、给李四读加写权限、给王五完全无权限传统模型搞不定。这个时候就需要 ACLAccess Control List。ACL 允许你为任意用户或任意组单独指派权限。常用工具是setfacl和getfacl# 给用户 zhangsan 读权限 setfacl -m u:zhangsan:r /data/project/data.txt # 给组 ops 读写权限 setfacl -m g:ops:rw /data/project/data.txt # 移除某个用户的 ACL 项 setfacl -x u:zhangsan /data/project/data.txt # 查看 ACL 信息 getfacl /data/project/data.txt开了 ACL 的文件在ls -l的权限位末尾会多出一个号比如-rw-rw-r--。ACL 是传统权限模型的有力补充但它也增加了系统的复杂度建议在需要的目录局部使用别全局铺开。另外做备份时要确保备份工具支持保留 ACL否则恢复出来 ACL 全丢了。5.5 权限排查速查表症状优先检查项常用命令能 cd 但 ls 提示无权限目录r权限不足ls -ld dir能 ls 但进不了目录目录x权限不足namei -l path文件明明在组里却报权限不足该用户不在目标组中id username新建文件属组不一致目录缺少 SGID 位ls -ld dir确认有s删除文件被拒文件属性或粘滞位限制lsattr file看属主关系SUID 不生效挂载选项 nosuidmountACL 未按预期生效文件是否存在getfacl file尾声一个从实战中养成的习惯我养成的一个习惯是每过一段时间就用find扫描一次系统里的敏感权限设置# 找出所有 SUID 文件 find / -perm -4000 -type f 2/dev/null # 找出所有所有权异常的文件特别关注 home 目录和 /tmp find /home -nouser -o -nogroup 2/dev/null # 找出权限过宽的关键文件 find /etc -type f -perm -ow -exec ls -l {} \; 2/dev/null这些命令看起来很基础但真的能防患于未然。Linux 的权限体系说穿了就是一套“身份 访问控制”的组合逻辑。你把用户、用户组、普通权限、特殊权限和文件属性这几层按照“最小够用”的原则去配置系统的稳定性和安全性都会提升一个台阶。配置权限之前先问自己一句这个文件到底需要谁读、谁写、谁执行答案越精确配置越安全。