首页 / 资讯中心 / 文章详情

Linux用户与文件权限全解析:从Permission denied到chmod实战

Linux用户与文件权限全解析:从Permission denied到chmod实战 ★ FEATURED ARTICLE
1. 从一次深夜报错说起用户与权限为什么是绕不过去的坎刚接手 Linux 服务器的人十个里有八个第一次被卡住的地方不是命令记不住而是屏幕上那句冷冰冰的Permission denied。我当时的第一反应是这文件明明在我面前怎么就动不了然后习惯性地开个 root 一把梭问题当场消失隐患也就此埋下。后来项目做大多人共用一台机器有人误删了别人的数据有人部署的服务因为属主错乱起不来才回过头认真补上Linux 用户、用户组和文件权限这一课。这篇内容我想把这条线一次讲透Linux 里用户是怎么被定义的、权限位那九个字符到底在说什么、chmod和chown的数字是怎么算出来的以及最关键的——遇到Permission denied时应该按什么顺序去查。它适合刚接触 Linux 的同学也适合已经能敲命令但总在权限上翻车的开发者。我不会只给你一堆命令清单而是把每个参数背后的理由讲清楚让你下次见到报错能自己定位而不是靠搜索引擎撞运气。2. 用户、用户组与权限的底层模型2.1 Linux 为什么一定要做成多用户系统很多人把 Linux 当成单机系统来用装了桌面环境开机就进自己的账号感觉和 Windows 差不多。但 Linux 从设计的第一天起就是为多人共用一台机器准备的上世纪的大型机时代几十个终端接到同一台主机上凭什么保证 A 的操作不会把 B 的数据搞坏答案就是用户身份 权限校验这套机制。具体来说内核在做任何文件操作前都会先问自己三个问题当前发起操作的进程是谁uid 是多少、它属于哪些组gid 和附加组、目标文件允许谁做什么权限位。三个问题答完才决定放行还是返回EACCES也就是我们看到的Permission denied。这就是为什么同一个文件你用 A 账号能改用 B 账号就改不动——不是文件有问题是身份不匹配。理解了这一层你就不会再把权限当成一个配置项而会把它当成系统安全的第一道闸门。这也是为什么生产环境严禁所有人共用 root以及为什么容器、CI 流水线都要专门建一个非特权用户跑服务。2.2 三个关键文件passwd、shadow、group用户信息不是存在内存里的而是落在三个纯文本文件里它们的分工很明确文件存什么关键字段权限/etc/passwd用户基本档案用户名:x:UID:GID:描述:家目录:登录Shell644所有人可读/etc/shadow密码哈希与过期策略用户名:哈希:最后一次改密天数:最小间隔:最大有效:警告天数640 或 000仅 root 可读/etc/group组与组成员组名:x:GID:成员列表644/etc/passwd里第二字段那个x是个历史遗留的占位符真正的密码哈希早就搬到/etc/shadow里去了。原因很直白早期密码哈希就放在 passwd 里而这个文件必须全局可读很多程序要靠它做 uid 到用户名的翻译等于把哈希暴露给所有人做离线爆破。分家之后普通程序读 passwd 拿身份映射只有 root 能读 shadow 校验密码安全性立刻提升一个档次。这里有个新手常踩的点UID 为 0 的账号就是 root不管它叫什么名字。有些加固规范会建议把真正的 root 改名再建一个假的 root 账号UID 非 0当诱饵原理就在这——内核只认数字不认字符串。2.3 用户组的两副面孔主组和附加组每个用户有且仅有一个主组primary group记录在/etc/passwd的第四字段同时可以有零到多个附加组supplementary groups记录在/etc/group的成员列表里。判断权限时内核会把主组和所有附加组一起拿来比对。这个设计解决的是一个人要同时参与多个项目的场景。比如你既在dev组又在ops组那两个组共享的目录你都能进不需要来回切换账号。反过来如果你只给某人加错了组他就会莫名其妙地被某个目录拒之门外——这是排查Permission denied时第一个要确认的地方。id命令可以一次性把这几层关系摊开给你看$ id alice uid1000(alice) gid1000(alice) groups1000(alice),27(sudo),1001(dev)再配合groups、whoami基本就能确认当前这个进程到底以什么身份在跑。注意附加组的变更是要重新登录或者新开会话才生效的因为进程启动时组列表就已经固定下来了。我见过太多人usermod -aG加完组在当前终端里怎么试都是Permission denied新开一个 ssh 会话就好了——不是命令没生效是当前 shell 还没更新组信息。3. 把那串-rwxr-xr-x彻底读懂3.1 九个字符的三段结构与八进制换算ls -l输出里最长的那一串其实是十位第一位是文件类型后面九位才是权限。$ ls -l deploy.sh -rwxr-xr-x 1 alice dev 2183 Aug 12 09:14 deploy.sh第一位-普通文件d目录l符号链接c字符设备b块设备ssocketp命名管道。后面九位分成三组分别对应属主user、属组group、其他人other每组三位依次是r读4、w写2、x执行1。没有权限的位置用-占位。于是rwxr-xr-x换算成八进制就是属主4217属组4015其他人4015合起来755。常用组合其实就那么几个八进制符号形式典型用途644rw-r--r--普通文本、配置、网页文件600rw-------私钥、~/.ssh/id_rsa、含密码的配置755rwxr-xr-x目录、可执行脚本700rwx------个人私有目录、.ssh目录本身750rwxr-x---组内共享、对外封闭的目录775rwxrwxr-x团队协作目录同组可写777rwxrwxrwx除非做实验否则不要出现换算的逻辑值得记牢而不是背表把读、写、执行分别当成 4、2、1 三个砝码需要哪个就加哪个。这样你看到任意一个三位数都能反推出权限反之亦然。3.2 chmod 的两种写法什么时候用哪种chmod支持符号模式和数字模式两种写法。符号模式的结构是[对象][操作符][权限]对象取u/g/o/a操作符取/-/chmod ux deploy.sh # 给属主加执行权 chmod go-w notes.txt # 去掉属组和其他人的写权 chmod grx shared.log # 属组精确设为读执行 chmod ar public.html # 所有人可读数字模式则是直接覆盖全部九位chmod 644 app.conf chmod 755 /opt/scripts chmod 600 ~/.ssh/id_rsa我的使用习惯是知道目标状态就用数字只做增量微调就用符号。比如给一个脚本加执行权chmod x最直观但给一堆配置文件统一收紧chmod 644更不容易漏。要注意chmod 644 dir这种写法作用在目录上是灾难性的——目录没了x位就进不去等于把自己锁在门外这个坑后面还会细说。3.3 chown 与 chgrp改属主才是治本的手段很多Permission denied的根因不是权限位不够而是文件的属主压根就不是你。这时候chmod加权限只是治标正确做法是改属主。chown alice app.log # 只改属主 chown alice:dev app.log # 属主和属组一起改 chown :dev app.log # 只改属组 chown -R www-data:www-data /var/www/html # 递归改整个目录树-R这个参数要格外小心。它会把子目录、子文件全部改成同一个属主和属组包括那些本来不该动的比如用户上传目录里的文件、.git目录。另外递归改属组时如果希望新文件能继承目录的组更稳妥的方案是给目录加SGID 位见下一节而不是反复手动chown -R。顺带说一个容易混淆的点chown -R user:group dir之后再往里新建文件属组默认是创建者的主组不是目录的属组。想让新文件自动归到目录的组就必须给目录设 SGID。3.4 特殊权限位suid、sgid、sticky除了九位常规权限还有三个特殊位藏在同一个字段里分别占八进制的高位 4、2、1。SUID4作用在可执行文件上任何用户执行它时进程的有效 UID 会临时变成文件属主。最典型的就是passwd命令——它是 root 所有但因为设了 SUID普通用户也能通过它去改自己的密码、间接写/etc/shadow。$ ls -l /usr/bin/passwd -rwsr-xr-x 1 root root 68208 ... /usr/bin/passwd注意属主那组的x变成了s。SUID 是提权漏洞的高发区自己写脚本时不要随便加尤其是 shell 脚本——绝大多数内核会直接忽略脚本上的 SUID 位这本身就是一道安全防护。SGID2作用在文件上时进程有效 GID 变为文件属组用法和 SUID 类似但更少见作用在目录上时它会让该目录下新建的文件和子目录自动继承目录的属组。这个特性在团队协作目录里非常实用chmod 2775 /srv/project # 目录设 SGID设完之后无论谁往里建文件属组都固定是project组同组的人都能读写省掉了无数次chown :project。Sticky1只对目录有意义效果是目录里的文件只有属主本人或 root才能删除或重命名。/tmp就是标配$ ls -ld /tmp drwxrwxrwt 12 root root 4096 ... /tmp其他人那组的x变成了t。没有它任何人只要对/tmp有写权限就能删掉别人正在用的临时文件——这就是一个共享目录从能用到敢用的分界线。4. Permission denied 的分类排查手册4.1 先分清是文件权限还是目录权限报错信息通常长这样$ cat /var/log/app/error.log cat: /var/log/app/error.log: Permission denied新手会直接chmod 644 /var/log/app/error.log改完发现还是不行。原因是访问一个文件要经过它的每一级父目录任何一级不让你通过后面的权限再宽松也没用。这时候namei -l是最省事的工具它会把整条路径上每一层的权限列出来$ namei -l /var/log/app/error.log f: /var/log/app/error.log drwxr-xr-x root root / drwxr-xr-x root root var drwxr-xr-x root root log drwx------ root root app -- 问题在这里 -rw-r--r-- root root error.log一眼就能看出是/var/log/app这个目录把其他人挡在外面了。没有namei的环境用ls -ld一层层往上敲也能达到同样效果只是慢一点。4.2 目录的 r、w、x 到底各自管什么目录权限和文件权限的语义完全不同这个差异是绝大多数误判的来源r读允许列出目录内容也就是ls能看到文件名。w写允许在目录内创建、删除、重命名文件。x执行允许进入目录也就是能访问目录内的文件、能cd进去。关键在于删除一个文件靠的是目录的 w 权限而不是文件本身的 w 权限。这就是为什么你在共享目录里可以删掉别人的文件如果目录给了你写权限却改不动它的内容。理解了这点/tmp要加 sticky 位、共享上传目录要小心设计就都顺理成章了。还有一个反直觉的组合只有 x 没有 r 的目录。你可以直接cd进去、可以访问已知文件名的文件但ls会报Permission denied。这在某些只允许按固定路径取文件的场景里是有意为之的设计。4.3 权限位之外的那些坑权限位对上了还是报Permission denied那就要往更外层找。以下是几个我实际踩过的方向SELinux / AppArmor。CentOS、RHEL 系默认开 SELinux文件权限完全正确安全模块照样拦你。判断方式是看/var/log/audit/audit.log有没有 AVC 记录或者临时用getenforce看状态。修复手段通常是restorecon -Rv /path恢复正确的上下文而不是直接把 SELinux 关掉。挂载选项。findmnt -T /srv/data能看到挂载参数如果带ro就是只读挂载怎么chmod都没用noexec会让脚本即使有x位也执行不了nosuid会让 SUID 位失效。文件系统属性。lsattr能看到i不可变和a只追加这类标志被chattr i锁过的文件root 都改不动必须先chattr -i。POSIX ACL。getfacl能看到比ls -l更细的授权规则有些目录的权限是通过 ACL 而不是传统三位组给的只看ls -l会漏判。对应的命令是setfacl -m u:alice:rwx dir。磁盘满或配额超限。写入时磁盘空间耗尽也可能以权限类错误的形式表现出来配合df -h和df -iinode 用尽一起看。远程登录的Permission denied, please try again。这个和文件权限完全是两码事它是 SSH 认证失败——密码错了、PermitRootLogin被禁、AllowUsers白名单没包含你或者密钥没被authorized_keys接受。排查方向应该是/etc/ssh/sshd_config和认证日志而不是去改文件权限。4.4 一张报错速查表把常见现象和对应动作整理成表下次直接对号入座现象最可能的原因首选动作新建文件同级目录下报无法创建目录缺 w 或 xls -ld看目录权限cd进不去目录目录缺 xchmod x或chmod 755ls列不出目录内容目录缺 rchmod r能读不能写文件文件缺 w或属主不是你先确认id再决定chown还是chmod删不掉别人的文件目录有 w 但被 sticky 保护用属主账号删或由管理员处理脚本有 x 位仍执行失败挂载了noexec或 shebang 路径错findmnt -T检查挂载参数改完权限依然拒绝SELinux 拦截看 audit 日志restorecon服务启动报无法写日志服务账号对日志目录无权限建专用用户并chown日志目录SSH 登录报Permission denied认证失败非文件权限查 sshd 配置和认证日志5. 从零搭一套可用的用户与权限方案5.1 建用户几行命令背后的取舍sudo useradd -m -s /bin/bash -c Deploy account deploy sudo passwd deploy sudo usermod -aG www-data deploy-m会创建家目录并拷贝/etc/skel的骨架文件.bashrc、.profile等不写它的话家目录不会自动建登录后一堆工具会报找不到配置文件。-s指定登录 shell如果是纯服务账号可以设成/usr/sbin/nologin从源头堵死登录可能。-c是备注信息写清楚用途半年后回来看还能认出来。usermod -aG里的-a是 append漏掉它会覆盖掉用户原有的附加组把一个还在sudo组里的人踢出去这种事故我亲眼见过。改完组记得让用户重新登录或者用newgrp临时切一下。想确认结果用id deploy和groups deploy各看一眼比翻三个文件快得多。删除用户时userdel默认保留家目录要连家目录一起清掉得加-r但生产环境我建议先保留几天再手工清理免得误删数据。5.2 权威原则最小权限与专用账号有一条原则值得刻在脑子里每个长期运行的服务都用独立的、权限刚好够用的非特权账号跑。数据库一个账号Web 服务一个账号部署脚本一个账号彼此不共享。这样做的好处是万一某个服务被攻破攻击者拿到的是一个几乎什么都干不了的 shell而不是整台机器。落到具体操作上一个 Web 服务的典型权限布局是这样的sudo useradd -r -s /usr/sbin/nologin -d /var/www/app www-app sudo chown -R www-app:www-app /var/www/app sudo find /var/www/app -type d -exec chmod 750 {} \; sudo find /var/www/app -type f -exec chmod 640 {} \;注意这里对目录和文件用了不同的权限目录 750文件 640。原因是目录需要 x 才能进入而文件不需要脚本类的可执行文件单独处理。一刀切chmod 755 -R会让所有配置文件都变成其他人可读把数据库密码暴露给任何能登录机器的账号。5.3 umask新文件的默认权限从哪来每次新建文件权限都不是凭空来的而是基准权限减去 umask。文件基准是 666目录基准是 777。默认 umask 022 时新文件是 644新目录是 755。日常最需要改 umask 的场景是团队协作目录希望新文件同组可写就得把 umask 调成 002。umask 002 # 当前会话生效 echo umask 002 ~/.bashrc # 持久化在/etc/profile或/etc/login.defs里统一配置可以让所有用户生效。另外目录上加 SGID 位配合 umask 002是团队目录最稳的组合拳组继承有 SGID 保证组内可写有 umask 保证两个都做对了基本不用再事后补救。5.4 sudo把 root 权限关在笼子里生产环境直接用 root 登录是禁忌正确姿势是给普通账号配 sudo并且只授权必要的命令。sudo visudo -f /etc/sudoers.d/deploy在里面写deploy ALL(root) NOPASSWD: /usr/bin/systemctl restart app %ops ALL(ALL) ALL第一行只允许 deploy 用户免密重启指定服务其他什么都干不了第二行让 ops 组拥有完整 sudo行首的%表示这是组名。用visudo而不是直接编辑文件是因为它会做语法校验写错了会拦下来——直接编辑/etc/sudoers写崩了你就失去了唯一的提权入口。排查 sudo 相关问题时sudo -l能列出当前用户被授权执行哪些命令比翻配置文件快得多也是我明明配了为什么还是被拒这类问题的第一站。6. 踩过的坑与长期维护经验6.1 chmod -R 777看起来最省事代价最大chmod -R 777是新手遇到权限问题的万能解药也是我见过最贵的一行命令。它把所有文件的权限位全部打开任何能登录机器的账号都能改你的代码、替换你的配置文件、往脚本里塞一行反向 shell。更糟的是它破坏了权限能反映意图这个信息——三个月后你再看到这个目录完全不知道哪些文件本该是可执行的哪些本该是只读的。如果已经这么干过补救办法不是拍脑袋改回去而是从另一个正常环境或备份里拷贝一份ls -lR的输出做对比逐条恢复。系统目录的默认权限一般能通过重装对应的软件包来找回包管理器会带上正确的权限信息。6.2 umask 与 SGID 的配合能省掉大量重复劳动前面提过这里再强调一次因为它是真正一劳永逸的做法。团队目录的正确开局是三步sudo chown -R :dev /srv/project sudo chmod 2775 /srv/project echo umask 002 | sudo tee -a /etc/profile.d/dev-umask.sh这样一来只管把文件往目录里丢属组和权限自动就是对的不需要每次部署后跑一条chown -R。反过来如果先建了一堆文件再补 SGID那些老文件是不会自动改属组的还得手工修一遍。6.3 权限修复的常规流程线上遇到权限问题我一般按这个顺序走避免越修越乱第一步id确认当前身份和组列表同时确认会话是不是最新的必要时重开终端。 第二步namei -l或逐级ls -ld看完整路径上每一层的权限和属主。 第三步stat看文件的精确权限、属主、属组和三个时间戳。 第四步如果权限看着没问题getfacl看 ACLlsattr看扩展属性findmnt -T看挂载选项getenforce看安全模块。 第五步定位清楚后再动手改完立刻复测并把改动记录到变更单里。这个流程的意义在于先诊断后治疗。直接动手改权限往往会掩盖真实原因过两天换个场景又犯。6.4 面试里关于用户和权限的高频考法这类题目在基础面试里出现频率很高我整理了几个常见问法和自己习惯的回答角度问题回答的要点chmod 755和chmod urwx,gorx等价吗等价但后者更明确表达意图SUID 有什么用、有什么风险临时提升有效 UID是提权漏洞高发点脚本上通常无效为什么/tmp是 1777sticky 位防止用户互删文件用户加了组为什么不生效进程组列表在会话启动时固定需重新登录怎么让新文件自动继承目录属组目录设 SGID2775删文件看文件权限还是目录权限看目录的 w 权限怎么查一个路径上哪一级卡住了namei -l回答时如果能顺带说清楚为什么而不是只报命令通常能加分不少。7. 我个人的几条长期习惯说几条自己这些年养成的小习惯都不是什么高深技巧但确实省了很多事。先看再改。任何时候动手改权限之前先把ls -l和id的输出贴出来看一眼。养成这个反射之后绝大部分问题在敲下chmod之前就已经定位清楚了。给关键目录写注释。在/etc/sudoers.d/的自定义文件和/srv目录下放一个README写清楚谁应该是什么权限、为什么这么设。权限设计一旦隔了半年连写它的人都会忘。密钥文件永远 600.ssh目录永远 700。SSH 对这两个权限非常挑剔权限太松会直接拒绝使用密钥而这个报错往往含糊其辞容易让人往错误方向排查。生产环境的权限变更走记录。用getfacl -R /path acl.bak在变更前存一份快照出问题能一键setfacl --restore回滚。这个操作成本极低但真的救过我一次。尽量不用 root 做日常操作。需要提权时用 sudo让每一条高权限命令都留下日志。这既是安全要求也是出事之后唯一能还原现场的依据。权限这件事本质上是在回答谁、能在什么范围内、做什么。把这个问题想清楚了Permission denied就不再是一个让人烦躁的报错而是一条准确的提示——它在告诉你你的身份和这个操作之间还差一个明确的授权关系找到它、补上它问题就结束了。
阅读完成 · 觉得有帮助?
咨询建站