刚接触 Linux 那阵子我最头疼的既不是 vim 难用也不是 Shell 语法绕人而是文件权限。明明同一个目录别人能进我不能进明明一条命令加上 sudo 就能过不加就甩我一句Permission denied。当时我心里就一个念头这玩意儿简直是玄学。后来玩久了才明白Linux 权限没有想象中那么玄乎翻来覆去就三件事你是谁、文件认谁、你被允许干什么。把这些理清了再看ls -l那一串字符就跟看门牌号一样清楚。这篇我不讲虚的直接从“我是谁”这条线开始把用户身份、权限位、特殊权限、实战场景全捋一遍。不管你是刚开始学 Linux 的新手还是被权限折腾过几次的半桶水看完应该都能少踩几个坑。1. 先解决“我是谁”用户身份从哪来1.1 别把用户名当身份底层看的是 UID很多新手有个误区觉得 Linux 认的是 login 时候输入的那个账号名像 root、ubuntu、mysql 这些。实际上内核根本不认识你叫什么它只认数字编号也就是 UID用户 ID和 GID组 ID。你登录时输入的账号名只是系统帮你翻译了这串数字方便你记忆罢了。你可以直接在终端敲一个命令验证id输出大概长这样uid1000(tom) gid1000(tom) groups1000(tom),4(adm),27(sudo),1001(docker)这里的uid1000(tom)才是系统眼里的你数字 1000 是你的真实身份括号里的 tom 是给人看的别名。groups 那一列表示你同时属于哪些组注意第一个组1000(tom)是你的主组后面几个是附加组。这个差异很关键后面讲权限归属时会用到。那这些 UID 和用户名存在哪答案是/etc/passwd和/etc/group。我见过不少新人一听/etc/passwd第一反应是“密码文件”其实它只存用户基本信息密码早就挪到/etc/shadow里了。你随便打开/etc/passwd每行七个字段用冒号分隔大致是用户名、密码占位符、UID、GID、描述信息、家目录、登录 Shell。比如root:x:0:0:root:/root:/bin/bash tom:x:1000:1000:Tom:/home/tom:/bin/bash这里有个从业者必须知道的常识0 号 UID 就是 root这是系统的最高特权身份。所以判断一个用户有没有超级权限别只看名字是不是 root第一优先看 UID 是不是 0。真有那种把普通用户名改成 root 的骚操作但 UID 是 1000那它照样不是特权用户。1.2 whoami、who am i 和 sudo到底谁在说谎whoami这个命令估计是大家学的第一个身份命令。它的逻辑很简单打印当前有效用户的用户名。但坑就在“有效”两个字上。我举个真实场景。你登录账号 tom执行whoami # 输出 tom然后你执行sudo -i切换到 root shell再跑一下whoami # 输出 root这时候谁是真实的你严格说原始登录用户是 tom当前有效身份是 root。那么whoami显示的其实是有效身份不是原始身份。想查原始登录用户可以敲who am i。如果通过 sudo 临时提权who am i可能会显示原始的那个 tom。这两个命令的区别虽然小但排查登录日志、审计操作归属时非常关键搞混了会把责任归错人。另一个绕不开的就是sudo和su。它们俩都是切换/提升身份的工具但设计哲学完全不同su - root切换时把整个会话环境都换掉需要输入目标用户通常是 root的密码切过去之后你就是完整的 root。sudo command以 root 或指定用户身份执行单条命令只需要当前用户自己的密码并且执行完自动回到原身份。系统通过/etc/sudoers文件决定谁能 sudo、能执行哪些命令。我强烈建议普通运维环境里能用 sudo 就别用su -因为 sudo 有审计日志执行过的每条特权命令都会记进日志用 su 切到 root 之后一台机器上所有人共用同一个 root 权限出了事很难定位是谁干的。最小权限原则的第一步就是别轻易把 root 会话给出去。2. 文件权限的读心术看懂ls -l每个字符2.1 十个字符拆开看类型、三组权限和一个点把身份搞清楚之后下一步就是弄明白文件系统怎么校验你。最常见的就是在目录下执行ls -l你会看到类似这样的输出-rw-r--r-- 1 tom dev 4096 Aug 12 10:33 app.conf drwxr-xr-x 2 tom dev 4096 Aug 12 10:35 scripts -rwsr-xr-x 1 root root 34872 Aug 10 08:12 helper第一列那一串字符看起来像乱码拆开就是信息富矿。以-rw-r--r--为例总共 10 个字符第 1 个字符-表示这是普通文件后面 9 个字符分成三组每组三个分别代表属主owner、属组group、其他人others的权限。先说第 1 个字符它表示文件类型。最常见的几个首字符类型说明-普通文件文本、二进制的常规文件d目录目录本质上也是一种文件l符号链接软链接b块设备磁盘等c字符设备终端、串口等s套接字进程间通信用的p管道命名管道 FIFO很多人不知道目录的权限跟普通文件截然不同后面我会专门讲。现在先把重点放在三组rwx上。顺带提一嘴第一列末尾偶尔会出现一个.比如-rw-r--r--.这代表这个文件有 SELinux 上下文关联或者系统启用了 SELinux 标签。如果出现的是说明这个文件还有额外的 ACL 权限。这俩符号提醒你别把权限只看成 9 个字符系统里还藏着另一套规则。2.2 rwx 针对文件与目录的语义完全不同r、w、x 这三个字母在普通文件上很好理解可读、可写、可执行。但到了目录身上它们的含义会发生一次语义跳转这个跳转是无数新人栽跟头的地方。对普通文件来说r能读取文件内容比如能 cat 查看。w能修改文件内容但能不能删除这个文件取决于所在目录的写权限。x能把它当作程序或脚本来执行。对目录来说r、w、x 分别对应三种能力r能列出目录里有什么名字。注意只是“列出名字”没有 x 权限的话你 ls 能看到文件名但看不到文件的类型、权限等更详细的信息。w能在目录里创建、删除、重命名文件或子目录。这个权限很重握着它等于握着这个目录下所有文件的“生杀大权”。x能进入这个目录可以作为路径的一部分cd进去。没有 x你连进都进不去。所以你会遇到一种诡异情况对某个目录有 r 权限没 x 权限ls可以看到一堆文件名但cd /进入目录报权限不足想查文件具体信息也查不到。反过来有 x 没 r能 cd 进去能直接访问你确切知道名字的文件却不能ls目录看有哪些文件。对于目录x 权限平常被低估实际上它比 r 更基础。在我自己维护服务器时设置日志目录或备份目录宁可给 r-x 也不给 rwx就是为了防止用户列文件加增删。2.3 权限的数字换算rwx 为什么等于 7日常操作里经常看到chmod 755这种写法很多人背下来了但没搞明白 755 怎么来的。其实很简单r 记 4w 记 2x 记 1把三组权限分别加起来得到一个数字。比如rw-r--r--拆开算属主rw-就是 4 2 6属组r--就是 4其他人r--就是 4所以rw-r--r--就是 644。同理rwxr-xr-x是 755rwx------是 700。权限组合数字常见用途rw-------600私密配置文件rw-r--r--644普通文件默认rwx------700私有目录rwxr-xr-x755程序、脚本目录rwxrwxrwx777不推荐谁都能改这里必须强调一点777 是典型的反面教材。很多人都图省事权限不对了chmod -R 777 一把梭。但 777 意味着任何用户都能读写执行等于把门锁翘了。我之前接手一台被挂马的服务器排查半天发现某个上传目录被写了个 777任何网站用户都能传脚本上去攻击者直接拿 WebShell。权限这东西够用就好别贪多。3. 动刀改权限chmod、chown、chgrp 的实操要点3.1 chmod 的符号模式 vs 八进制模式改权限最常用的是 chmod它有两种写法八进制数字模式和符号模式。八进制模式就是前面说的chmod 644 file优点是简洁缺点是不够直观。符号模式我反而建议新手先掌握因为可读性强不容易误操作。符号模式基本语法chmod [who] [操作] [权限] 目标who 可以是 u属主、g属组、o其他人、a所有人操作是加权限、-减权限、设置精确权限。举几个例子chmod ux script.sh # 给属主增加执行权限 chmod g-w file.txt # 移除属组的写权限 chmod or file.txt # 其他人的权限精确设置为只读 chmod arwx demo.sh # 所有人加读写执行权限相当于 777我在实战里偏爱符号模式的一个场景是只改一个权限位比如chmod x给脚本加执行权限但如果脚本本身有特殊权限位SUID用chmod 755会顺手把特殊位也清零符号模式则更安全不动没指定的位。再补充一个从实践中得到的提醒chmod后面可以跟多个目标也可以配合-R递归修改。但-R要慎重我在一个生产环境里见过有人执行chmod -R 755 /home/把用户家目录里所有私钥文件都变成了 755等于把钥匙贴门外了。递归改权限前一定想清楚目录和文件的合理权限往往不是同一套更稳妥的做法是用find分别匹配文件与目录再改。3.2 改属主和属组chown 与 chgrp 的真正含义改权限只是修“门锁”文件的所有权同样重要。Linux 里每个文件都有 owner 和 group用 chown 和 chgrp 修改。它们能改变的是文件头上记录的属主 UID 和属组 GID。基本用法chown tom:dev app.conf # 把属主改成 tom属组改成 dev chown tom app.conf # 只改属主 chown :dev app.conf # 只改属组等同于 chgrp dev app.confchown 常踩的坑是符号链接很多人在软链接上执行chown -R改的却是链接指向的目标软链接本身的权限和属主基本没人管。系统出于安全考虑对符号链接执行 chown 默认跟随目标多数发行版会忽略对链接本身的修改。要只管链接本体得用chown -h。这个细节初看无关痛痒但如果你在维护一个带大量软链接的部署目录就懂有多重要了。根目录权限不是随便能动的。chown普通用户只能改自己文件的属组且必须是指定组内的成员改属主更是只有 root 才能做。权限不足时会直接报错 Operation not permitted。这也侧面体现了 Linux 的“权限不可越级”思想每层都有每层的边界。3.3 umask新建文件为什么默认没权限你会发现新建的文件权限通常是 644新建的目录是 755。这个规则的背后就是 umask。它决定新建文件权限的“默认上限”。Linux 对新建文件的默认基础权限是 666因为不能默认给你可执行权限目录是 777。然后拿这个基础值“去掉”umask 里存在的权限位得到最终默认权限。当前 shell 的 umask 值直接用命令查umask # 比如输出 0022umask 0022 表示要从基础权限里去掉属组的写权限2和其他人的写权限2。于是新建文件就是 666 - 022 644新建目录就是 777 - 022 755。注意这里说的“减去”是指去掉权限位不是简单的算术减法。比如 umask 是 002基础 666 减去 002 得到 664。如果 umask 是 077那么文件是 600目录是 700比较安全。umask 写在配置里会影响这台机器上所有新建文件比如/etc/profile、/etc/bashrc、用户家目录下的.bashrc。很多做运维的会把系统 umask 从 022 改成 027这样同组用户无法读其他用户的新文件其他人更是完全没权限。从安全角度讲我支持这种收紧策略但要注意部署脚本里如果有“产生文件后用其他服务账号读取”的逻辑umask 一改就可能静默导致访问失败。改 umask 前最好全局搜一遍相关的文件发布流程。4. 特殊权限位SUID、SGID、Sticky Bit 的进阶逻辑4.1 SUID为什么普通用户能改自己的密码普通的 rwx 只够解决“日常进门”的问题但 Linux 里有一种需求很特殊一个普通用户需要临时借助文件属主的特权来干某件事。最经典的例子就是passwd。你想想看普通用户执行passwd要修改/etc/shadow而这个文件默认只有 root 能写。按常理普通用户根本没资格改。但系统想让每个用户都能改自己的密码怎么办Linux 选了个巧妙但不乏争议的方案给 passwd 这个程序设置 SUID 位。SUIDSet User ID的作用是当一个可执行文件带有 SUID 位时任何用户运行它进程的有效用户 ID 会被临时设为文件属主的 UID。如果这个文件属主是 root那么普通用户执行时这个进程暂时拥有 root 的权限去修改 shadow 文件执行完再归还。你在ls -l中看到的典型表现是文件属主的 x 位变成了 s-rwsr-xr-x 1 root root 68208 ... /usr/bin/passwd注意那个-rws不是rwx。带 SUID 的 root 文件就是 Linux 里最常见的“特权程序”。那怎么设置 SUIDchmod us /path/to/program # 数字表达则是在三位权限前面加一位 4 chmod 4755 /path/to/programSUID 是把双刃剑威力巨大。如果给一个 shell 脚本或一个 bash 设置了 SUID那普通用户运行它等于拿到了 root 权限。所以生产环境中应当经常排查带 SUID 的文件特别是那些属主是 root 的。我自己常用的排查命令find / -perm -4000 -type f 2/dev/null看到可疑的 SUID 文件但不确定能不能删可以去查这个文件对应软件包是否合法。凡是来源不明的 SUID 文件基本可以判定是入侵后留下的后门。4.2 SGID让协作目录不再文件混乱SGID 跟 SUID 思路类似但它作用于两个不同对象时有不同效果作用于可执行文件时进程的有效组 ID 会临时变成文件的属组作用于目录时效果更常见也更实用所有在该目录下新建的文件或子目录其属组会自动继承目录的属组而不是创建者的主组。这在多人协作项目中非常有用。假设你的团队开了一个共享目录/srv/project属组是 dev。张三、李四都在 dev 组里。如果目录没设 SGID张三新建的文件属组是他自己的主组通常就是张三李四虽然通过 group 权限能访问但文件属组是张三后续 ACL 和组策略管理容易乱如果给目录设了 SGID那么张三新建的每个文件自动属于 dev 组李四凭借组权限就能正常协作。设置方式chmod gs /srv/project # 数字表达是在前面加一位 2 chmod 2775 /srv/projectls -l下能看到目录属组的 x 位变成 s类似drwxrwsr-x 2 root dev 4096 ... /srv/project新手容易看走眼目录类权限位里的s小写表示该位同时有执行权限大写S则表示没有执行权限。如果看到一个目录是drwxrwSr-x说明 SGID 已设置但没有 x 权限这种情况下 SGID 对目录的“继承属组”效果可能不会正常生效。这是很多权限排查里的坑细节决定了有效性。4.3 Sticky Bit/tmp 目录怎样避免文件互删第三种特殊位叫 Sticky Bit中文常叫粘滞位。它在目录上的作用也很有现实意义一个带 Sticky Bit 的公共目录所有人都能创建文件但只有文件属主和 root 能删除或重命名目录里的文件。最典型的例子就是/tmp。它的权限是drwxrwxwt注意最后那个t。如果没有 Sticky Bit/tmp是 777任何用户都能删掉其他用户扔在临时目录里的文件。有了它用户之间互相“拆台”的情况就被禁止了。设置方法chmod t /some/public/dir # 数字表达是在前面加一位 1 chmod 1777 /some/public/dir这个位对共享目录特别有用。我在公司搭公共临时目录时就会用chmod 1777 /data/shared/tmp。所有人可读写但谁也不能删别人的文件极大减少了协作中的误操作。特殊权限位的数字组合用一位前缀表达4 是 SUID2 是 SGID1 是 Sticky Bit。它们可以叠加比如chmod 1777就是 Sticky Bit 777chmod 4755就是 SUID 755。5. 完整实战把权限知识变成可落地能力5.1 场景一搭一个多用户协作的网站发布目录理论知识说再多不如完整过一遍场景。假设我要给开发团队搭一个发布目录/srv/www/orders团队组叫 webteam成员包括若干开发者需求是大家都能读写这个目录目录内新建的文件自动属于 webteam方便互相覆盖修改临时文件和备份目录不能让其他无关用户乱删发布脚本需要修改文件时用 sudo不直接给 root实操步骤# 1. 创建组和目录 sudo groupadd webteam sudo mkdir -p /srv/www/orders # 2. 给目录设属组并设好权限 sudo chown root:webteam /srv/www/orders sudo chmod 2770 /srv/www/orders2770中的 2 是 SGID让新建文件自动继承 webteam770 意味着只有属主 root 和 webteam 组成员能访问其他用户一律拒绝。这比常用的 2775 更严格因为 web 目录一般不需要其他用户读。然后成员加入组sudo usermod -aG webteam zhangsan sudo usermod -aG webteam lisi这里用-aG而不是直接改-G是为了保留用户原有的附加组。否则你可能把用户从 docker 组里踢出来这个错误我在工作中见过不止一次。再看权限位ls -ld /srv/www/orders # drwxrws--- 2 root webteam 4096 ... /srv/www/orders注意组权限位那个 s表示 SGID 已在生效。zhangsan 在这个目录里创建新文件后文件属组会自动变成 webteam其他成员就有权编辑。最后给发布脚本设置单独的 sudo 规则让指定脚本以 webteam 身份运行sudo visudo -f /etc/sudoers.d/webdeploy写入Cmnd_Alias DEPLOY /srv/www/orders/deploy.sh webteam ALL(ALL:webteam) NOPASSWD: DEPLOY这样团队无需知道任何密码就能安全执行发布脚本且权限只限定在特定命令上。这就是最小权限思想在目录管理中的完整落地。5.2 场景二Permission denied 的完整排查思路遇到Permission denied的时候别慌也别上来就 chmod 777。我建议按以下顺序排查第一步确认当前身份和目标关系。id ls -ln /path/to/file用ls -ln而不是ls -l可以看到真实的 UID 和 GID避免被用户名混淆。第二步确认路径上每一层目录都给了足够权限。这是个极其常见的隐患文件本身权限正确但上级目录缺 x导致你根本走不到那个文件跟前。可以用 namei 命令看完整路径权限namei -l /srv/www/orders/app.conf它会从/开始逐层列出每个路径组成部分的权限方便一眼锁定哪层目录掉了链子。第三步判断是文件权限还是 ACL 或 SELinux 拦截。如果权限位看着没问题检查一下 ACLgetfacl /srv/www/orders/app.conf如果 ACL 为空仍不能访问考虑 SELinux 类策略影响临时用ls -Z查看文件的 SELinux 上下文或检查对应的布尔值配置。第四步分清是“没有权限”还是“以错误身份操作”。比如服务以 www-data 用户运行你以 tom 测试时权限不足非常正常。别用 root 测通就认为没问题要先确认服务进程的真实运行身份ps aux | grep nginx第五步才是决定改权限还是改属主。改权限时能用最小改动就不大开大合能只改属主就尽量不动权限位。比如文件是对的但属主错了chown解决如果只缺执行权限chmod x解决。5.3 场景三最小权限原则的日常落地技巧最小权限原则说起来就一句话能给只读就不给写能给单命令就不给 shell能给一组就不给所有。落地到日常运维里有几个具体技巧。第一个是避免用 root 跑日常命令。我见过很多人在服务器上一天到晚 root 挂机遇到问题随手 chmod 777久而久之整个文件系统权限一片混乱。更稳的做法是普通账号操作需要特权时再 sudo 单条命令。第二个是给服务账号分配专用家目录和专用组。比如启动 Web 服务的用户就用 www-data它的家目录权限设为700其他用户无法直接读。部署代码时把 web 目录的属主设为www-data或把开发人员放进一个组并设置目录组权限而不是直接让服务账号的权限和其他账号混在一起。第三个是定时排查异常权限。我自己一般在服务器上设一个简单定时任务每周扫一次全盘 SUID 文件和带 ACL 的文件find / -xdev -perm -4000 -type f 2/dev/null find /data -xdev -type f -exec getfacl -p {} \; 2/dev/null | grep -B1 user:.*:w | head -100不必形成一个很重的流程重点是保持“权限是要管理”的意识。很多安全事故都是从某个不该 777 的文件开始的。6. 权限速查与踩坑记录那些我替你踩扁的坑6.1 速查表一条命令搞定一类需求这里整理一个常见的快速查阅表都是我实际用频率比较高的操作需求命令/写法查看当前身份idwhoami看文件权限细节ls -lls -ln看路径每层权限namei -l /path修改权限chmod 644 filechmod ux file递归改文件/目录权限find dir -type f -exec chmod 644 {} \;改属主与属组chown user:group file设置 SUIDchmod 4755 file或chmod us file设置目录 SGIDchmod 2770 dir或chmod gs dir设置 Sticky Bitchmod 1777 dir或chmod t dir查看默认权限掩码umask查看 ACLgetfacl target设置 ACLsetfacl -m u:tom:rw file6.2 这些年我踩过的权限坑第一个坑用chmod -R处理前端构建产物。某次我把某个 dist 目录chmod -R 777图省事结果测试环境被爬虫写了一堆垃圾文件磁盘差点写满。后来我强制自己区分文件和目录的权限普通静态资源文件用644目录用755实在要递归就这么写find /web/dist -type f -exec chmod 644 {} \; find /web/dist -type d -exec chmod 755 {} \;第二个坑修改属主时不小心动了系统目录。曾有一次我想快速修一个挂载目录的属主命令里少写了路径层级直接指到了上级目录结果一批系统文件属主全变了。那次之后我所有 chown/chmod 都强制要求一眼看准路径并习惯性地在命令前先ls -ld确认目标。第三个坑忽略 umask 对后续创建文件的影响。在某个项目里我把 /etc/profile 的 umask 改成 027结果第二天同事反馈他新拉下来的配置文件模块无法启动。原因是某些服务刚创建出来的文件属组校验不通过连接被拒绝。所以说 umask 这种全局配置改的时候要考虑它对所有新建文件的影响面。第四个坑混淆了符号链接权限。软链接的权限显示是lrwxrwxrwx它的实际访问权限取决于链接指向的目标很多人看这个rwxrwxrwx就以为链接可以被任意改实际上你改的是链接指向的目标文件。一时没想明白就去 chown 软链接结果改了目标文件属于典型的手滑操作。6.3 权限排查的进阶思路用 root 身份做一次“模拟切换”最后分享一个我自己排查权限问题时特别喜欢的思路先切到目标用户身份去复现问题看它到底报什么错。sudo -u www-data ls -l /srv/www/orders/这一句就能模拟 www-data 用户的视角去看目录立刻知道它能不能列出内容、能不能进入。如果复现不出来那就回到当前用户再试借此对比差异。等诊断清楚之后再去决定是改属主、改组、改权限位还是调 ACL。不要在一个 Permission denied 上僵住也别因为“反正能用”就放宽成 777多花两分钟把根因找到后面能省下更多时间。权限这套东西我越玩越觉得它像装修房子类型是户型UID 是房本名字rwx 是每间房的门锁特殊权限位是逃生通道和防拆墙。设计一套合理的权限方案比事后反复修补要舒服得多。希望你读完这篇之后再看到Permission denied想到的不是“好烦”而是“好我来看看这套门禁系统到底哪一层拦住了我”。