1. 用户与组的关系梳理
1.1 为什么Linux要区分用户
学习Linux绕不开用户和权限,这是整个系统安全模型的根基。我见过不少初学者一上来就习惯用root操作一切,等到出了问题才意识到,用户与权限的理解不到位,后面排查问题时连方向都找不到。
Linux是一个多用户操作系统,它的设计初衷就是让多个用户共享同一台机器上的资源。既然要共享,就得有隔离和保护。隔离靠什么实现?就是用户身份和权限机制。每个用户有独立的账户信息,每个文件有独立的属主和权限位,系统根据这些信息决定谁能读、谁能写、谁能执行。这套机制看起来很朴素,但它是整个系统稳定性和安全性的基础。
在Linux中,用户分为三类。第一类是超级用户root,它有最高的权限,可以读写任何文件、管理系统服务、修改系统配置。第二类是系统用户,它们通常不提供给人工登录使用,而是给服务后台进程用的,比如数据库服务会以专门的系统用户运行,这样即使服务被攻破,攻击者也只能在那个用户的权限范围内活动。第三类是普通用户,是管理员手动创建供人使用的账户,日常工作和学习都建议用普通用户进行。
理解这个分类的重要性在于:root权限是把双刃剑,它强大但也危险,一个误操作可能会让整个系统崩溃。我见过有人在root下执行了一个错误的删除命令,导致系统文件被清空,最终只能重装系统。在学习和生产环境中,普通操作都应使用普通用户身份,只有必须做系统级变更时才切换到root或使用sudo提权。
1.2 用户和组的对应关系
用户组是一个逻辑概念,它的作用是把多个用户聚合在一起进行统一权限管理。举个生活化的例子:一家公司有多个部门,市场部、研发部、行政部,每个部门有自己的办公区域和资料室。如果公司要给整个研发部开通某份资料的访问权限,一个一个去配置研发部的每个成员会很麻烦。更合理的做法是建立“研发部”这个组,把研发部的所有员工加进这个组,然后对资料所在的目录设置“允许研发部组的成员访问”,这样只需要设置一次。
Linux的组也完全一样。假设一个项目目录需要让多个开发者共同维护,但又不希望其他无关用户进来读文件,这时可以创建一个开发组,把开发者们都加入进去,然后把目录的属组设置为这个开发组并赋予组读写权限。这样省去了逐个用户授权的麻烦,权限管理也更清晰。
用户和组的关系是多对多的,一个用户可以加入多个组,一个组也可以包含多个用户。在主组之外加入的组称为附加组,这个概念在配置权限时经常遇到。比如用户zhangsan的主组是zhangsan,但管理员把它加入到了webdev这个附加组,那么它就能访问属组为webdev的文件,同时他默认创建的文件的属组仍然是zhangsan。
1.3 用户信息存储的位置
用户的账户信息保存在两个核心文件中。第一个是/etc/passwd,它每行对应一个用户,包含用户名、密码占位符、UID、GID、用户描述信息、家目录和登录Shell。第二个是/etc/shadow,它存放加密后的密码和相关策略信息,这个文件默认只有root能读,普通用户无法访问。
很多初学者会有个疑问:既然/etc/passwd里有密码占位符,为什么还需要单独的shadow文件?原因是/etc/passwd是全局可读的,任何用户都能查看。如果加密后的密码散列值直接放在这里,恶意用户就可以把文件复制走,用暴力破解工具离线破解密码。把密码散列挪到只有root可读的/etc/shadow中,普通用户连看都看不到,破解难度就大大提高了。
/etc/passwd中还有一个容易忽略的字段是登录Shell。比如/sbin/nologin表示这个用户不能登录系统,通常用于服务账户,这是个很好的安全措施。创建系统用户时默认就是这个Shell,创建普通用户时默认是/bin/bash。如果某个账户无需登录,设置nologin是防止被利用的好习惯。
2. 用户管理的核心命令
2.1 创建用户时系统做了什么
创建用户最常用的命令是useradd,用法很简单,但很多人不清楚它背后做了一系列操作。执行useradd zhangsan后,系统会依次完成这些事情:在/etc/passwd中添加一条记录,在/etc/shadow中添加密码记录,创建家目录/home/zhangsan,并拷贝/etc/skel目录下的隐藏配置文件作为初始配置,创建与用户名同名的主组,最后还会在/var/spool/mail/目录下创建用户邮件文件。
/etc/skel目录值得多说一句,它相当于一个模板目录。系统创建用户时,会把这里的所有文件复制到用户家目录下,常见的有.bashrc、.bash_profile等。管理员可以把需要给新用户统一下发的初始配置放在这个目录里,这样创建出来的新用户会自动拥有这些配置。比如说团队规定了统一的命令提示符格式或默认编辑器,改这个目录就可以了,不用等用户创建后再逐个配置。
创建用户时几个常用参数需要掌握:-u指定UID,-g指定主组,-G指定附加组,-s指定登录Shell,-d指定家目录路径,-m强制创建家目录,-M不创建家目录。举例来说,要创建一个归属于webdev组、附加组为deploy、使用bash登录、家目录在/opt/www的部署用户,用到的命令是:
useradd -g webdev -G deploy -s /bin/bash -d /opt/www -m deploy_usr这里有一点很容易踩坑:如果指定家目录为/opt/www,而且这个目录之前已经存在,useradd不会自动修改它的属主,创建出来的用户可能会发现家目录的属主不是自己,无法正常读写。解决办法是创建后手动执行chown。简单来说,useradd只是创建账户记录,不负责处理已有目录的属主关系。
2.2 修改和删除用户
usermod用于修改用户属性,它的参数和useradd基本对应。把用户zhangsan的登录Shell改为nologin,可以用:
usermod -s /sbin/nologin zhangsan把用户加入附加组使用-aG参数,注意这里的-a很重要,表示追加而不是覆盖。如果不加-a直接使用-G,会把这个用户从其它附加组中移除,只保留指定的这一个组。我在实践中就见过有人想把新用户加到一个组,结果因为漏了-a,用户原本在另一个组里的权限被静默清掉了,排查了很久才找到原因。
usermod还有一个好用的参数-L和-U,用于锁定和解锁用户账户。锁定账户在员工离职、账号暂时冻结的场景中经常使用,它会让用户无法登录,但账户信息不会删除,方便之后重新启用。
删除用户使用userdel,默认情况下userdel不会删除家目录和邮件目录,如果需要连家目录一起删除,要加-r参数。这一步一定要确认好,因为家目录中可能保存了用户的个人文件,删除后就无法找回。建议在执行删除前先备份重要目录,或者先使用usermod -L锁定账户观察一段时间,确认没有影响再执行删除。
2.3 密码设置的注意事项
创建用户后如果没有设置密码,该用户是无法登录的。设置密码使用passwd命令,root可以修改任何用户的密码,普通用户只能修改自己的密码:
passwd zhangsan还有一个批量场景:如果有多台服务器需要统一设置初始密码,可以用chpasswd批量导入,避免逐台逐用户手工设置。比如准备一个username:password格式的文件,执行chpasswd < 文件路径,就把文件里的用户密码全部更新掉了。在批量初始化环境的场景中,配合脚本使用效率很高。
设置密码时有个细节值得注意,passwd命令交互式输入密码时不会显示任何字符,这是正常的,不是终端卡住了。很多新手第一次输密码看到屏幕上没有任何反应,以为自己输入没生效,结果多输了几次导致密码设置混乱。另外在生产环境中建议使用强密码,包含大小写字母、数字和特殊字符,长度不低于12位。
关于密码过期策略,可以查看/etc/shadow中的第三到第五个字段,它们分别表示距离上次修改的时间、最短修改天数、最长使用天数。用chage命令可以调整这些策略,比如强制用户首次登录后修改密码:
chage -d 0 zhangsan这样设置后,用户第一次登录系统时会收到提示要求立即修改密码,适合在批量创建初始账户后统一执行。
3. 文件权限的底层逻辑
3.1 文件权限位的完整解读
文件权限是Linux中最常见的权限模型,它的表示方式通常是一串十个字符,比如-rwxr-xr--。第一个字符表示文件类型,横杠是普通文件,d是目录,l是符号链接,c是字符设备,b是块设备。后面九个字符分为三组,每组三位,分别对应属主权限、属组权限、其他用户权限。
三组中的每一位依次是读(r,值为4)、写(w,值为2)、执行(x,值为1),没有对应权限时显示为横杠。这组数字设计得很巧妙,它用二进制和八进制的关系把权限表示成单个数字。把三位的值相加,就可以得到7、6、5这类权限数值。比如rwx = 4+2+1 = 7,r-x = 4+1 = 5。因此,755就代表属主拥有读写执行权限,属组和其他用户只拥有读和执行权限。
用ls -l命令查看文件时,第一列显示的就是权限位和文件类型。在很多教程中提到“755权限”,这个数字组合在网站文件目录配置中非常常见,属主可以修改,其他用户只能读取和执行,是一个兼顾使用和安全性的折中方案。理解这个数字映射后,看到chmod 644、chmod 750这些命令,就不会觉得是生硬记忆了。
目录的权限解释和文件稍有不同。目录的读权限表示可以列出该目录下的文件名列表,写权限表示可以在目录内创建、删除、重命名文件,执行权限表示可以进入目录并访问其内部的文件或子目录。如果要进入一个目录查看里面的文件,至少需要该目录的执行权限。如果只有读权限而没有执行权限,即使能列出文件名,也无法访问具体文件内容,这一点是做Web目录权限、共享目录配置时特别容易搞混的地方。
3.2 数字法和符号法修改权限
chmod命令支持两种修改权限的方式。第一种是数字法,直接指定最终权限值:
chmod 755 /path/to/file chmod 644 /path/to/file数字法简单直接,适合明确知道目标权限的场景。它的问题是需要写出完整的权限值,如果只想给属组增加一个写权限而不动其它权限,数字法就有些笨拙了。这时应该使用符号法,它的格式是“谁+操作+权限”,“谁”用u表示属主、g表示属组、o表示其他用户、a表示所有用户,“操作”用+表示增加、-表示移除、=表示设置。
给属组增加写权限:
chmod g+w /path/to/file移除其他用户的读取权限:
chmod o-r /path/to/file同时设置属主读写执行、属组读执行、其他用户无权限:
chmod u=rwx,g=rx,o= /path/to/file符号法的优势在于增量修改时不容易误操作其它权限位,在多步配置权限时很有用。我个人的习惯是:设定完整权限用数字法,调整单项权限用符号法,各有各的适用场景。
3.3 修改文件的属主和属组
chown命令用于修改文件的属主和属组。修改属主:
chown zhangsan /path/to/file同时修改属主和属组,属主和属组之间用冒号分隔:
chown zhangsan:webdev /path/to/file只修改属组:
chgrp webdev /path/to/file如果目录下有大量文件需要递归修改,加上-R参数。这里有一个经验之谈:在生产环境中递归修改属主属组前,建议先检查目录内是否存在符号链接。chown默认不追踪符号链接,加-R后行为会有所不同,不同发行版的实现还不一致。稳妥的做法是先执行find命令找出目录中所有链接文件,再决定是否统一处理。
chown之后,原本设置的acl、setuid等特殊权限可能会被清掉,这是个隐蔽的坑。如果需要保留这些权限,修改完属主属组后要重新设置特殊权限,或者在使用chown前先把特殊权限记录下来。
4. 三类特殊权限的实战理解
4.1 SUID权限
SUID(Set User ID)权限作用于可执行文件上,它的效果是:当用户执行该程序时,运行权限会临时提升为文件属主的权限。最典型的例子是passwd命令,需要以root身份修改密码相关文件,普通用户执行passwd时正因带有SUID权限,才能修改自己的密码。
查看带有SUID权限的文件时,属主权限位的执行标识会显示为s,例如:
ls -l /usr/bin/passwd -rwsr-xr-x 1 root root ...设置SUID权限有两种方式:
chmod u+s /path/to/file chmod 4755 /path/to/file数字法设置SUID时在权限数字前加4。SUID带来的安全风险很大,如果管理员给一个普通可执行文件设置了SUID,且属主是root,那么任何用户运行该程序时都会以root身份执行。一旦程序本身有漏洞,就可能被利用来提权。网上不少系统加固建议要求排查系统内所有SUID文件,这个思路是合理的,尤其要留意那些不应设置为SUID却以root身份运行的异常文件。
4.2 SGID权限
SGID作用于可执行文件和目录两种情况。作用于可执行文件时,效果类似SUID,只不过临时继承的是文件属组的权限。真正的实用场景在于目录:对一个目录设置SGID后,在该目录下新建的文件会自动继承目录的属组,而不是创建者的默认属组。
这个特性在团队共享协作目录中特别有用。之前说过,用户创建文件的默认属组是这个用户的主组。如果没有SGID设置,两个不同部门的用户在一个共享目录里创建文件,后续文件会分属不同的组,权限管理会很混乱。设置了SGID后,新文件统一归属目录的属组,团队内配置好组权限就能互相访问了。
设置SGID的命令:
chmod g+s /path/to/dir chmod 2770 /path/to/dir数字法中,2所在的位置就是SGID位。查看时,属组权限位的执行标识显示为s。
4.3 Sticky Bit粘滞位
粘滞位最常见的应用是/tmp目录。它作用于目录上,效果是:在该目录下,只有文件的所有者、目录的所有者和root用户才能删除文件,其他用户即使对目录有写权限,也无法删除不该删的文件,普通文件和目录通常没有这个限制。
/tmp目录对所有用户开放写权限,如果没有粘滞位,任何一个用户都可以删除其他用户留下的临时文件,这显然是不可接受的。设置粘滞位后,大家都只能在临时目录里管理自己的文件,既保持了共享空间的存在,又避免互相干扰。
查看粘滞位时,其他用户权限位的执行标识显示为t:
drwxrwxrwt 1 root root ...设置粘滞位:
chmod +t /path/to/dir chmod 1777 /path/to/dir数字法中的1就是粘滞位。同时设置SUID、SGID和粘滞位的常见组合参考:4755表示SUID + rwxr-xr-x,2770表示SGID + rwxrwx---,1777表示粘滞位 + rwxrwxrwx。
5. sudo提权的配置与避坑
5.1 sudo的基本工作流程
sudo允许普通用户以其他身份执行命令,通常是以root身份。它解决了一个实际问题:你不想把root密码告诉普通用户,但希望他能执行部分管理命令。
使用sudo时,普通用户输入自己的密码即可,系统验证通过后以root身份执行命令。这个设计比共享root密码安全得多,因为root密码没有泄露出去,而且所有sudo执行的操作都会记录到日志中,审计时可追溯。
查看普通用户能执行哪些sudo命令,可以用sudo -l。要执行管理命令,只需在命令前加sudo:
sudo systemctl restart nginx sudo tail -f /var/log/nginx/error.log5.2 sudoers文件的配置方法
sudo权限的配置文件在/etc/sudoers,直接编辑这个文件的正确方式是用visudo命令。它会在保存时检查语法错误,避免配置错误导致sudo完全不可用。直接使用vim编辑,万一语法出错,sudo会被破坏,修复起来非常麻烦。
sudoers的规则格式是:用户 主机列表=(可切换用户) 命令列表。三行最常见的配置分别是:允许用户zhangsan以root身份执行所有命令、允许组webdev的所有成员执行所有命令、允许用户dev_usr执行一组运维命令:
zhangsan ALL=(ALL) ALL %webdev ALL=(ALL) ALL dev_usr ALL=(ALL) /bin/systemctl, /usr/bin/dnf配置中还有一个重要的特殊性:规则是按从上到下的顺序匹配的,后写的规则会覆盖先写的规则。比如先配置了zhangsan可以执行所有命令,后面又配置了zhangsan不能在某个路径下使用命令,后一条规则会生效。
给普通用户授予sudo权限时,建议遵循最少权限原则:只需让他能执行哪些命令,就精确列出哪些命令,不要图省事写成ALL。尤其不要随意给普通用户配置类似ALL=(ALL) NOPASSWD:ALL这样的规则,这意味着他可以免密执行任何命令,和直接给他root权限没有区别。
5.3 sudo踩坑案例分析
第一个常见的坑是环境变量丢失。sudo在默认配置下会把环境变量过滤掉,只保留不必要的安全环境变量。这导致用户用了sudo后找不到原本在用户环境中的PATH路径。解决办法是显式指定或用代理命令而不是直接切换。
第二个常见坑是sudo su和sudo -i的混淆。sudo su直接切换到root用户,相当于获得了完整的root shell。sudo -i则模拟root登录环境。在实际操作中,如果需要连续执行多条管理命令,临时切到root shell会节省很多时间;但如果只执行单条命令,还是直接使用sudo命令,涉及身份切换的操作在安全审计上并不十分透明。
第三个坑是NOPASSWD配置不当。NOPASSWD表示执行sudo命令时无需输入密码,适用于自动化脚本场景。如果给某个命令配置了NOPASSWD,但命令本身又可以被用于执行其他操作,比如配置了NOPASSWD可以执行vim,实际上vim中又能通过:!command继续执行系统命令,这等于变相给了完全的免密sudo权限。配置sudo规则时对这类可交互、可执行子进程的命令要格外谨慎。
6. 权限排查的经验总结
6.1 权限相关的典型报错分析
遇到“Permission denied”提示时,按照下面的顺序排查:文件属主是否是当前用户、文件属组是否包含当前用户、文件权限位是否允许对应身份读写执行、目录的权限是否允许用户穿越。很多时候只检查了文件本身的权限,却忽略了从根目录到目标文件之间的每个目录都要有执行权限。比如/home/用户目录如果权限设置为700,其他用户自然无法进入这个目录下的任何文件,即使文件本身对所有人开放了读取。
看到“Operation not permitted”提示时,情况不同,这通常不是权限位的问题,而是文件有不可修改的属性。用lsattr命令查看文件属性,如果看到i属性,表示文件被锁定不可修改。这种情况用chmod是没有用的,需要先去除属性:
chattr -i /path/to/file另外,日志中如果出现“sudo: unable to resolve host”,这通常是主机名解析问题,不影响sudo使用但会影响日志记录。排查时在/etc/hosts中加入本机主机名和IP映射即可,不用过度紧张。
6.2 隐藏的目录权限陷阱
在目录权限排查中有一个经典的坑:目录的写权限决定了能否在目录内创建和删除文件,与目录内文件的权限无关。很多人以为删不掉某个文件是因为文件自身的权限不足,实际上删除操作要求的是所在目录的权限。只要目录允许写和执行,即使文件本身是只读的,也能删除它。
我在实际配置共享目录时遇到过类似的问题。团队共享目录设置成775权限,其它用户虽然能进入目录,却无法删除自己的文件。排查到最后,发现是因为目录没有粘滞位且目录的其它用户权限中没有写权限。这个案例说明,共享目录配置权限时要综合考虑属组关系、SGID、粘滞位,不是简单给一个775就能解决的。
还有一个隐蔽问题是umask。umask决定新建文件和目录的默认权限,系统常用值是022,它对应新建文件的默认权限是644,新建目录是755。如果某个业务需要生产目录下的新文件自动具备组写权限,要调整用户的umask值,在/etc/profile或用户的.bashrc中设置。umask和权限的关系简单记:文件默认权限是666和umask做差,目录默认权限是777和umask做差。
6.3 实用的权限审计方法
在实际运维中,我认为最值得养成的习惯是定期审计权限配置。几个实用做法:用find找出所有SUID和SGID文件,重点确认每个文件是否都有必要保留;用audit2why分析审计日志中的权限拒绝事件;用stat命令查看文件的完整属性信息,包括时间戳和权限位。定期执行这些检查,可以在问题变成故障前就把风险控制住。
对于新手,这里可以先用一个组合命令快速查找系统中的异常权限文件:
find / -perm -4000 -type f 2>/dev/null find / -perm -2000 -type f 2>/dev/null前一条查找所有带有SUID权限的普通文件,后一条查找所有带有SGID权限的普通文件。对输出的每个文件,确认它是否是系统自带的必要文件。正常情况下,找到一个不认识的SUID文件就需要引起警惕了。
结合这套Linux用户与权限的知识,再回看“黑马Linux”那套教程的内容,不难发现整个章节的编排逻辑相当清晰:先建立“用户-组-文件权限”的整体框架,再逐个介绍用户管理和权限管理命令,最后通过sudo机制实现安全的权限授予。学习这一部分时,建议每学完一个命令就立刻在终端中实操一遍,用ls -l观察文件权限的变化,用id观察用户和组的关系。亲自创建几个用户,设置几种不同的权限组合,再模拟一下权限不足时的报错,比单纯看教程的记忆效果要好得多。
根据我个人带新人的经验,最容易掌握的练习路径是:先创建两个普通用户,创建一个公共目录,把目录的属组改为一个新建的组,把两个用户都加进这个组,再设置目录权限和SGID,让两个用户在目录中互相读写文件。整个过程覆盖了用户管理、用户组管理、chown、chmod、特殊权限设置,一步步做下来,这一章的知识点就串起来了。等遇到实际问题时,再回过头查细节,印象会很深。