ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Linux权限管理全解:chmod、chown、umask、ACL与sudo

Linux权限管理全解:chmod、chown、umask、ACL与sudo 搞 Linux 的迟早会碰上一件事执行一个命令系统冷冰冰甩给你一句Permission denied。我当年第一次碰到这个报错的时候第一反应是怀疑系统坏了折腾半天发现就是目录少了个执行权限。后来带过团队、排过生产故障才意识到 Linux 权限管理不是一条chmod 777能解决的事它决定了你系统的安全边界也决定了你和同事协作时会不会互相踩脚。这篇文章我打算把 Linux 权限管理这套东西从头捋一遍从ls -l的每一列怎么看到chmod、chown、umask的底层逻辑再到特殊权限位、ACL、sudo 这些日常绕不开的工具全部用我实际踩过的坑来讲。适合刚开始做运维、写代码时经常被权限问题卡住的同学也适合那些已经会用命令但说不清原理的“半熟手”。1. 权限模型先读明白ls -l输出的每一列很多教程上来就讲 chmod但我觉得第一步是先把ls -l的输出看懂。这是所有权限操作的“地图”地图都看不懂后面全是瞎撞。1.1 九位权限位到底在说什么随便找一个文件执行ls -l你会看到类似这样的输出-rw-r--r--. 1 root root 12345 Jun 8 10:32 app.log drwxr-xr-x. 2 root root 4096 Jun 8 10:33 conf第一列的第一个字符表示文件类型-是普通文件d是目录l是软链接b是块设备c是字符设备。这个字符经常被人忽略但它决定了后面的权限位对“目录”和“文件”的语义完全不同后面会细说。后面九个字符分成三组每组三位分别对应属主user、属组group、**其他人other**的权限。每组里的三个字符依次是读r、写w、执行x没有权限就显示-。比如-rw-r--r--就是属主可读可写属组可读其他人可读。这看起来很简单但有一个非常关键的细节权限位的顺序是固定的永远是 rwx不是任意排列。所以当你用数字模式时r4、w2、x1这个编码才能成立。真正让新手困惑的是目录的执行权限。文件的x表示“能否运行这个程序”目录的x表示“能否进入这个目录”。注意没有目录的x即使你有r权限也只能列出目录里的文件名但无法cd进去也无法访问里面的任何文件。我见过太多人给目录配了r却忘了x然后一脸懵地来问我为什么还是进不去。1.2 属主、属组和硬链接数的关系ls -l输出的第三列是属主第四列是属组。这俩决定了权限位的前两组作用在谁身上。一个文件的属主通常是创建它的用户属组通常是创建者当前的主组但这两条都有例外。比如你用vim编辑一个 root 拥有的文件如果这个文件允许你写保存时即使你不是属主也可以修改内容因为内核只看你当时的进程是否有对应权限不看你是不是“文件主人”。这里容易踩坑的是修改文件内容和删除文件是两码事。能不能删除文件取决于文件所在目录的写权限而不是文件本身的写权限。这个后面讲目录权限时再展开。第二列的数字是硬链接数对普通文件来说等于指向这个 inode 的硬链接数量对目录来说等于“子目录数 2”。这个数字偶尔能帮你判断目录结构是否异常比如一个目录的硬链接数异常高多半是下面多了很多子目录。1.3 数字权限 755、644 是怎么算出来的每组权限用八进制数字表示r4、w2、x1把三个值相加就是这一组的数字。例如rwxr-xr-x属主是 4217属组是 4015其他是 4015连起来就是 755。这里我建议你记住几个常用组合的含义而不是每次现算644文件默认权限属主可写其他人和属组只读。网页静态文件、配置文件常用。755目录默认权限属主可读可写可执行其他人可读可执行。程序目录、脚本目录常用。600属主可读可写其他人都没权限。密钥文件、敏感配置必须用这个。700属主可读可写可执行其他人无权限。私密目录常用。我见过不少人把数据库密码文件设成 777表面上跑得欢实际上跟裸奔没区别。等下讲 umask 的时候你会明白为什么默认权限一般不会出现 777 这种值。2. chmod 和 chown 的实操要点不光要会用还要知道为什么命令本身很简单但生产环境里因为误用这两个命令搞出故障的例子数不过来。这一节我把常见用法、递归陷阱和权限继承问题一起讲清楚。2.1 符号模式与数字模式的切换数字模式用一条命令一次性设置三段权限适合“我知道最终要设成什么值”的场景chmod 750 /srv/www/project符号模式则适合“我只要调整某一类人的某一个权限”的场景格式是[who] 操作符 [权限]who 可以是u属主、g属组、o其他、a全部操作符是、-、。举例说明chmod ux run.sh # 给属主加执行权限其他人不动 chmod g-w,o-r file.txt # 去掉属组的写权限去掉其他人的读权限 chmod ar readme.md # 所有人都只有读权限我看到不少新人有一个习惯每次改权限都重打一遍完整数字比如想给脚本加个执行权限直接chmod 755结果是脚本的属主、属组原有的特殊权限全部被覆盖掉了。如果这个脚本之前设了 setgid 位你这一下就给冲掉了。所以我个人习惯是小改动用符号模式整体重置用数字模式两者结合才稳。2.2 递归修改的隐藏风险chmod -R可以递归修改目录下所有文件但这里有个大坑文件需要的执行权限和目录需要的执行权限语义完全不同。一个目录里通常既有文件又有子目录文件一般不该有x目录必须有x。如果你执行chmod -R 777 /data那所有普通文件也都被加上了执行权限既不安全也不干净。更推荐的做法是分开处理find /data -type f -exec chmod 644 {} \; find /data -type d -exec chmod 755 {} \;这样做之后文件是 644目录是 755符合常规需求。你也可以用chmod -R urwX,go-wX这样的符号模式注意大写X很特殊它只对目录或者已经有执行权限的文件添加执行位不会把所有普通文件都变成可执行文件这个技巧在批量调整代码目录时非常实用。还有一个更隐蔽的风险chown -R和chmod -R如果作用在挂载点或者软链接上可能会改变你意想不到的对象。比如一个软链接指向/etc/passwd你对软链接执行chown有的版本会直接改到目标文件上。现在主流系统默认不允许chown跟随软链接但chmod的行为各发行版仍有差异所以操作前最好先ls -l看清楚是不是软链接。2.3 chown 和 chgrp谁才有资格改属主chown用来改属主和属组格式是chown 用户:组 文件。一个容易忽略的规则是普通用户可以将文件的组改到自己所属的组但只有 root 能改属主。这是因为如果普通用户能把文件“送人”那系统的权限隔离就形同虚设了。关于组名我提一个细节chown root:root和chown root.root在老版本 Linux 里都支持点号分隔但现在很多发行版只推荐冒号。更关键的是如果只想改组而不想改属主直接chgrp更清晰chgrp devteam /srv/app/config.yaml这个命令个人研发环境里用得少但团队协作时非常常见。比如后端服务用www用户跑代码更新需要部署用户deploy写入你可以把项目目录的属组设为www然后让deploy也属于这个组最后用 setgid 保证新建文件自动继承目录的属组这套组合拳在真实项目中比粗暴地chmod 777靠谱得多。3. 特殊权限位和隐藏属性普通权限管不到的地方这块内容是很多教程的盲区但生产环境里偏偏靠它解决大问题。我分四个点讲setuid、setgid、sticky bit 和 chattr 不可变属性。3.1 setuid为什么普通用户也能改自己的密码/usr/bin/passwd这个命令仔细看它的权限-rwsr-xr-x. 1 root root 33544 Jun 7 2023 /usr/bin/passwd注意属主权限位不是rwx而是rws那个s就是 setuid 位。它的作用是当用户执行这个程序时进程的有效用户 ID 变成文件属主root而不是当前登录用户。所以普通用户才能写/etc/shadow去修改自己的密码。setuid 的设置在数字模式里是4开头比如chmod 4755符号模式是us。这东西极其危险一个带着 setuid 的脚本如果写得不好等于给任意用户开了一个 root 后门。我见过有人为了省事给编辑器设置 setuid结果任何用户都能以 root 权限去编辑系统文件这比直接给 sudo 还可怕。因此排查系统安全问题时我习惯用这条命令找全局 setuid 文件find / -perm -4000 -type f 2/dev/null正常的发行版会有 passwd、sudo、mount 等少量命令如果多出一些不认识的/usr/bin/xxx基本可以断定有人做了什么奇怪的操作。3.2 setgid让协作目录里的新文件自动继承属组setgid 位有两个作用一是在文件上执行时进程中有效组 ID 切换为文件属组二是在目录上在该目录下新建的文件和子目录会自动继承这个目录的属组。后面这个作用对团队协作太重要了。举个例子开发组所有成员都属于devteam共享目录/srv/app/code属组是devteam然后执行chmod 2770 /srv/app/code设置好 setgid 之后任何用户在这个目录里新建的文件属组都会自动是devteam而不是用户自己的主组。这样团队成员之间就能互相读写彼此创建的文件不需要每次新建完都手动chgrp。数字模式下 setgid 是2开头符号模式是gs。这里有个配合使用的细节如果目录权限是2770组内用户新建的文件默认权限还受 umask 影响。如果 umask 是 022那新建出来的文件是 644组内其他人只能读不能写。如果你们协作需要互相改文件就得把 umask 改成 002 或者 007让组权限有写位。umask 的具体原理下一章详细说。3.3 sticky bit/tmp 目录为什么人人都能写但删不掉别人的文件sticky bit 一般只用在目录上最典型的例子就是/tmp。它的作用是目录里创建的文件只有文件属主、目录属主或 root 才能删除即使其他人对这个目录有写权限也不行。设置方式很简单chmod t /data/shared chmod 1777 /data/shared数字模式下1开头代表 sticky bit。/tmp的标准权限就是1777你要是把/tmp设成了 777就意味着任何用户都能删除别人的临时文件这在多用户服务器上会引发各种莫名其妙的问题比如另一个用户的程序突然报错说临时文件不存在。3.4 chattr连 root 都删不动的“免死金牌”前面讲的所有权限在 root 面前都是浮云但chattr不一样。它对文件设置的是文件系统级别的属性例如chattr i config.yaml # 设置不可变属性 chattr a app.log # 只允许追加内容设置i之后即使是 root 也无法修改、删除、重命名这个文件直到你执行chattr -i解除。这个特性在加固系统时非常好用比如把关键的/etc/passwd、/etc/shadow、/etc/sudoers全部加i属性可以防住很多误操作和入侵后的篡改。但请注意一个坑chattr不是所有文件系统都支持ext4、xfs 没问题但某些网络文件系统或者部分 FUSE 挂载的目录可能会静默失败或者报错。另外如果你开的是某些容器环境文件系统能力受限chattr也可能不生效。设置之前先确认文件系统类型别到时候发现白忙一场。4. umask 的实战经验为什么文件一创建出来权限就不对很多权限问题不是出在 chmod而是出在“创建时就没给对”。umask 就是控制新建文件默认权限的机制搞懂它你才能根治那些“每次都要手动 chmod”的毛病。4.1 umask 的计算逻辑不是简单减法先看系统当前的 umaskumask # 输出类似 0022umask 的作用是从“满权限”里抠掉一部分作为默认权限。对文件来说满权限是666因为文件创建时就不该默认带执行权限对目录来说满权限是777因为目录没有执行权限就进不去。所以当 umask 是022时新建文件的权限是666 - 022 644新建目录的权限是777 - 022 755。这就是为什么你touch一个新文件天然就是 644。注意这里我说的是“逻辑上的扣除”不是二进制位上的严格相减但在绝大多数日常场景里用减法理解就够了。真正严谨的规则是默认权限 满权限 与 umask 取反值 做按位与。比如 umask 是 002取反后是 775666 775 664所以新文件属组就多了写权限。这个细节在组协作环境里会直接影响你能不能改别人的文件。4.2 按场景调整 umask全局、用户级、命令级修改 umask 的三个层级我分别说。全局级改/etc/profile或/etc/bashrc影响所有登录用户。适合服务器管理员统一设置安全基线比如把默认 umask 从 022 改成 027这样新建文件默认是 640目录是 750其他人一律不可读。适合隐私要求高的生产环境但不适合需要共享的目录。用户级改~/.bashrc或~/.profile只影响当前用户。适合个人开发机。命令级在脚本或命令前临时指定例如umask 077 mkdir -p private_data这个技巧在脚本里特别管用。如果脚本里要解压一个压缩包默认的 umask 可能让解压出来的文件权限太宽松你可以在解压前临时把 umask 收紧解压完再恢复。我个人踩过的坑是写日志归档脚本时没注意 umask结果生成的备份文件是 644其他人也能读里面还有数据库的导出数据幸好发现得早。从那以后凡是脚本里涉及敏感文件我第一行就写umask 077。5. ACL 和 sudo权限管理的进阶工具传统权限模型只有 ugo 三组一个文件要同时给 A 用户读、B 用户写、C 用户执行纯靠 chmod 根本做不到。ACL 就是来解决这种细粒度授权的。5.1 setfacl 和 getfacl给单个用户单独授权setfacl可以在传统权限之外给指定用户或组附加权限。比如/srv/app/config.yaml当前属主是 root属组是 root你想让alice这个用户也有读权限执行setfacl -m u:alice:r /srv/app/config.yaml getfacl /srv/app/config.yamlACL 的规则是“谁匹配谁生效”用户alice会被授予读权限其他人不受影响。这在团队协作里太常用了比把用户塞进某个组更灵活也更符合最小权限原则。目录还可以设置默认 ACL让新建文件自动带上 ACLsetfacl -m d:u:alice:rwx /srv/app/shared这样以后任何人在这个目录里新建文件alice都自动有读写执行权限不需要再单独授权。但要注意ACL 会改变ls -l的输出带 ACL 的文件权限位后面会多一个号例如-rw-r-----。排查权限问题时看到号就要知道这个文件存在 ACL 规则用getfacl查看明细。5.2 sudo 配置给用户分配“最小可执行权力”sudo 的本质不是“把所有权力交给用户”而是只允许用户以特定身份执行特定命令。编辑/etc/sudoers必须用visudo命令它会检查语法防止你写错导致所有人都没法提权。常用的授权写法alice ALL(ALL) /usr/bin/systemctl restart nginx bob ALL(ALL) /bin/systemctl这份配置的意思是alice 只能以任意用户身份执行systemctl restart nginx这一条命令bob 只能执行systemctl这个程序。注意这里的匹配是前缀匹配如果你授权了/bin/systemctl那用户其实可以执行/bin/systemctl下的所有子命令需要结合实际情况收紧。我见过一个经典事故给运维人员授权了ALL(ALL) ALL又把密码设为空。结果某天有人登录后直接执行passwd root改掉了 root 密码。所以 sudo 授权要遵循两条原则一是命令尽量精确到具体子命令二是必须要求密码不要用NOPASSWD图省事。5.3 典型场景新建用户并分配目录权限的完整命令很多运维新手会碰到这个需求新同事入职要给他创建账号还要让他能操作项目目录/srv/www/project。一套完整操作如下useradd -m -s /bin/bash alice passwd alice groupadd webteam usermod -aG webteam alice chown -R root:webteam /srv/www/project chmod -R grwXs /srv/www/project find /srv/www/project -type f -exec chmod gr {} \;解释一下这几条命令的逻辑。-aG是把用户追加到 webteam 组不加-a会把用户移出原有附加组。chmod grwXs中大写X只给目录加执行权限不给普通文件加执行权限s是设置 setgid 位保证以后新文件自动属于 webteam。最后一条find不是必需的但能确保历史文件至少对组内可读。这样操作完alice 可以进入/srv/www/project正常读写文件但无法修改项目外的系统目录。相比直接给 sudo 或者改 777这个方案既安全又方便后续维护。6. 常见问题与排查技巧实录权限问题出得最多的地方其实不是“不知道命令”而是“不知道往哪个方向排查”。我整理了几类高频故障和对应的排查思路。6.1 Permission denied 的标准排查顺序拿到Permission denied我建议按顺序查四层第一层确认当前用户是谁whoami、id。很多人用 sudo 安装软件或拉取代码然后忘了后续访问该目录时也要带 sudo导致权限不一致。第二层查目录链上每一级的权限Linux 要访问一个路径必须对该路径下的每一层目录都有执行权限。比如访问/srv/www/project/app/srv、/srv/www、/srv/www/project、/srv/www/project/app都得有x。偶尔有人把/srv的权限从 755 改成了 755 中间某级变成r--就会导致所有用户大面积访问失败。第三层看文件自身的属主属组和 ACLls -l和getfacl。第四层看进程上下文比如进程是不是跑在特定用户下是否被 systemd 的User或ProtectSystem限制住了。这里我提一句ProtectSystemtrue会让服务无法写入/usr、/boot、/etc很多 systemd 服务的诡异报错都跟这个有关。只要按这个顺序走一遍90% 的权限问题都能定位。6.2 报错 Operation not permitted 但权限看起来是对的如果你确认权限没问题执行命令却提示Operation not permitted八成是文件系统的隐藏属性或 SELinux 在拦截。先查chattr属性lsattr config.yaml如果输出带i执行chattr -i config.yaml解除。如果不是 chattr 的问题再看 SELinux 上下文ls -ZSELinux 的报错通常会在/var/log/audit/audit.log里留下记录生产环境排查时可以直接用ausearch -m avc -ts recent查看。如果确认是这个原因最简单的临时验证办法是执行setenforce 0但只建议用来确认问题真正的长期方案是把文件上下文改对比如chcon -t httpd_sys_content_t /var/www/html或者安装类型匹配的 policy 包而不是直接关掉 SELinux。我需要强调一下生产环境不建议长期setenforce 0这等于把系统的安全防线拆了一半。6.3 文件权限被递归改乱之后的修复思路如果你不小心把整个目录甚至整个系统chmod -R了一通别慌先评估范围。如果只是某个数据目录按“目录 755、普通文件 644、可执行文件 755”的规则修复find /data -type d -exec chmod 755 {} \; find /data -type f -exec chmod 644 {} \; find /data -type f -name *.sh -exec chmod 755 {} \;如果连系统目录都被改了比如/bin、/lib、/etc都被 HHH 误操作波及那就比较麻烦了。不要想着一个命令全部还原因为找不到原始权限表。这时候最快的办法是从备份恢复或者用发行版自带的包管理工具重新安装受影响包来重置权限。比如 rpm 系可以在 bash 脚本里循环比对/var/lib/rpm/中的文件属性和权限重新安装软件包Debian 系可以用apt-get install --reinstall。这种事故通常要花大量时间处理所以我的核心建议是批量递归操作前先备份一份权限清单getfacl -R /srv/www /backup/www.acl # 恢复时执行 setfacl --restore/backup/www.acl这比裸奔式的chmod -R稳得多。6.4 目录权限正常但用户无法写入的冷门原因有时候ls -ld看目录属主是你的权限也是 755 或 775但你就是创建不了文件。这里有几个冷门原因第一目录的上级目录没有写权限。你要创建文件需要目标目录和所有上级目录都允许你经过但只有最底层目标目录需要写权限。如果上级目录缺少x你进都进不去。第二磁盘空间满了创建文件会报错但报错信息可能是Permission denied而不是No space left on device这在某些文件系统上出现过。第三ACL 掩码mask限制了最大权限。即使你给某个用户设了rwx如果 ACL 的 mask 是r-x实际生效权限会被允许到 r-x写权限不生效。用getfacl查看时会看到mask::r-x需要执行setfacl -m m::rwx调整。第四配额限制。用户或组的磁盘配额用尽写操作也会报权限类错误。排查这类问题不要只盯着目标目录多看看上下文环境。6.5 关于权限管理的一些实操习惯最后分享几个我自己的习惯纯个人经验不一定适合所有团队但至少能帮你少踩坑。第一个习惯是操作前先确认当前身份。我有一台测试服长期开着 root 登录某次本想给普通用户配置目录结果手滑在 root 会话里执行了chown -R整个/home把所有用户的 home 目录全变成了 root 所有。这个错误让我意识到任何时候执行批量权限命令都要先whoami和pwd双确认。第二个习惯是用最小权限起步逐步放宽不要一上来就给 777。某些程序确实会要求可写目录但绝大多数情况下通过调整属组和 ACL 就能解决。即使最终要放宽权限也要明确到底放给谁、放开到什么程度、什么时候回收。第三个习惯是定期审计特殊权限位和 SUID 文件。多用户服务器上安全风险通常不是来自系统漏洞而是来自权限配置不当。我用一个简单的 cron 脚本每天把全系统 SUID 文件清单发到日志目录配合find / -perm -4000和find / -perm -2000做变更检测。这些操作不复杂但能帮你及时发现那些不该出现的提权入口。权限管理这个东西刚学的时候觉得就是几条命令的事真正深入下去才发现它串起了用户管理、文件系统、进程安全、系统审计一整条链路。你不需要第一天就记住所有特殊位的数字编码但最好养成“动手前先看权限、排查时先看链路”的习惯。每换一台服务器、每搭一个新的服务目录都花十秒钟把属主、属组、ACL、隐藏属性过一遍长期下来能帮你避免很多半夜被叫起来处理故障的情况。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进