刚接手一套Linux服务器的时候,我最大的感受不是命令不熟,而是“账号和权限”这四个字里藏着的坑,远比想象中多。很多初学者能给用户加个sudo就算完事,可真正上了生产环境才发现,随便一个权限位、一条sudo规则、甚至umask设置,都可能成为安全隐患或故障导火索。这篇文章不打算从man手册抄一遍,而是把这些年实操里踩过的坑、总结出的套路,连同底层原理一起拆开讲清楚,希望能帮正在做系统运维、或者刚入门Linux管理的朋友少走点弯路。
1. 先捋清楚:账号和权限管理的整体思路
1.1 账号体系的两张核心表
Linux的账号管理,本质上就是维护几个文本文件。很多人一听“用户管理”就条件反射去敲useradd,但真正出问题时,排查对象往往是/etc/passwd和/etc/shadow这两个文件。
/etc/passwd每一行代表一个用户,字段用冒号分隔,依次是用户名、密码占位符、UID、GID、注释信息、家目录、登录Shell。注意这里第二个字段通常是一个“x”,真正的密码早就被挪进/etc/shadow了。为什么这么设计?历史原因是早期密码就存在passwd里,但/etc/passwd是全局可读的,任何用户都能读,密码哈希一旦泄露,离线爆破只是时间问题。后来把密码密文挪到只有root能读的shadow文件里,普通用户无法直接查看哈希,安全性才基本立住。
/etc/shadow里存的是加密后的密码哈希,以及密码有效期相关的字段:最近修改日期、最短修改间隔、密码过期警告天数、过期宽限期等。用passwd修改密码时,实际更新的就是这个文件。我们做账号审计的时候,重点检查的也是shadow文件里的哈希算法标记——现在主流是$1$(MD5)、$5$(SHA-256)、$6$(SHA-512),新的发行版还会看到$y$(yescrypt)。如果系统里还有老旧的MD5哈希,建议尽早强制用户改密,因为MD5在GPU集群下爆破成本已经非常低了。
1.2 身份模型:文件是给用户看的,权限是给进程用的
这件事我一开始也理解得很浅:以为权限就是“用户能不能读写某个文件”。实际上Linux的权限判断发生在进程层。当你执行ls、cat、vi这些命令时,真正发起系统调用的是bash进程或者编辑器进程,而内核在做权限校验时,用的是进程的UID/GID,而不是终端面前坐着的那个“人”。
这里有个极其容易混淆的点:有效用户ID(EUID)和真实用户ID(RUID)。一个进程通常以当前登录用户的身份运行,所以EUID等于RUID。但一旦设置了SUID权限位,进程的EUID会被切换成文件属主的UID,权限判断也随之变化。后面讲特殊权限时会专门展开,这里先建立概念:权限管理的核心对象是进程,用户只是进程身份的来源。
所以,在做账号规划时,不要只想着“给谁开个账号”,要想清楚这个账号启动的进程需要访问哪些资源。一个纯后端API服务账号,就不该有交互式Shell;一个CI/CD脚本账号,不该能sudo到root。最小权限原则落到实操层面,就是从账号的Shell字段、家目录权限、sudo规则、可执行文件属主这几个维度强制收敛。
2. 用户和组管理实操:命令背后的逻辑
2.1 添加用户别只会useradd
大部分教程会告诉你useradd -m username建用户,这本身没错,但生产环境里要规划的细节更多。今天我创建账号的典型套路是:
useradd -m -s /bin/bash -c "应用部署账号" deploy echo '临时密码' | passwd --stdin deploy chage -d 0 deploy三个参数值得逐一拆解:
- -m:自动创建家目录并复制/etc/skel下的骨架文件。如果不加,家目录不会出现,后续很多软件默认往家目录写配置时会失败。
- -s /bin/bash:指定登录Shell。如果这个账号只需要跑服务,我通常给/sbin/nologin,避免交互登录,从源头减少被爆破面。
- chage -d 0:强制用户在首次登录时修改密码。很多安全基线里都有这条需求,它的实现原理是修改shadow里的“最近修改日期”字段为0,系统强制走一次改密流程。
另外,useradd默认的UID/GID分配来自/etc/login.defs里的UID_MIN和UID_MAX范围。一般发行版是1000到60000。如果要对多台服务器保持账号UID一致,比如配合NFS共享家目录,就需要手动指定UID:
useradd -u 1500 -g devgroup -d /home/devops -s /bin/bash devops这样同步到别的机器时,只要UID和GID一致,文件归属就不会错乱。跨服务器同步账号时,我最常犯的错误是想当然认为用户名一样就万事大吉,实际上内核认的是UID不是名字。A机器上devops是1500,B机器上devops是1600,共享目录里A创建的文件在B上会显示成另一个用户属主,排查起来非常绕。
2.2 组策略别忽略主组和附加组
Linux的组分主组(初始组)和附加组。主组在/etc/passwd第4个字段,附加组在/etc/group里。用户创建文件时,默认的属组就是主组。想给一个用户临时开某个目录的权限时,把用户加进对应的附加组更合理:
usermod -aG docker devops这里的-aG是“追加”而不是“覆盖”。很多人容易漏掉-a,直接usermod -G docker devops,结果用户被从其他附加组里清空了。这条命令我至少踩过三次坑,每次都得先去/etc/group确认一遍。
组删除时也要留个心眼:组里还有用户时,groupdel会拒绝执行。如果一个组已经没人用了,但某些历史文件还在以这个组为属组,直接删掉组会造成文件属组显示为数字,这种“孤儿属组”清理起来很麻烦。稳妥做法是先迁移文件属组,再删组:
find /data -group oldgroup -exec chgrp newgroup {} \; groupdel oldgroup2.3 密码策略:不光设定时,还要设过期
很多团队对密码的管理停留在“passwd username改一下”的层面,完全忽略了老化策略。真实的运维需求是:新建账号必须定期改密、密码不能无限期有效、离职员工账号要能快速禁用。这些需求用chage就能覆盖:
chage -M 90 -m 7 -W 14 devops-M 90表示密码最长使用90天,-m 7表示两次密码修改最短间隔7天,-W 14表示过期前14天开始警告。这些配置写入/etc/shadow的第5、4、6个字段。如果某天想临时排查一个账号,又不想改密码,可以用chage -l username查看当前策略。
禁用账号和锁密码是两码事:
- usermod -L / passwd -l:锁定密码,本质是在加密密码前加感叹号,但账号本身还能通过SSH密钥登录。
- usermod -s /sbin/nologin:把Shell改成nologin,阻止交互登录。
- 彻底禁用SSH登录:在sshd_config里加DenyUsers,或者把用户的公钥从authorized_keys里删掉。
我个人处理离职员工账号的标准流程是:先摘掉SSH公钥,再锁密码,再把用户从所有附加组里移除,最后把用户Shell改成/sbin/nologin。顺序很重要,先摘公钥能防住密钥登录,锁密码能防密码登录,移除附加组是防止他通过组权限继续访问业务目录,改Shell是补最后一道闸。
3. 权限管理的核心:rwx、特殊权限位与ACL
3.1 权限位的字符串、八进制与umask
接触Linux权限,绕不开ls -l输出的那串字符,比如-rwxr-xr--。这10个字符里,第1位是文件类型,后面9位是三组rwx。每组三位分别对应属主(u)、属组(g)、其他用户(o)。权限项的含义也分文件与目录两种:
- 读权限r:文件可读;目录可列出目录项,但要注意只有r权限时你虽然能看到文件名,却拿不到inode信息,ls -l会报错。
- 写权限w:文件可改;目录可新增、删除、重命名目录下的文件,这需要目录的x权限配合。
- 执行权限x:文件可运行;目录可进入(cd),而且是所有目录操作的前提。
这里最容易出问题的就是目录权限。很多人以为给目录777就万事了,实际上目录的r和x是绑定的,只有r没有x,你连目录都进不去,能ls列出名字但要访问文件具体属性还会报权限错误。我排查“能看见文件但打不开”的问题时,第一反应一定是去看父目录的x权限。
八进制权限是rwx的数值化表示:r=4,w=2,x=1,三者相加得到一个0到7的数。chmod 755 file等于属主rwx、属组r-x、其他r-x。这背后的计算逻辑就是简单的位权相加,没有魔法。新文件默认权限不是直接等于chmod结果的,还受umask影响。umask本质是一个掩码,最终权限 = 666(文件)/777(目录)减去umask的值。系统默认umask通常是022,所以新文件是644、新目录是755。如果团队有共享协作目录,把umask改成002,新文件的组权限就从r--变成rw-,这样同组用户才能真正协作编辑文件。
3.2 SUID、SGID和粘滞位:三个容易误用的特殊权限
这三个特殊权限分别对应用户、组和其他用户位的x位置,如果出现s、S或t,说明特殊权限被打开了:
- SUID(4000):当文件被设置为SUID时,任何用户执行这个文件,进程的EUID会变成文件属主的UID。最经典的例子是/usr/bin/passwd。普通用户修改自己的密码需要写/etc/shadow,但shadow文件只有root能读写,为什么普通用户还能修改?就是因为passwd这个文件属主是root且带有SUID位,普通用户执行passwd时,进程临时获得root权限去修改shadow文件。SUID是很强力的权限,一个编译得很干净的提权漏洞往往就藏在带SUID的程序里。我从来不建议随意给脚本或解释器显式添加SUID,尤其是给python、perl这种解释器加SUID,几乎等于给系统开了一扇大门。
- SGID(2000):目录的SGID位最常用。目录设置SGID后,在该目录下新建的文件和子目录会自动继承目录的属组,而不是创建者的主组。团队共享目录的标准做法就是:chown -R root:devteam /shared,chmod -R 2770 /shared。这样无论谁在这个目录里创建文件,文件属组都是devteam,避免了“A创建的文件B没法改”的协作噩梦。
- 粘滞位(1000):/tmp目录就是典型例子。粘滞位只对目录有效,它的效果是:目录下的文件只有文件属主、目录属主或root才能删除,其他人即使对目录有写权限也不能删别人的文件。如果不加这一位,/tmp下任何用户都能互相删文件,系统早就乱套了。
设置和移除的惯用写法:
chmod u+s /path/bin/tool chmod g+s /data/shared chmod +t /tmp排查特殊权限时,可以用find直接扫描:
find / -perm -4000 -type f 2>/dev/null find / -perm -2000 -type d 2>/dev/null这两条命令我每次安全巡检都会跑一遍,重点看有没有来历不明的SUID文件。曾经的挖矿病毒就会往/tmp或家目录里丢带SUID的二进制,配合漏洞提权,危害极大。
3.3 ACL:rwx覆盖不了时的救星与陷阱
标准的rwx权限在遇到“一个目录既想让某个用户能写,又不想让整个组都能写”这种需求时会很尴尬。ACL(访问控制列表)就是为了解决这种细粒度授权而生的。
setfacl -m u:zhangsan:rwx /data/project setfacl -m g:devteam:r-x /data/project getfacl /data/project设置ACL后,ls -l的权限位最后会多一个+号,表示这个项目还有附加ACL规则。ACL规则本质上是扩展了权限判断逻辑,内核在传统u/g/o检查之后,还会继续匹配ACL条目。但ACL有个隐藏坑:只要设置过ACL,chmod对属主/属组权限的修改可能导致ACL mask自动收紧。比如你设置了一个ACL规则,然后chmod 750,ACL mask会自动变为r-x,一些原本设置的项目可能被“看不见地”裁剪掉。所以排查ACL问题第一步永远是getfacl而不是ls -l。
另一个ACL的坑是在做文件迁移时。tar和cp默认不一定保留ACL规则,用cp -a或者tar --acls才能保留。如果你在一个设置了ACL的目录里用普通cp复制,目标文件的ACL会丢失。跨备份恢复时,忘记加ACL参数可能导致上层业务权限校验失败,这个坑我踩过一次之后就把“备份要带ACL”写进了团队SOP。
4. sudo与提权管理:比直接登录root更稳妥的日常操作
4.1 为什么生产环境不该敞开root?
很多团队图省事,给所有运维都发root密码,服务器被黑后审计日志一片模糊。直接登录root的问题有三个:一是无法追溯具体是谁执行的命令,除非开了auditd且记录完整;二是误操作没有缓冲,一条rm -rf直接删库;三是密码一旦泄露就是整个系统的沦陷,不存在权限分级。
更好的方式是sudo。sudo的作用是让普通用户以特定身份执行特定命令,并且把执行记录写进/var/log/secure或/var/log/auth.log。给一个用户sudo权限不代表给他全部权力,完全可以精确到命令路径。
4.2 用visudo设计最小化授权
编辑/etc/sudoers的标准姿势是visudo,因为它会做语法检查,语法错误会导致sudo完全不可用,那时只能进单用户模式修复,非常痛苦。sudoers的规则格式是:
用户名 主机名=(可切换身份) 命令列表几条我在实际环境里常用的模板:
# 给开发账号NOPASSWD重启某个服务 devops ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart webapp # 允许运维账号管理用户,但不用密码 op ALL=(ALL) NOPASSWD: /usr/sbin/useradd, /usr/sbin/usermod, /usr/sbin/userdel # 给某账号完整root权限,但要求密码(默认行为) admin ALL=(ALL) ALL使用NOPASSWD的争议点在于:方便是方便,但如果被入侵,攻击者可以无阻碍地执行白名单命令。所以白名单命令一定要收缩到最小集合,像vim、python、bash这类能衍生Shell的命令,绝不能出现在NOPASSWD列表里。一个很经典的提权技巧就是:允许用户无密码跑vim,vim里执行!bash就直接获得root Shell。解决思路是命令路径要精确,必要时加上参数校验,比如:
devops ALL=(root) /usr/sbin/visudo -c, /usr/sbin/useradd *但即使这样,用户仍可能通过改环境变量或利用命令自身特性绕过,所以我的建议是:可给sudo权限的账号数量宁可少,不可多;每次变更后,用sudo -l确认最终生效的规则。
4.3 sudo的审计与日志追踪
经常有人问我,怎么知道某台服务器上谁什么时候执行过rm命令。答案就在sudo日志里:
grep sudo /var/log/secure | tail -50日志里能看到执行时间、用户名、执行的完整命令。但如果命令里带了引号或重定向,日志可能记录不全。更严谨的审计可以引入专门的sudo日志收集,把auth.log实时同步到集中日志平台。我见过不少公司出了安全事件后,想查“谁删了目录”却发现sudo日志被日志轮转清掉了,所以生产服务器的日志保留周期至少应该覆盖到历史安全审计的窗口,比如180天。
5. 账号权限管理的常见坑与排查经验
5.1 新文件权限不对:先查umask,再查目录属组
现象就是两个用户同组协作,A在共享目录里创建了文件,B打开文件却提示权限不足。拿到这种问题,我会按以下顺序排查:
# 1. 看用户属于哪些组 id B # 2. 看共享目录的属组和权限 ls -ld /data/shared # 3. 看新文件的权限 stat -c "%A %U %G" /data/shared/testfile # 4. 看默认umask umask几乎80%的情况是文件权限是644,但“其他用户”的权限只有r,B用户需要的是w,而B用户和A用户又不是同组。解决办法就是前面说的:把共享目录加上SGID并设置组权限为rwx,并且把A和B都加入同一个附加组。这是协作场景的标准解,直接chmod 777只会把安全底线降低。
5.2 家目录权限过宽:NFS、SSH都隐性问题
家目录权限太宽(比如777)表面上看没什么,实际上会带来两个具体隐患:一是SSH严格模式下会拒绝使用权限过宽的密钥文件,导致密钥登录失败;二是NFS共享时,家目录权限过宽等于让网络内所有用户都能读别人的私密文件。
我处理家目录的标准策略是700,即只有属主能进。如果确实有共享需求(比如团队开发目录),我给单独的共享目录设置2770+ACL,而不是宽泛地放大家目录权限。
5.3 SELinux强制访问控制:Linux权限体系里容易被忽视的“隐形锁”
很多人在排查权限问题时,chmod也对了、属主也对了、ACL也没问题,文件还是无法访问,最后发现是SELinux在背后拦截。SELinux是整个权限体系的上层建筑,它允许在传统DAC(自主访问控制)之上再叠加一层MAC(强制访问控制)。用getenforce可以查看当前状态:
getenforce如果显示Enforcing,那么文件能否被服务访问,取决于SELinux上下文,而不只是rwx位。已知的高频问题是:
- 自定义目录里的文件被无法被Web服务器读取,因为文件缺少httpd_sys_content_t上下文
- 迁移数据后,家目录或NFS目录的SELinux标签丢失
快速解决思路是restorecon重置标签或者chcon手动修改上下文:
restorecon -Rv /data/webroot chcon -t httpd_sys_content_t /data/webroot/index.html我不建议一键setenforce 0,因为那等于关闭了系统自带的一道重要防线。正确的做法是确认是哪个上下文不对,再有针对性地调整。
5.4 账号被锁、密码过期、无法登录的排查链路
“用户说自己密码是对的但就是登不上”是工单系统里出现率排前三的问题。我惯用的排查链路:
# 1. 检查账号是否被锁定 passwd -S username # 2. 检查密码是否过期 chage -l username # 3. 检查Shell是否合法 grep username /etc/passwd # 4. 如果是SSH登录,检查sshd_config是否启用了AllowUsers/DenyUsers grep -i allowusers /etc/ssh/sshd_config # 5. 检查是否被fail2ban等工具临时封禁 fail2ban-client status sshd尤其注意密码过期那项:账号长期未登录,密码超过有效期后,SSH密码登录会被系统拒绝,但密钥登录可能仍然可用。所以遇到问题别只看一种登录方式,要横向比对。
6. 整理成可直接落地的权限管理清单
经验再多,不落成清单就容易漏。我每个季度会做一次账号权限复核,流程基本固定:
- 拉出所有可以SSH登录的账号列表:awk -F: '$7 != "/sbin/nologin" && $7 != "/bin/false"' /etc/passwd
- 对比业务人员名单,清除已离职账号
- 用find扫一遍SUID/SGID文件,核对每个文件来源
- sudo -l审计每个特权账号的sudo规则,收缩不再需要的白名单
- 抽查共享目录的属组、SGID、ACL设置是否符合团队结构
- 检查/var/log/secure里是否有异常sudo尝试和批量密码错误
这套清单执行起来不复杂,难的是坚持。很多团队只有出了事故才回头看权限,其实权限管理的本质不是“防止内部人作恶”,而是“万一账号泄露或被误用,把影响面控制在最小”。
7. 我的最后几条实操心得
Linux账号和权限管理,表面上是一堆命令的组合,实际上是一套“身份、能力、边界”的治理模型。
我自己这些年最深的两条体会:
第一,改动前一定要看“当前值”。不管是chmod还是usermod,先执行对应的查询命令看清现状,永远比凭记忆直接改稳妥。因为每个系统的历史配置都不一样,你脑子里的“标准配置”可能在另一台机器上是反例。
第二,权限变更必须留痕。在命令行执行的每一条chmod、chown、usermod、sudoers修改,都应该有对应的变更记录。哪怕只是记在内部工单里,也比等出了问题对着历史日志大海捞针强。
最后送上一句常被我挂在嘴边的话:权限问题的排查路径,永远是从“进程身份”出发,再验证“文件权限位”,最后检查“ACL与强制访问控制”。沿着这个顺序走,绝大多数疑难杂症都能快速定位。