说起Linux权限管理,我真的是一把辛酸泪啊。记得刚入行那会儿,因为权限设置不当,把生产环境搞崩过好几次,那种半夜被电话轰炸的感觉,现在想起来还心有余悸。
今天就跟大家聊聊Linux权限这个看似简单,实则坑多如牛毛的话题。别看网上教程一大堆,真正用起来的时候,各种奇葩问题层出不穷。
Linux的权限系统说白了就是三个角色:文件所有者(owner)、组(group)、其他人(others)。每个角色都有读(r)、写(w)、执行(x)三种权限。
用ls -l命令看文件权限的时候,那一串字符其实很好理解:
-rwxr-xr--
第一个字符表示文件类型,后面九个字符分成三组,分别对应owner、group、others的权限。
不过这里有个坑,我当年就栽过。看到权限是644的文件,以为其他用户只能读,结果发现某些用户居然能执行。后来才知道,如果用户属于文件的组,那就按组权限来,而不是others权限。这个逻辑听起来很简单,但实际操作中经常搞混。
修改权限最常用的就是chmod命令了。数字模式和符号模式我都用过,各有各的好处。
数字模式比较直接:
chmod 755 /path/to/file
chmod 644 /path/to/file
4代表读权限,2代表写权限,1代表执行权限。要什么权限就把对应数字加起来。755就是owner有读写执行权限(4+2+1=7),group和others有读和执行权限(4+1=5)。
符号模式更灵活一些:
chmod u+x file.sh # 给owner添加执行权限
chmod g-w file.txt # 移除group的写权限
chmod o=r file.log # 设置others只有读权限
chmod a+r file.conf # 给所有人添加读权限
我个人更喜欢用符号模式,因为可以精确控制要修改哪个权限,不会影响其他权限。数字模式虽然简洁,但容易一不小心把其他权限也改了。
有一次我要给一个脚本添加执行权限,本来想用chmod +x,结果手快打成了chmod 777,直接把文件权限全开了。还好是测试环境,要是生产环境那就完蛋了。
处理目录权限的时候,经常需要用到-R参数递归修改。但这个功能用起来要特别小心,一个不注意就可能把整个系统搞崩。
chmod -R 755 /var/www/html
这个命令看起来没问题,但如果目录下有很多文件,而你又不想让所有文件都有执行权限怎么办?
我的做法是分开处理:
find /var/www/html -type d -exec chmod 755 {} \; # 只给目录设置权限
find /var/www/html -type f -exec chmod 644 {} \; # 只给文件设置权限
这样目录有执行权限可以进入,文件没有执行权限更安全。
修改文件所有者用chown,修改组用chgrp。不过chown其实可以同时修改所有者和组:
chown user:group file.txt
chown user file.txt
chown :group file.txt
第一个命令同时修改所有者和组,第二个只修改所有者,第三个只修改组。
这里有个经验之谈,在生产环境中修改重要文件的归属之前,一定要先备份。我见过太多因为chown操作导致服务无法启动的案例了。
特别是系统文件,千万不要随便改归属。有一次同事想解决权限问题,直接把/etc目录的所有文件都chown给了普通用户,结果系统直接起不来了,最后只能重装系统。
传统的Linux权限系统有个限制,就是只能设置owner、group、others三种角色的权限。但实际工作中,经常需要更细粒度的权限控制,这时候就需要用到ACL(Access Control Lists)了。
ACL可以给特定的用户或组设置权限,不受传统权限模型的限制。
查看文件的ACL权限:
getfacl filename
设置ACL权限:
setfacl -m u:username:rwx filename # 给特定用户设置权限
setfacl -m g:groupname:r-- filename # 给特定组设置权限
setfacl -m o::--- filename # 设置其他用户权限
删除ACL权限:
setfacl -x u:username filename # 删除特定用户的ACL
setfacl -b filename # 删除所有ACL
ACL还支持默认权限,对目录设置默认ACL后,在该目录下创建的新文件会自动继承这些权限:
setfacl -d -m u:username:rwx /path/to/directory
不过ACL也有坑,最大的问题是兼容性。不是所有的文件系统都支持ACL,而且在复制文件的时候,ACL权限可能会丢失。用cp命令的时候要加上-p参数才能保留ACL权限。
我在一个项目中用ACL给不同的开发团队设置了不同的目录权限,结果有一天发现权限全乱了。后来才发现是因为有人用rsync同步文件的时候没有加上保留ACL的参数,把所有的ACL权限都搞丢了。
除了基本的rwx权限,Linux还有三个特殊权限位:SUID、SGID、Sticky Bit。这些权限位功能强大,但也容易被忽视。
SUID(Set User ID)权限让普通用户在执行文件时临时获得文件所有者的权限。最典型的例子就是passwd命令,普通用户可以修改自己的密码,但密码文件/etc/shadow只有root能写。
ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68208 Jul 14 2021 /usr/bin/passwd
注意那个s,就是SUID权限的标志。
设置SUID权限:
chmod u+s filename
chmod 4755 filename # 数字模式,4表示SUID
SGID(Set Group ID)类似,但是获得的是文件所属组的权限。对目录设置SGID后,在该目录下创建的文件会自动属于目录的组,而不是创建者的主组。
chmod g+s dirname
chmod 2755 dirname # 数字模式,2表示SGID
Sticky Bit主要用于目录,设置后只有文件所有者才能删除自己的文件。/tmp目录就设置了这个权限:
ls -ld /tmp
drwxrwxrwt 12 root root 4096 Dec 7 10:30 /tmp
那个t就是Sticky Bit的标志。
chmod +t dirname
chmod 1755 dirname # 数字模式,1表示Sticky Bit
这些特殊权限位在安全方面很重要,但也要小心使用。SUID权限如果设置不当,可能会成为提权攻击的入口。
很多人不知道umask的存在,但它却默默影响着每个新创建文件的权限。
umask值决定了新文件的默认权限。系统默认的umask通常是022,意思是新文件的group和others权限要去掉写权限。
查看当前umask:
umask
修改umask:
umask 027 # 临时修改
要永久修改umask,需要在shell配置文件中设置,比如~/.bashrc或/etc/profile。
文件默认权限的计算有点绕,新文件的最大权限是666(rw-rw-rw-),新目录的最大权限是777(rwxrwxrwx),然后减去umask值。
比如umask是022,那么新文件权限就是666-022=644,新目录权限就是777-022=755。
不过这里有个坑,umask的计算不是简单的数学减法,而是按位取反后做AND运算。我当初也被这个搞糊涂过,以为umask 123就是直接用666-123,结果发现不对。
实际工作中,不同的用户可能需要不同的umask设置。比如web服务器的用户,可能需要umask 002,这样同组用户可以修改文件。而普通用户可能用umask 077更安全,这样只有自己能访问新创建的文件。
说了这么多理论,来分享几个实际踩过的坑。
有一次部署一个web应用,发现上传的图片显示不出来。检查了半天发现是权限问题,web服务器进程没有读取图片文件的权限。本来以为chmod 644就行了,结果发现还是不行。
后来才发现,不光文件要有读权限,文件所在的目录也要有执行权限,web服务器才能访问到文件。这个细节很容易被忽略,目录的执行权限实际上是"进入"权限,没有这个权限就无法访问目录下的文件。
还有一次,我用脚本批量处理文件,结果发现有些文件处理不了。用ls -l一看,文件权限都是正常的,但就是读不了。后来用lsattr命令一看,发现文件被设置了不可修改属性(immutable)。
lsattr filename
----i--------e-- filename
那个i就是immutable属性,即使是root也不能修改这种文件,必须先用chattr命令去掉这个属性:
chattr -i filename
这种扩展属性很容易被忽略,但有时候会成为问题的根源。
回到ACL,虽然前面提到了基本用法,但实际应用中还有很多细节。
比如在一个多用户的开发环境中,我需要让不同的开发者对同一个项目目录有不同的权限。传统权限模型做不到这么细致的控制,但ACL可以。
# 给项目目录设置基本权限
chmod 750 /var/www/project
# 给不同开发者设置不同权限
setfacl -m u:dev1:rwx /var/www/project # 开发者1有完全权限
setfacl -m u:dev2:r-x /var/www/project # 开发者2只能读和执行
setfacl -m u:tester:r-- /var/www/project # 测试人员只能读
# 设置默认ACL,新文件自动继承权限
setfacl -d -m u:dev1:rwx /var/www/project
setfacl -d -m u:dev2:r-x /var/www/project
这样设置后,不同角色的人员就有了合适的权限,既保证了安全性,又满足了工作需要。
不过ACL有个让人头疼的地方,就是权限计算变得复杂了。当文件同时有传统权限和ACL权限时,系统会取两者的交集。这个逻辑有时候会让人摸不着头脑。
权限问题排查有一套固定的套路,我总结了几个步骤:
这个顺序基本能覆盖大部分权限问题。
有个小技巧,用namei命令可以显示路径上每个目录的权限,对排查路径权限问题很有用:
namei -l /var/www/html/index.php
这个命令会显示从根目录到目标文件路径上每个组件的详细权限信息。
权限管理不只是功能问题,更是安全问题。有几个安全原则一定要记住:
最小权限原则是第一位的。能用644就不用755,能用755就不用777。我见过太多为了图方便直接chmod 777的情况,这简直是在给黑客开门。
定期检查SUID和SGID文件也很重要:
find / -perm -4000 -type f 2>/dev/null # 查找SUID文件
find / -perm -2000 -type f 2>/dev/null # 查找SGID文件
这些文件如果被恶意利用,可能成为提权攻击的跳板。
还有就是要注意文件的隐藏属性。有些恶意软件会给自己的文件设置隐藏属性,让管理员不容易发现:
lsattr -a /path/to/directory # -a参数显示隐藏文件的属性
在大规模环境中,手动管理权限是不现实的,需要用到自动化工具。
写脚本批量处理权限时,一定要先测试,确认无误再执行。我习惯用find命令配合-exec参数:
# 先用echo测试命令是否正确
find /path -name "*.log" -exec echo chmod 644 {} \;
# 确认无误后再执行
find /path -name "*.log" -exec chmod 644 {} \;
还可以用xargs命令提高效率:
find /path -name "*.sh" -print0 | xargs -0 chmod +x
-print0和-0参数可以正确处理文件名中包含空格的情况。
权限变更的监控也很重要,特别是在生产环境中。可以用auditd来监控文件权限变更:
# 添加监控规则
auditctl -w /etc/passwd -p wa -k passwd_changes
auditctl -w /etc/shadow -p wa -k shadow_changes
这样任何对这些重要文件的修改都会被记录到审计日志中。
用inotify工具也可以实时监控文件变更:
inotifywait -m -r -e attrib /important/directory
Linux权限在不同发行版之间基本是一致的,但在跨平台环境中会有问题。比如在Windows和Linux之间传输文件,权限信息可能会丢失。
用samba共享文件的时候,权限映射也是个头疼的问题。Windows的权限模型和Linux完全不同,两者之间的转换经常出问题。
还有就是容器环境,Docker容器内外的用户ID映射也会导致权限问题。这个坑我也踩过,容器内的文件在宿主机上看权限完全不对。
Linux权限管理看起来简单,实际上细节很多,坑也很多。我这些年踩过的坑数不胜数,每次都是血的教训。
几个关键点再强调一下:
权限管理是Linux系统管理的基础,也是安全的第一道防线。掌握好权限管理,不仅能避免很多故障,还能大大提高系统的安全性。
但同时也要敬畏权限,每一次chmod、每一次chown,都要三思而后行。一个不小心就可能酿成大祸,我见过太多因为权限设置不当导致的生产事故了。
记住,在Linux的世界里,权限就是一切。权限管理做好了,系统就稳定了一半。
如果这篇文章对你有帮助,别忘了点赞转发,让更多的运维朋友看到。权限管理的坑太多了,大家一起交流经验,互相提醒,才能少踩坑。
关注@运维躬行录,我会持续分享更多实战经验和踩坑总结,让我们一起在运维的路上少走弯路!