1. 账号管理的核心逻辑与实操
1.1 用户、组与UID/GID的基本概念
Linux是一个多用户多任务的操作系统,这就决定了它必须有一套完整的机制来区分不同的人和不同的权限边界。当年刚接触Linux时,我犯过一个典型的错误:拿到服务器第一件事直接用root登录,然后所有操作都在root下进行,结果某次误删了一个目录,连带把另一个项目的配置文件也带走了。从那之后,我才真正开始重视Linux账号和权限管理这个基本功。
要理解账号体系,先要清楚Linux里“用户”其实是一个数字身份的抽象。每个用户对应一个唯一的UID,用户组对应GID。系统真正识别的不是用户名,而是这一串数字。用户名是给人看的,UID/GID是给内核看的。你可以在/etc/passwd里看到所有用户的基本信息,但密码并不在这里,而是在/etc/shadow文件里,后者只有root或具备相应权限的账号才能读取。
/etc/passwd中每一行由7个字段组成,用冒号分隔,依次是:用户名、密码占位符、UID、GID、注释信息、家目录、登录Shell。比如:
code复制user01:x:1001:1001::/home/user01:/bin/bash
这里的x就是密码占位符,真正的密码哈希存在/etc/shadow中。系统内置了一些伪用户,比如bin、daemon、adm等,它们通常是服务运行所需的账号,不建议手动改动。UID为0的用户是超级用户,只有一个root,这个规则也是权限模型的基础。理解这一点之后,再去看useradd、usermod、userdel系列命令,就不会觉得它们是零散的了。
1.2 用useradd和usermod正确创建账号
我见过不少新手用useradd建完用户后,发现用户既没有家目录,也没有设置密码,登录之后连Home都没了。原因是CentOS和Ubuntu的useradd默认行为不同,这在面试题里出现过很多次。CentOS系列中useradd会默认创建家目录和Mail Spool,而Debian/Ubuntu系列的默认策略则依赖于/etc/login.defs和/etc/default/useradd配置。
实操中我建议显式指定参数,而不是依赖默认值。创建一个标准的业务账号,我会这样操作:
bash复制useradd -m -d /home/appuser -s /bin/bash -c "Application User" appuser
-m:强制创建家目录-d:指定家目录路径,如果路径和用户名不是默认对应关系,这个参数就很关键-s:指定登录Shell,如果这个用户只跑服务不需要交互登录,可以指定为/sbin/nologin-c:添加注释,团队协作时注释一个“这是谁、干嘛用的”,几个月后你回来看账号列表,会感激当初写注释的自己
创建完用户后立即设置密码或锁定密码。如果暂时不需要密码登录,可以用passwd -l username锁定,或者直接设置一个随机密码:
bash复制echo "RandomStr_2024" | passwd --stdin appuser
注意--stdin这个参数是CentOS系列支持的写法,Debian/Ubuntu下不识别,需要用chpasswd命令替代。这个是跨发行版操作常见的坑。
用usermod可以修改已有用户的属性。给用户追加到附加组、修改家目录、修改Shell、锁定或解锁账号,是日常运维的高频操作:
bash复制usermod -aG docker appuser
usermod -d /newhome/appuser -m appuser
usermod -s /sbin/nologin appuser
-aG这里一定要带上-a,表示append追加,不加-a的话会把用户从原来的附加组里全部移除,只保留新加的组。这个坑我踩过一次,当时一个同事本来在组dev里,我用usermod -G ops把他加进运维组,结果他瞬间失去了dev组的所有权限,报障电话立刻打过来。
1.3 /etc/skel目录与登录Shell的联动
创建用户时,家目录中的初始化文件来自/etc/skel目录。这个目录里默认有.bashrc、.bash_profile、.profile等文件。如果你希望新建用户默认带有某些环境变量、别名或脚本,把它们放到/etc/skel里即可。
举个例子:公司统一使用私有仓库,需要新用户默认配置npm或pip的镜像源,我在/etc/skel/.bashrc末尾追加了:
bash复制export PIP_INDEX_URL=https://mirrors.private.com/pypi/simple
alias ll='ls -lhtr'
这样以后所有新建用户都会自动带上这些配置,不用再一个个去补。这里有个细节:/etc/skel的改动只影响新建用户,对已存在的用户不生效。如果要批量修改存量用户,得写个循环脚本遍历/home下的目录逐个追加。这种批量操作最好先在一台测试机上验证,或者从一个用户开始,确认没有问题再全量执行。
关于登录Shell,我特别建议为服务账号指定/sbin/nologin而不是/bin/bash。比如运行Nginx的nginx用户、运行MySQL的mysql用户,它们本质上是服务进程的身份载体,不是给人登录用的。如果给了bash,一旦Web应用被入侵,攻击者直接su nginx拿到的就是一个可交互的Shell,攻击面立刻扩大了很多。很多安全加固文档都会检查账号的Shell类型,凡是服务账号带着bash的都会被审计出来,这是安全合规中最常见的整改项。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 文件权限体系:从r/w/x到特殊权限位
2.1 权限位的组成与数字表示原理
文件权限是Linux权限管理中最核心的落地场景。每个文件或目录都有三组权限,分别针对属主(user)、属组(group)和其他人(other)。每组权限由读(r=4)、写(w=2)、执行(x=1)三位组成,权限的数字表示法其实就是二进制的简写。
为什么读是4、写是2、执行是1?因为4=2^2,2=2^1,1=2^0,三个权限位分别占一个二进制位,组合起来正好是一个0-7的八进制数。比如rwx对应的二进制是111,换算成十进制就是7;r-x对应101,就是5。所以我一直建议新手不要死记chmod 755、chmod 644这些常见组合的数字,而是理解这个计数原理,遇到rwxr-xr--这种形式能一眼换算出来。
目录权限和文件权限的含义有本质区别,很多人在这里栽跟头。
- 目录的
r权限:能列出目录内容,也就是能执行ls查看里面有哪些文件名 - 目录的
w权限:能在目录里创建、删除、重命名文件或子目录 - 目录的
x权限:能进入目录,即能cd进去
注意一个反直觉的地方:如果对目录只有r权限而没有x权限,你虽然可以ls看到文件名列表,但无法进入目录,也无法查看文件的inode元信息,ls -l会报错。反过来只有x没有r,你能进入目录,但是看不见里面有什么东西,你只能访问已知名字的文件。这种奇怪配置在实战中几乎不会出现,但理解了它能帮你避免对权限模型的错误猜测。
2.2 chmod、chown和chgrp三件套的正确用法
chmod负责改权限位,chown负责改属主,chgrp负责改属组。日常用得最多的是前两个。
bash复制chmod 755 script.sh
chmod u+w file.txt
chmod -R 755 /path/to/dir
chown appuser:appgroup /data/app
chown -R appuser:appgroup /data/app
符号模式我特别推荐给精确操作场景。比如只想给属主加执行权限,不想动其他权限位,用数字模式必须先知道当前权限是多少,然后心算出修改后的值,容易出错。chmod u+x则直接表达“给属主加执行权限”,简洁且不易误伤。这个特性在写自动化脚本时尤其有用,因为脚本里往往不知道目标文件的初始权限状态。
chown有一个高频陷阱:chown user:group file这条命令里,冒号两边分别写用户名和组名,但如果你只写chown user file,文件的属组不会变。如果想同时改属主和属组,必须写冒号。另外chown -R递归修改时,如果目录里有软链接,需要特别注意。默认情况下chown会跟随软链接去修改目标文件,而不是修改软链接本身,这在某些场景会造成意外。如果希望只修改软链接本身,需要加-h参数。我在处理/usr/bin下的软链时踩过这个坑,chown -R一个目录,结果把所有软链接指向的真实文件属主全部改了,排查了半天。
2.3 特殊权限位:SUID、SGID与Sticky Bit
之前讲的r/w/x三个权限位应对的是大多数常规场景,但还有三个特殊权限位是面试和实战中的高频考点。
SUID(Set User ID)作用于二进制可执行文件上,它的效果是:当普通用户执行该文件时,进程的有效用户ID会临时变成文件属主的UID。最典型的例子是/usr/bin/passwd,普通用户需要修改自己的密码,而密码文件/etc/shadow只有root能写,于是passwd程序被设置了SUID位,普通用户执行它时临时获得root身份去写密码文件。查看SUID位的方法是ls -l中属主的x位置显示为s(小写s)或S(大写S,表示没有x权限时的占位,这种情况通常是不正常的)。
设置SUID的命令:
bash复制chmod u+s /path/to/binary
chmod 4755 /path/to/binary
SGID作用于目录时,新创建的文件或子目录会继承父目录的属组。这个特性在多人协作的项目目录中非常有用。比如团队共享目录/data/team,属组是teamgrp,给它设置SGID后,无论谁在里面新建文件,文件的属组都是teamgrp,而不是创建者自己的主组,这样团队成员之间互相修改文件不会遇到权限障碍。
Sticky Bit作用于目录,限制删除权限。设置了Sticky Bit的目录里,只有文件属主、目录属主或root才能删除文件,其他人即使对该目录有写权限也删不动别人的文件。/tmp目录的权限就是drwxrwxrwt,最后的t就是Sticky Bit。如果没有这个位,任何用户都能在/tmp里删掉别人的临时文件,系统就乱套了。
实操中建议定期扫描系统中的SUID文件,因为SUID是提权攻击的高发点,凡是不需要SUID的可执行文件都应当去掉这个位。可以这样扫描:
bash复制find / -perm -4000 -type f 2>/dev/null
2.4 umask与默认权限的计算逻辑
umask决定了新建文件和目录的默认权限。它的本质是一个掩码,用于从系统默认权限中“扣掉”权限位。系统默认值通常是:文件666,目录777。新建文件默认没有可执行权限,因为创建文件通常只是写数据;新建目录需要有可执行权限,否则无法进入目录。
计算方式是:默认权限减去umask值。比如umask是022,那么新建文件的权限是666 - 022 = 644,新建目录是777 - 022 = 755。这个结果和大多数人的直觉一致:文件是rw-r--r--,目录是rwxr-xr-x。
但这里有个需要注意的地方:umask的减法不是十进制数值相减,而是按权限位逐位扣减。如果umask是027,那么目录还是777 - 027 = 750,文件是666 - 027 = 640。如果umask是033,目录是777 - 033 = 744,文件是666 - 033 = 644。看起来好像数字减法没问题,但在特殊情况下会有偏差。比如umask为111时,按位操作后目录是666,文件是666,因为这个掩码扣掉的是所有用户的执行位,但文件本来就没有执行位,所以文件权限只受其余位影响。准确的理解是:umask中为1的二进制位,在默认权限中会被置0。
在生产环境中,我一般把业务服务器的umask设置为027而不是默认的022。原因是027会让新建文件默认变成640,属组可读,其他人完全不可读。这对于多人在同一台服务器上管理服务、但又不希望完全开放权限的场景很合适。修改umask可以直接在/etc/profile或/etc/bashrc里改,也可以给单个用户写在~/.bashrc中。如果是一个服务进程,还必须在启动脚本或systemd单元中指定umask,因为systemd默认的umask是022,从shell继承的umask不一定会传递给它。
3. 权限管理的高级应用:ACL与sudo
3.1 ACL扩展权限解决单一属主/属组不够用的问题
标准的UGO权限模型只能设置属主、属组、其他人三组权限,这在现实中经常不够。比如一个研发团队,有个目录/data/project,开发人员需要读写,部分测试人员只需要读,还有一个外包人员需要临时写几天。用传统模型实现需要建多个组、反复改权限,非常繁琐。这时ACL(Access Control List,访问控制列表)就派上了用场。
ACL允许你为任意用户或组单独设置权限,而不受属主/属组限制。查看ACL用getfacl,设置用setfacl。我举个例子:
bash复制setfacl -m u:zhangsan:rwx /data/project
setfacl -m g:testers:rx /data/project
setfacl -m u:waibao:rwx /tmp/temp_share
第一条命令给用户zhangsan添加对/data/project的rwx权限,第二条给testers组添加rx权限,第三条给外包临时账号添加临时目录的写权限。配合ACL,这些场景不需要动核心目录的属主属组,非常灵活。
需要注意两点。第一,用ls -l看到权限末尾有+号,就表示这个文件或目录带有ACL。第二,chmod修改权限会影响ACL中的ACL mask值。ACL mask的作用是限制ACL中命名的用户和组的最大权限,属于ACL机制里的一个特殊概念。默认情况下mask是全部权限,修改属组权限时mask会被联动调整,可能导致已有ACL条目权限被压缩。排查ACL“权限明明加了但实际不生效”的问题,首先要看的就是mask值。
3.2 sudo权限委派与sudoers配置细节
sudo是Linux权限管理中“给普通用户提权”的标准方式。比起直接让人用root密码登录或su切到root,sudo的日志审计、命令粒度控制、密码策略都要好得多。生产服务器上禁用root直接SSH登录、让管理员通过sudo执行特权命令,已经是业界标配。
/etc/sudoers是这个机制的核心配置文件,必须用visudo命令编辑,因为它会检查语法,防止配错后所有sudo都失效。常见配置形式:
plaintext复制# 允许wheel组的用户使用所有命令
%wheel ALL=(ALL) ALL
# 允许zhangsan在不输入密码的情况下重启nginx
zhangsan ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx
# 允许testers组的用户以root身份执行特定命令
%testers ALL=(root) /usr/bin/systemctl restart nginx, /usr/bin/tail /var/log/nginx/*.log
ALL=(ALL) ALL这个配置翻译成人话是:允许所有主机上(第一个ALL)的所有用户(括号里的ALL)以任意用户身份(括号前的用户列表)执行全部命令(最后的ALL)。严格按最小权限原则,最后的命令列表应当精确到具体命令和参数,而不是一概ALL。
一旦把sudoers配坏了,所有sudo都会报错,而且普通用户没有权限修改这个文件,就会陷入“想修复但没权限修复”的死循环。这里给一个保险做法:修改前先备份,然后用visudo -c校验语法,一定不要直接退出SSH会话,先另开一个终端测试sudo是否正常,再关掉旧会话。如果已经把sudo搞失效了,唯一可靠的办法是登录到物理控制台或云厂商的救援模式,以单用户模式启动系统修复。
3.3 su与sudo的使用边界和日志审计
很多老哥习惯用su - root切换到root,然后在一大堆命令下操作,最后退出。这种做法的最大问题是:没有审计。切换到root之后做的每件事,都以root身份执行,日志里只看到操作时间,很难追溯是谁干的。而sudo恰恰相反,/var/log/secure(CentOS)或/var/log/auth.log(Ubuntu)会把每一次sudo的命令、执行用户、时间都记录下来。出问题时,这份审计记录能精确还原操作链路,这是运维事故排查中非常重要的证据。
关于切换后是否加载目标用户环境变量,su和su -有区别。su -模拟完整登录,会加载目标用户的环境变量、进入其家目录,相当于“变成另一个人”;su只是切换UID,保留当前Shell的环境变量。绝大多数情况下建议用su -,否则容易遇到“sudo能跑、直接执行却找不到命令”的环境变量问题。
在个人使用习惯上,我给自己两条原则:
- 能不用root登录就不用root登录,日常操作先用普通用户,需要提权时用
sudo加具体命令 - 每个管理员分配独立账号,绝不共享root密码,配合sudo日志做审计
这样的做法在出事的时候能快速定位到具体责任人,平时也不会因为权限过大误操作搞挂服务器。
4. 账号生命周期与安全加固实战
4.1 账号的创建、批量管理与回收流程
账号管理不只是创建和授权,更关键的是账号生命周期管理。一个员工离职、一个项目下线,如果账号没有及时回收,就是安全隐患。业内出过不少事故:离职员工的账号没有被禁用,结果被外部拿到后登录服务器删数据。所以账号生命周期至少要覆盖四步:创建、授权、定期审计、回收。
创建一个标准账号我会走一套固定的流程,避免漏项:
- 确认需求:这个账号给谁用、用来干什么、需要哪些权限
- 创建账号并指定Shell:交互式登录给
/bin/bash,纯服务用途给/sbin/nologin - 初始化密码并设置过期策略:首次登录强制改密码
- 加入必要的附加组并配置sudo或ACL权限
- 验证账号能正常登录且权限符合预期
批量创建账号时,写个脚本循环执行即可,但要注意两点:一是密码生成要足够复杂且不重复;二是批量执行前先小范围试点,不要一上来全量跑。
批量禁用或删除账号的场景,实战中最常遇到的是“用户已离职但还需要保留他的数据”和“直接删除账号”两种选择。前者建议用usermod -L锁定账号(-L可以在/etc/shadow的密码哈希前加上!),再把用户从所有组里移除以回收权限;后者才用userdel -r,-r会连带删除家目录。锁定账号比直接删除安全,因为数据还在,可以后续慢慢处理;直接删除账号后数据通常无法找回。
4.2 密码策略与账号过期控制
密码策略是账号安全的第一道门。Linux自带的密码策略控制主要在/etc/login.defs和/etc/shadow里体现。/etc/shadow中每一行密码字段有多个部分,用冒号分隔,分别记录密码哈希、最后修改时间、最小修改间隔、最大有效期、过期警告天数、宽限期、账号失效日期和保留字段。实际操作中,设置密码策略时最常用的是chage命令:
bash复制# 设置用户zhangsan每90天必须改密码,过期前7天提醒
chage -M 90 -W 7 zhangsan
# 强制用户首次登录后修改密码
chage -d 0 zhangsan
# 查看用户的密码过期信息
chage -l zhangsan
密码过期策略的核心目的是让密码有“保质期”,降低密码被长期泄露而不自知的风险。但过度频繁地要求改密码(比如30天)实际效果并不好,用户容易把新密码写成老密码后面加个数字。根据经验,90天到180天是比较平衡的值。
注意一点:chage -d 0强制首次登录改密码这个操作,配合passwd初始化密码时一定要写清楚给用户的话。如果不说明,用户登录后不知道要改密码,或者改了之后忘记新密码,又得走流程重置,整个过程体验很差。
4.3 定期检查账号和权限风险的几个实用命令
生产环境做账号权限巡检,我有一套固定的检查清单,全部用命令完成,不会遗漏重点。
- 查看所有拥有UID为0的账号,确认是否只有root一个:
bash复制awk -F: '$3==0{print $1}' /etc/passwd
如果出现其他用户UID为0,说明有人创建了“影子root”,这是绝对不允许的,必须立即处理。
- 查看没有密码的账号:
bash复制awk -F: '($2==""){print $1}' /etc/shadow
密码为空意味着可以不输入密码直接登录,这种账号必须立即锁定或设置密码。
- 查看最近一次修改密码时间是否过期:
bash复制chage -l username
批量检查可以结合awk遍历/etc/shadow。
- 扫描SUID/SGID文件:
bash复制find / -perm -4000 -o -perm -2000 -type f 2>/dev/null
- 查看当前系统里的登录会话和用户登录历史:
bash复制who
last
lastlog
这些命令定期跑一遍,能发现绝大多数明显的账号风险。配合grep可以快速过滤异常。
4.4 日志审计与常用工具
日志是账号权限管理的事后维度,出了问题能不能复盘,靠的就是日志。
last和lastlog:查看用户登录历史和每个用户最后一次登录时间journalctl -u sshd或/var/log/secure:查看SSH登录尝试和sudo执行记录auditd:如果需要更精细的文件访问审计,Linux审计框架能监控指定文件的所有读写操作
有一次排查线上异常日志写入,发现某个服务目录下多出了来源不明的文件,我第一时间查看/var/log/secure里的sudo记录和lastlog,发现一个被遗忘的测试账号在一个月前还有登录记录。顺着登录IP定位到是某位前同事的备份脚本在跑定时任务。这就是日志审计的价值。
对于安全和审计要求较高的环境,建议额外部署集中日志收集或堡垒机系统,把所有服务器的登录和sudo记录集中到一个地方。单机日志很容易被清理,集中后才能做到“删了也有副本”。
5. 常见问题与排查技巧实录
5.1 用户创建后无法登录或没有家目录
现象:useradd创建用户后,用该用户登录,终端报错“No directory, logging in with HOME=/”,或者干脆登录失败。
原因:多半是创建时没有生成家目录,或者家目录权限不对。
排查方法:先查看/etc/passwd里该用户的家目录字段,再用ls -ld /home/username确认目录是否存在、属主是否正确。如果不存在,手动创建并复制/etc/skel模板文件:
bash复制mkdir /home/appuser
cp -r /etc/skel/. /home/appuser/
chown -R appuser:appuser /home/appuser
如果目录存在但权限被改过,记得家目录权限最好是755或700。如果权限是其他用户可写,SSH登录会报错拒绝。
5.2 chmod后权限与预期不一致
现象:执行chmod 750 file后,ls -l查看时权限位显示正常,但其他用户或组访问该文件时仍然报权限拒绝。
排查思路:不要只看UGO权限,先执行getfacl file查看是否有ACL条目,尤其注意ACL mask的值。mask会限制所有命名用户(除属主外)和命名组的权限,即使UGO看起来给的是750,如果mask是r--,那么组的权限就只剩下r。这种情况在之前配置过ACL的文件上特别容易出现。
另一种常见情况是文件在SFTP或FTP上传时被服务端重新设置了权限,上传后的权限和本地不一致。排查时先确认文件最后修改时间和权限变更时间是否吻合,往往能发现线索。
5.3 sudo执行报错“不在sudoers文件中”
现象:普通用户执行sudo命令,提示“xxx 不在 sudoers 文件中。此事将被报告。”
原因:该用户没有被加入任何有sudo权限的组(如wheel或sudo组),也没有在/etc/sudoers中被单独授权。
解决办法:用root或另一个有sudo权限的账号执行:
bash复制usermod -aG wheel zhangsan
或者用visudo在sudoers中添加:
plaintext复制zhangsan ALL=(ALL) ALL
这里强调一下,用usermod -aG时一定带-a,否则用户会被移出原有附加组。加完组后,用户需要退出重新登录才能生效,因为组权限在登录时读取。
5.4 服务器被频繁尝试SSH登录,如何从账号层面加固
现象:/var/log/secure里大量“Failed password”记录,来自同一或不同IP。
处理思路:
- 禁用root直接SSH登录,修改
/etc/ssh/sshd_config中的PermitRootLogin no,然后重启sshd - 使用密钥认证替代密码认证,设置
PasswordAuthentication no - 限制允许登录的用户或组,比如只允许
wheel组登录 - 对连续失败N次的IP启用临时封禁,可以用
fail2ban实现
账号层面最关键的还是“能登录的账号尽量少、密码尽量强、登录方式尽量用密钥”。即使攻击者扫描到了服务器IP,面对没有root登录、没有密码认证、只有特定账号可密钥登录的配置,成功率基本为零。
5.5 常见问题速查表
| 现象 | 可能原因 | 快速排查命令 | 解决方式 |
|---|---|---|---|
| 登录提示无家目录 | 家目录不存在或家目录权限错误 | ls -ld /home/用户名 |
手动创建目录并设置属主 |
| 文件权限明明有r,别人还是读不了 | 文件所在目录缺x权限,或ACL mask限制 | getfacl 文件路径 |
检查父目录权限,调整mask |
| sudo执行报错 | 用户不在sudoers中 | id 用户名查看组 |
加入wheel/sudo组或编辑sudoers |
| 新建文件权限不是预期值 | umask配置问题 | umask查看当前值 |
修改/etc/profile或用户~/.bashrc |
| 用户删不掉“device or resource busy” | 进程占用用户相关文件或目录 | ps -u 用户名 |
先kill相关进程再删除 |
| 修改/etc/passwd后用户无法登录 | 文件字段格式错误导致解析失败 | pwck校验 |
用vipw编辑并检查格式 |
账号和权限管理平时不出声,出问题就是大问题。我个人踩过的最深的一个坑是批量脚本里漏了-a参数,直接导致一批用户被移出附加组,权限全乱。从那以后,凡是涉及批量修改用户属性的操作,我都会先打印出操作前和操作后的id对比结果,确认无误再继续。权限管理没有捷径,靠的是操作前多想一层、操作后多查一眼。希望这篇内容能帮你把Linux账号和权限管理这块地基打得更扎实。
