
在Linux上折腾了这么多年我发现很多新手的第一个坎既不是命令也不是内核而是文件权限。你明明照着教程敲了命令可程序还是报“Permission denied”你辛辛苦苦配好的网站换个用户访问就404。说到底是因为没搞懂Linux控制文件访问的那套规则。这篇文章就专门讲讲文件访问控制这回事——从最基础的rwx权限位到chmod、chown、umask再到SUID/SGID、ACL和sudo我会把原理和实操放在一起讲还会穿插一些实际踩坑和排查经验。只要你跟着过一遍以后再碰到权限问题心里能有个清晰的排查路线。1. 文件权限的基础认知先搞懂rwx和文件类型1.1 三种身份属主、属组、其他人任何Linux文件都带着三个圈子的权限属主owner、属组group和其他人others。理解这个设计可以拿合租屋来打比方房子是全体室友合租的但有三种人——房东属主、和你同住的人属组、路过的陌生人其他人。房东可以改合同室友可以日常使用陌生人未经允许只能隔着门看看。Linux把这种“身份差别”落到了每一个文件上。你可以在终端敲ls -l查看当前目录的详细信息第一段就写着这三类身份的权限。为什么要分成三组而不是每个人单独设因为早期Unix崇尚简单三组权限足够覆盖绝大多数场景。后来大家发现还不够精细于是才有了第4章要讲的ACL。但基础始终是这三组权限。每个权限位用一个字母表示r读、w写、x执行没有权限就用-代替。三个一组顺序固定属主权限、属组权限、其他人权限。所以一串权限长成rwxr-xr--这样的九宫格。注意这个顺序绝不能乱读的时候要从左往右拆成三段看。1.2 文件类型是文件、目录、还是链接ls -l输出的最前面还有一个字符它不是权限而是文件类型。最常见的几个-普通文件d目录l符号链接b块设备c字符设备。很多教程会忽略这个细节但新手看错了会闹笑话——比如你以为自己在给文件加执行权限结果那是个目录或者是软链接结果行为完全不一样。尤其是符号链接它的权限位通常显示成lrwxrwxrwx但实际上最终生效的是被指向的那个文件的权限链接本身的权限基本没有意义。如果你用chmod去改一个软链接的权限大概率会导致链接失效或报错正确做法是用chmod -h或者直接去改目标文件。这个坑我当年至少踩过三次。目录的权限规则和普通文件完全不同。普通文件的读权限表示你能读取文件内容写权限表示能修改文件内容执行权限表示能把这个文件当作程序运行。目录的“读”权限表示能列出目录里有哪些文件名“执行”权限表示能不能进入这个目录cd 进去而“写”权限表示能在这个目录里新建、删除、重命名文件。注意删除一个文件其实不看这个文件的权限而是看你对该文件所在目录有没有写权限。这个反直觉的规则是很多权限问题百思不得其解的根源。1.3 ls -l 输出逐字段拆解我们来实际拆一条典型的ls -l输出-rw-r--r-- 1 root root 1024 Jan 1 08:00 test.txt从左到右分别是-rw-r--r--文件类型 三组权限。这里类型是普通文件属主可读写属组可读其他人可读。1硬链接数体现这个文件有多少个名字。root属主。root属组。1024文件大小单位字节。Jan 1 08:00最后修改时间。test.txt文件名。你可以用stat test.txt看更明细的信息但日常排查先学会看ls -l就够了。权限位九个字符每三个一组这个格式记牢了后面所有操作都围绕它展开。这里还要补一个知识点为什么权限可以用数字表达因为r、w、x本质上就是三个二进制位有权限记1没权限记0。所以rw-对应二进制110换算成十进制就是 426。我用个表格把常用组合列出来你收藏着用得着权限二进制数字---0000--x0011-w-0102-wx0113r--1004r-x1015rw-1106rwx1117后面写chmod 755这种命令时你脑子里要立刻浮现出7rwx5r-x5r-x。2. 修改权限与属主chmod、chown、umask2.1 chmod 符号模式按人加权限权限不是生来固定的你随时可以用chmod改。改权限有两种表达方式一种是符号模式一种是数字模式。符号模式的套路是谁 操作 权限。谁用u属主、g属组、o其他人、a所有人表示操作是加、-减、精确设置权限就是 rwx。比如chmod ux install.sh # 给属主加执行权限 chmod g-w data.txt # 去掉属组的写权限 chmod or readme.txt # 把其他人的权限精确设置为只读 chmod arx /opt/app # 给所有人加读和执行权限符号模式适合快速微调。比如你刚下载一个脚本想让它能运行直接chmod x script.sh就够了它会同时给三种身份都加上执行权限。这里要注意如果文件本身没读权限即使加了执行权限也没法运行脚本需要读入内容所以常用做法是chmod 755 script.sh或者chmod urwx,gorx。2.2 chmod 数字模式一把设到位数字模式更直观属于“三合一”的写法。chmod 754 file就是属主7rwx、属组5r-x、其他人4r--。这个写法的好处是设置后结果明确不会像那样依赖原有权限。实际项目中目录和文件的常用权限组合有固定套路普通文件一般给644rw-r--r--或755rwxr-xr-x目录一般给755或775。为什么文件不能给755如果你给了意味着任何人都能执行它但对于普通数据文件来说执行权限没有任何意义还增加了被利用的风险比如某个脚本被篡改后可直接作为程序运行。所以原则是文件保持644只有明确需要执行的脚本、二进制程序才给755或更严格。递归设置目录用-R参数chmod -R 755 /data/app。但递归有一个副作用它会连目录里的文件也统一设成755通常这不是你要的结果。更好的习惯是分开处理目录用755文件用644。后面我会给一个用find完成的精准改法。2.3 chown 修改属主和属组chmod只改权限位不改归属。文件属于谁要用chown来改。基本用法chown zhangsan:developers report.pdf # 同时改属主和属组 chown zhangsan report.pdf # 只改属主 chown :developers report.pdf # 只改属组 chown -R www:www /var/www # 递归修改整个目录很多服务启动后会以特定用户运行比如Nginx用www-data你可以chown -R www-data:www-data /var/www/html让程序能读写这些文件。另外还有个chgrp命令专门改属组但我更喜欢在chown里用冒号一起处理少打一次命令。要注意-R的威力它会把目录下所有文件、子目录的属主全部改掉。如果你只想改目录本身就不加-R。之前有同事一条chown -R root:root /直接把服务器干趴下这种惨案网上比比皆是所以敲递归命令时我强烈建议先敲pwd确认自己在哪再看一眼路径有没有拼错。2.4 umask新建文件的默认权限你新建一个文件时它的权限不是凭空来的而是由系统默认值和umask共同决定。默认值普通文件是666因为文件本身默认没有执行权限目录是777。然后系统会把这个默认值减去umask得到最终权限。当前用户的umask可以用umask命令查看通常显示0022。那么普通文件的实际权限就是666 - 022 644目录就是777 - 022 755。也就是说默认情况下你touch出来的文件是rw-r--r--你mkdir出来的目录是rwxr-xr-x。为什么要把写权限和部分执行权限“减掉”这是安全基线新建的东西默认不让组内其他人或陌生人乱写。如果你想让新文件自动变成rw-rw----可以执行umask 026。这个命令只影响当前shell要想永久生效写到/etc/profile或~/.bashrc里。这里有个常见误解umask 022不是“扣掉2”而是“从默认权限里去掉2对应的写位”。因为0是7里减完还是72是7减完变成5。所以文件644的由来就是这么一点点减出来的。理解了这一点你就能自己推算出任何umask下的默认权限。2.5 实操给网站目录设置一套合理权限咱们来个完整案例。假设你部署了一个静态网站Nginx以用户www-data运行代码放在/var/www/mysite。目标是网站管理员账号deploy能改代码www-data能读到所有文件其他人一律禁止访问。第一步设定属主和属组chown -R deploy:www-data /var/www/mysite第二步设置目录和文件权限find /var/www/mysite -type d -exec chmod 750 {} \; find /var/www/mysite -type f -exec chmod 640 {} \;这里目录是750rwxr-x---deploy全权www-data组读和执行能进入目录并能读取目录列表其他人没有权限。文件是640rw-r-----deploy可写www-data组可读其他人不可读。为什么不让www-data有写权限因为Nginx跑在它名下一旦有漏洞攻击者能拿到www-data的权限如果还能写文件站点就可能被篡改。所以只给读和执行是“最小权限”原则的落地。如果后续想单独给某个运维同事开临时查看权限就用setfacl这就是第4章的内容。3. 深入特殊权限SUID、SGID、Sticky Bit3.1 SUID程序运行时临时“变身”普通权限之外还有三个特殊位第一个叫SUIDSet User ID。它的效果是当用户执行某个带SUID的程序时该进程的有效身份会变成程序属主而不是当前用户。这句话听着绕我用passwd命令举例就清楚了。passwd要修改/etc/shadow文件这个文件只有root能写。普通用户要改自己的密码按理说根本没权限写shadow。所以系统给/usr/bin/passwd加了一个SUID位你执行它时进程临时以root身份运行才有能力更新shadow文件。你看下ls -l /usr/bin/passwd -rwsr-xr-x 1 root root 68208 ... # 属主权限里有个s不是rwx的x那个s就是SUID。它显示在属主的执行权限位。如果属主本来没有执行权限那会显示成大写S表示“设了SUID但执行权限没开”这是无效设置。设置SUID用chmod us file或chmod 4xxx file。但我要给你泼盆冷水别给脚本和二进制程序乱加SUID。这是Linux上最经典的特权提升漏洞来源之一。你自己写的脚本加SUID往往因为解释器的原因根本不生效而给某个有漏洞的二进制程序加SUID等于把一把写着“root”的钥匙递给所有能执行它的人。生产服务器上非必要不设SUID这是底线。3.2 SGID目录新文件自动继承属组SGID有两种作用用于文件时进程有效组变成属组用于目录时效果更常用——目录里新建的任何文件和子目录其属组自动继承目录的属组而不是创建人的默认属组。想象一个团队共享目录/data/team属组设为developers并设置SGID。那么不管张三还是李四在这个目录里新建文件文件属组都会是developers而不是各自的私人组。这样整个团队都可以按组权限协作访问不用每次chgrp。chown :developers /data/team chmod gs /data/team ls -ld /data/team drwxrws--- 2 root developers 4096 ... # 属组权限位上的x变成了s设置命令是chmod gs file或chmod 2xxx file。注意如果目录本身没有给组执行权限那个s会显示为大写S同样是不起作用的。3.3 Sticky Bit共享目录里的“防删锁”第三个特殊位叫Sticky Bit粘滞位。它通常用在共享目录上典型例子是/tmpls -ld /tmp drwxrwxrwt 20 root root 4096 ...权限末尾的t就是Sticky Bit。它的规则在一个设置了Sticky Bit的目录里即使你对目录有写权限也不能删除或重命名别人的文件只有文件属主、目录属主和root能删。这解决了什么问题大家都往/tmp里放临时文件如果任何人都能互相删那就乱套了。设置命令chmod t dir或chmod 1xxx dir。查权限时如果末尾是t表示有执行权限如果是大写T说明设了Sticky Bit却没有执行权限同样无效。3.4 特殊权限的数字组合与验证三个特殊位也参与数字权限而且排在普通权限前面4表示SUID2表示SGID1表示Sticky Bit。所以chmod 3777 /usr/local/shared就表示设置SGID加Sticky Bit普通权限777。但我不推荐把这几个数字揉在一起写因为代码审查时很难一眼读懂。我最常用的写法是分开加chmod 1777 /tmp/scratch # 或直接 chmod t /tmp/scratch chmod gs /data/team chmod us /opt/setuid-tool验证时不要只看ls -l更准确的是用statstat -c %A %a %U %G /data/team它会同时显示符号权限、数字权限和属主属组。遇到s或t显示异常时优先检查对应普通执行位是否打开。这个细节排查问题时能帮你少走很多弯路。4. 进阶访问控制ACL 与 sudo4.1 为什么需要ACL基础权限只能控制“属主、属组、其他人”三类人。实际工作中经常会有更细的需求比如/data/project属于dev1用户组属于devgroup但你现在需要临时让运维同事ops01也能进去看看又不想把他加进devgroup组怎么办基础权限做不到。ACLAccess Control List访问控制列表就是为这个场景诞生的。它可以突破三组限制给任意一个用户或组单独设置权限而且这些规则会作为扩展属性附着在文件上。支持ACL的文件系统现在都很常见ext4、xfs、btrfs默认都启用。查看一个文件是否带ACL最直观的是ls -l输出末尾会多一个-rw-r--r-- 1 dev1 devgroup 1024 ... project.txt4.2 setfacl/getfacl 实操ACL的操作命令是setfacl和getfacl。给ops01设置对/data/project读和执行权限setfacl -m u:ops01:rx /data/project然后getfacl /data/project会显示类似# file: data/project # owner: dev1 # group: devgroup user::rwx user:ops01:r-x group::r-x mask::r-x other::---这里的mask是ACL里的一个“权限上限”它限制了所有命名用户和命名组以及默认组能拿到的最大权限。比如mask是r-x即使你给某个用户写了rwx他实际也只能拿到r-x。所以以后看到权限没生效第一反应要看看mask是不是在捣鬼。常用操作setfacl -m u:ops01:rwx file # 修改某个用户的ACL setfacl -x u:ops01 file # 删除某个用户的ACL setfacl -m d:u:ops01:rx dir # 给目录设默认ACL新建文件自动继承 setfacl -R -m g:devops:r-- /data # 递归设置 setfacl -b file # 清空所有ACL递归设置时建议先getfacl看一遍现状再动手。曾见过一条setfacl -R -m u:www:rwx /var/www把大量系统默认ACL全部覆盖导致服务启动慢半拍那叫一个酸爽。4.3 sudo给普通用户一把“可控钥匙”严格来说sudo不是文件系统权限但它是“控制对系统资源访问”的重要一环有些权限问题最终都绕不开它。sudo让你以另一个用户通常是root的身份执行命令但可以精细到“谁能在哪里执行什么命令”。配置在/etc/sudoers强烈建议用visudo来编辑因为它会帮你做语法检查万一手滑写错它能当场拦住不至于让自己彻底失去sudo能力。一个常用配置zhangsan ALL(ALL) ALL这表示zhangsan可以在任何主机上以任何用户身份执行任何命令。但实际环境很少这么给通常按需授权zhangsan ALL(ALL) NOPASSWD: /usr/bin/systemctl restart nginx这条让zhangsan重启Nginx时不需要输入密码但仅限于这一条命令。这样你就把“重启服务”的权限交出去了又没把整台服务器的root全交出去。在排查权限问题时经常有人问我“我明明有sudo为什么chmod还是被拒绝”。这要分清你执行sudo command才会以root身份运行如果你只是普通执行chmod那依然是你自己身份。所以正确姿势是sudo chmod不是chmod后再抱怨。4.4 ACL和基础权限到底听谁的如果你同时用了基础权限和ACL最终生效规则是系统先看基础权限再看ACLACL中mask会限制命名用户和组的有效权限。当ls -l显示时基础权限第2~9位已经不代表最终结果了必须用getfacl看全貌。我遇到过最邪门的问题某文件ls -l明明显示-rwxr-x---但用户就是读不了查了半天原来是ACL里mask::r--把他能拿到的最大权限限成了只读。所以记住看到别急着下结论先getfacl。5. 常见问题与排查实录5.1 为什么路径上某个目录没权限也会卡住忘了说一个很重要的点访问一个文件不止要文件本身的权限还要路径上每一级目录都有足够权限。比如你要读/home/liming/secret/data.txt你需要能依次进入/、/home、/home/liming、/home/liming/secret每一级目录都必须有x执行权限才能放进r权限则是让你能列出名字。如果中间某级目录的权限是---------那后面的文件权限再宽松你也进不去。排查这种问题时一条命令就能看清全路径namei -l /home/liming/secret/data.txt它会从根目录一路列出每一级的权限和属主卡在哪一眼就知道。我在帮别人处理共享目录权限时百分之八十的“奇怪”问题都出在某一级中间目录。5.2 文件可读但目录进不去怎么办典型现象文件权限是644文件本身可读但你cat /home/liming/secret/data.txt就是显示“Permission denied”。为什么因为你的用户对/home/liming/secret这个目录没有执行权限无法穿过它。对目录没有x权限系统根本不会把里面文件的名字“呈现”给你的操作即使你通过绝对路径拿到了文件路径也一样被拦在门外。所以给共享目录授权时约定俗成目录至少给r-x方便别人进入并列出内容如果你想禁止别人查看目录里有什么但允许他打开已知文件可以把目录设为--x只有进入权限无列出权限。这个做法应用在/home/用户这类私密路径上非常有效。5.3 总是“Operation not permitted”的几个隐藏原因权限位看起来全对但操作还是失败这时要往三个方向排查第一文件系统是否只读。执行mount | grep 挂载点看看有没有ro如果只读任何chmod、rm都会报错。第二文件是否被加上了不可变属性。用lsattr file查看如果看到i属性那么即使root也无法随意修改需要先用chattr -i file去掉这个属性。第三SELinux或AppArmor的强制访问控制MAC可能在拦截。平时用getenforce看SELinux状态如果问题只出现在特定进程尝试ausearch -m avc查审计日志或者临时用setenforce 0测试测试完记得改回来。我建议新手不要一上来就禁用SELinux。虽然很多Linux发行版默认关闭或宽松但在企业环境它可能是安全屏障。真被它挡了先去查类型规则或上下文而不是直接setenforce 0否则解决问题后会把更大的问题留给未来。5.4 一套适用的权限重置操作如果你把一个目录搞乱了想快速恢复一套稳妥权限可以照下面这样做# 设置属主/属组 chown -R 目标用户:目标组 /path/to/parent # 目录统一 755 find /path/to/parent -type d -exec chmod 755 {} \; # 文件统一 644 find /path/to/parent -type f -exec chmod 644 {} \; # 清除所有ACL setfacl -R -b /path/to/parent这套组合拳适合大多数业务目录的“重新初始化”。如果你要的是更严格的场景比如上传目录需要可写就把对应目录权限改为750并保证进程用户在该组中。改完之后务必切到实际用户身份su 用户 -c touch /path/to/test试一下不要偷懒只看ls -l。5.5 排查权限问题的标准动作最后分享一个我自己的排查模板遇到权限问题照着走pwd确认当前位置ls -ld查看目录权限和属主。ls -l 目标文件看文件权限和属主。用namei -l 完整路径查整条路径。如果看到用getfacl 目标文件查ACL和mask。lsattr查不可变属性。mount查文件系统读写状态。仍然无解查SELinuxgetenforce、ausearch或AppArmor日志。这套流程基本能覆盖九成权限问题。剩下的一成往往要结合业务逻辑和日志来定位。我在实际运维里最深的体会是权限设计这事不是越严越好而是要在安全性和可用性之间找到平衡。你给某个目录设成000数据是安全了但业务全废了这显然不是目的。比较好的做法是先按角色定好属主、属组再给目录和文件分别设定合适的权限最后用ACL处理临时特例设置完一定要用最小权限的账号去测一遍模拟真实访问场景。最后再送个小技巧每次动手改权限前先把原始权限记下来用ls -l /tmp/perm_before.txt存一份万一改坏了还能立刻恢复。别看这招土关键时刻真能救命。Linux的文件访问控制体系想学通光靠记命令不够多动手制造几个权限问题、再试着复现和修复比背十遍文档都管用。