经常有刚开始接触 Linux 的朋友跑来问我:我明明用 useradd 新建了一个用户,为什么连自己的家目录都进不去?还有的人问,为什么同一个账号在 A 机器上能 sudo,在 B 机器上就提示 not in the sudoers file?这些问题的答案,几乎都指向同一个根源——对 Linux 用户的理解还停留在“账号密码”的层面。
很多人把“Linux 用户”简单等同于“一个能登录的账号”,但真实情况要复杂得多。用户这个概念贯穿了进程、文件、权限、安全策略,甚至是整个系统能不能稳定运行。搞懂它,你才算真正摸到了 Linux 的门把手。这篇文章我会从底层机制讲起,再逐步落到命令实操和问题排查,适合刚入门的同学,也适合那些用了一年 Linux 但遇到怪问题只能靠百度救火的运维朋友。
1. 用户不只是账号:Linux 把“身份”拆成了三件事
1.1 UID/GID 才是内核真正认的“身份证”
先说一个很多新手容易忽略的事实:Linux 内核根本不认识用户名,它只认两个数字——UID(用户 ID)和 GID(组 ID)。你看到的 root、testdev 这些名字,纯粹是给人看的,方便记忆和沟通。系统在登录的时候,会先去 /etc/passwd 这个文件里查一下,把用户名翻译成对应的 UID,后面所有的权限判断都基于这个数字。
你可以用一条命令查看用户信息:
getent passwd testdev输出大概是这样的:
testdev:x:1001:1001::/home/testdev:/bin/bash用冒号分隔的 7 个字段,含义分别如下:
| 字段 | 示例 | 含义 |
|---|---|---|
| 用户名 | testdev | 给人看的登录名 |
| 密码占位符 | x | 真正的密码哈希在 /etc/shadow 里 |
| UID | 1001 | 用户唯一数字 ID |
| GID | 1001 | 用户主组的数字 ID |
| 注释 | 空 | 一般是姓名、电话等说明 |
| 家目录 | /home/testdev | 登录后的默认目录 |
| 登录 Shell | /bin/bash | 默认命令解释器 |
为什么要单独拎出来讲 UID?因为后面很多权限问题,本质上都是 UID 不匹配。比如你误删了用户的 /etc/passwd 记录,但文件系统里还留着一堆属于这个 UID 的文件,这时候这些文件就变成了“无主孤魂”,显示成数字 UID,谁也不认领。
不同发行版对 UID 的分配范围稍有差异,但大体遵循这样的约定:
| UID 范围 | 类型 | 说明 |
|---|---|---|
| 0 | 超级用户 | root,所有权限的源头 |
| 1-999 | 系统用户 | 给 sshd、nginx、mysql 这类服务用的运行账号 |
| 1000-60000 | 普通用户 | 人工登录使用的账号 |
系统用户和普通用户的一个核心区别是:系统用户通常不允许登录,Shell 是 /sbin/nologin 或者 /usr/sbin/nologin,这是为了安全——服务只负责跑自己的进程,不需要交互式登录。
1.2 用户和进程、文件之间的三角关系
理解用户,光看 /etc/passwd 不够,还得明白用户到底在系统里怎么“存在”。我习惯用一个说法:Linux 里的用户是通过三样东西体现的——他拥有的进程、他拥有的文件、以及他的家目录。
每个进程都带有一个 UID,内核判断这个进程能打开哪个文件、能读哪个目录,就看进程的 UID 和文件属主/属组的权限位是否匹配。这就是为什么你普通用户登录后,执行ls /root会得到 Permission denied,哪怕你猜得到里面有什么。
文件层面,ls -l输出的第三列和第四列,就是文件的属主 UID 和属组 GID。比如:
-rw-r--r-- 1 root root 1234 Feb 20 10:00 config.yaml这个文件属于 root 用户和 root 组,其他用户对它只有读权限。
家目录则是用户专属的“房间”,一般位于 /home/用户名。但这并不是硬性要求——系统用户的家目录可以放在 /var/lib/nginx 这种地方,甚至可以不设家目录。判断一个用户是否真正存在,标准只有一个:/etc/passwd 里有他的记录,且 UID 有效。
你可以把整台 Linux 想象成一座公寓楼:UID 是门牌号对应的身份 ID,家目录是房间,进程是这个住户在楼道里走动的行为,而文件的属主就是在房门口挂牌子的人。内核是物业,它不看你是谁,只看你牌子上写的 UID。
2. 动手实践:从创建一个能用的用户说起
2.1 useradd 和 adduser,哪条命令更适合你
很多教程不会强调,但实际用起来区别很大:useradd是一个底层命令,参数多、功能全,但默认不会给你创建家目录,也不会帮你设置密码;而adduser在很多发行版上是一个 Perl 写的交互式脚本,会一步一步问你问题,自动创建家目录、设置密码、复制骨架文件。
如果你在 Ubuntu 或 Debian 系系统上,强烈建议新手优先用adduser。如果你是写脚本、批量交付环境,那就必须用useradd,因为它是非交互式的,可以在自动化里稳定执行。
这两个命令的对比可以看这张表:
| 维度 | useradd | adduser |
|---|---|---|
| 类型 | 底层二进制命令 | 封装脚本 |
| 交互性 | 无,参数驱动 | 交互式向导 |
| 自动创建家目录 | 需要加 -m | 默认创建 |
| 自动设置密码 | 不会 | 会引导设置 |
| 适合场景 | 脚本、批量、云镜像 | 人工单台操作 |
2.2 新建用户时的关键参数与 gid 设置
当你需要用 useradd 创建用户时,最常用的参数组合是这些:
sudo useradd -m -d /home/testdev -s /bin/bash -g ops -G docker,develop testdev逐个拆开看:
-m:创建家目录。不加的话,用户登录后会找不到 HOME,很多命令会直接报错。-d:指定家目录路径。默认是 /home/用户名,如果你想换到 /data/users/testdev,就得显式指定。-s:指定登录 Shell。普通用户给 /bin/bash,服务账号给 /usr/sbin/nologin。-g:指定主组,也就是用户默认的 GID。-G:指定附加组,可以逗号分隔多个。
这里面最容易出问题的就是-g和-G的区别。主组只能有一个,它决定了你新建文件时“属组”那一列默认写的是谁;附加组可以有很多个,它决定你能额外访问哪些组的资源。比如你把一个开发用户加到docker组,他就能执行 docker 命令,因为 docker 的套接字权限对 docker 组开放。
热搜里“linux 新建用户时设置 gid”说的就是-g。有些老系统还要先确认组是否存在,如果ops组不存在,useradd会直接报错。所以更稳妥的顺序是:
sudo groupadd ops sudo useradd -m -g ops -G docker,develop testdev sudo passwd testdev2.3 修改、锁定与彻底删除
用户创建好之后,日常管理离不开三件事:改、锁、删。
修改用户信息用usermod,常用操作:
# 修改用户的家目录,并把旧家目录内容迁移过去 sudo usermod -d /home/newplace -m testdev # 修改用户的登录 Shell sudo usermod -s /bin/zsh testdev # 追加附加组,注意 -a 必须配合 -G,否则会覆盖原有附加组 sudo usermod -aG sudo testdev # 临时锁定用户,锁了之后无法登录 sudo usermod -L testdev # 解锁 sudo usermod -U testdev密码策略管理用chage,这个常被忽略:
# 查看用户的密码过期信息 sudo chage -l testdev # 强制用户 90 天后改密码,并在过期前 7 天提醒 sudo chage -M 90 -W 7 testdev删除用户时,我见过太多人直接userdel testdev,结果 /home/testdev 下的一堆文件留在那里没人认领。正确做法是用:
sudo userdel -r testdev-r会把家目录和邮件池一起删除。但注意,这个操作是不可逆的,删除前最好先确认用户是否还有重要数据,必要时先打包备份。
这里有个非常关键的提醒:不要轻易删除 UID 已经被文件系统引用的用户。如果你删掉用户后又马上新建一个同名用户,系统会分配一个新的 UID,那么旧用户留下的所有文件,会全部显示成新用户的名字。这在审计和合规场景里非常危险。
3. 用户组与权限边界:为什么一台机器可以很多人共用
3.1 主组与附加组:ls 为什么只显示一个组
用户组的存在,本质上是为了批量授权。如果系统里 50 个人都要访问某个项目目录,管理员不可能一个个去 chown,把用户加进同一个组,再给目录设置组权限,事情就解决了。
组的定义在 /etc/group 文件里,一行代表一个组:
ops:x:1002:testdev,alice,bob字段分别是:组名、密码占位符、GID、组成员列表。
每个用户在 /etc/passwd 里有一个 GID,这是他的主组。这个组名会出现在他新建文件的“属组”列里。但用户同时可以属于很多附加组,可以用groups testdev查看。
有个常见疑问是:为什么我明明属于 5 个组,ls -l显示的文件属组只有一个?因为文件属组只能写一个 GID,就是文件创建者在创建瞬间的主组。如果你想改变文件归属的组,用chgrp或者chown :组名。
3.2 文件权限 rwx 与 umask 的日常影响
文件权限位是用户体系最直接的落地体现。rwx三组分别对应属主、属组、其他人:
r:读。对文件来说能看内容,对目录来说能列出目录里的文件名。w:写。对文件来说能改内容,对目录来说能新建/删除目录里的文件。x:执行。对文件来说能运行,对目录来说能进入目录。
很多人容易搞混目录的“写”权限。比如你给一个目录设了 777,但你没有该目录的“执行”权限,你照样进不去,因为缺少x就无法遍历目录内容。
为什么你新建的文件默认是 644,而不是 666?因为系统里有一个umask默认掩码,通常是 022。文件最大权限 666 减去掩码得到 644,目录最大权限 777 减去掩码得到 755。想验证就执行umask,想临时改就umask 002。如果你的团队协作目录希望组内成员都能编辑,通常会把 umask 改成 002,让文件自动变成 664。
日常调整权限的三板斧:
chmod 750 /data/project # 设置精确权限 chown -R ops:ops /data/project # 递归修改属主属组 chmod -R g+w /data/project # 给组加写权限3.3 sudo 提权与特殊权限位的水有多深
普通用户的权限是受限的,但日常运维免不了要执行 root 级别的命令,这就得靠 sudo。sudo 的规则写在 /etc/sudoers 里,强烈建议用visudo编辑,因为它会检查语法错误,防止你把 sudo 配置文件改坏导致所有人都无法提权。
一个常见的规则示例:
# 给 sudo 组所有成员完整 root 权限 %sudo ALL=(ALL:ALL) ALL # 让 testdev 可以免密执行 systemctl 命令 testdev ALL=(ALL) NOPASSWD: /usr/bin/systemctl理解这些字段的顺序很重要:谁 在哪些主机上 以什么身份 执行什么命令。如果你把ALL=(ALL:ALL) ALL里的命令字段写成具体命令,那么该用户只能执行那一条命令,这是最小权限的一种玩法。
再往深走,特殊权限位是用户权限体系里最容易踩坑的地方:
SetUID:文件属主是 root 且权限位是rws,比如 /usr/bin/passwd。普通用户执行它时,会临时以 root 身份运行,这样才能修改 /etc/shadow。SetGID:目录设置了rws后,在里面新建的文件会自动继承目录的属组,这对共享协作目录非常有用。Sticky Bit:/tmp 目录权限是rwxrwxrwt,它保证每个用户只能删除自己创建的文件,哪怕目录是 777。
不要因为权限调不对就一刀切chmod 777。这是很多新手最爱犯的错,也是最容易被安全扫描盯上的问题。正确思路是先想清楚谁能用、谁不该用,再用属主、属组、其他人三组权限去拟合。
4. 新建用户后最容易翻车的四个场景与排查链路
4.1 “用户拒绝访问内存文件权限”这类报错到底在说什么
热搜里有一条“用户拒绝访问内存文件权限怎么办”,听着很玄,其实大多数时候就是普通的 Permission denied,只是文件恰好挂在内存文件系统上。
有次我帮一个同学排查,他的程序报错“Permission denied”指向 /dev/shm 下的一个文件。第一反应不是去改权限,而是先看挂载情况:
df -h /dev/shm ls -ld /dev/shm结果发现 /dev/shm 挂载的是 tmpfs,权限是 777,但里面有一个子目录是 root 建的,权限 700。普通用户当然进不去。原因是这个程序之前是用 root 跑的,第一次运行时创建了这个目录,后来换普通用户跑就全程报错。
处理方式也很简单:
sudo chown -R testdev:testdev /dev/shm/xxx这里要提醒的是:内存文件系统里的东西重启就没了,所以在里面建数据属于临时行为,不适合放需要持久化的内容。遇到“拒绝访问”,先别急着 chmod,按这个链路排查:
ls -ld看目标路径权限和属主属组;id看当前用户的 UID、GID 和附加组;getfacl看有没有更细粒度的 ACL 规则;mount看文件系统挂载选项有没有 noexec、nosuid 限制。
4.2 切换用户后提示符变成 -bash-4.2$ 的定位过程
很多人在新建用户后,用su - testdev切换过去,发现提示符不是预期的 root@hostname,而是难看的-bash-4.2$。这看起来像个小问题,背后其实是家目录环境初始化没搞好。
第一次遇到时,我花了不少时间。定位过程是这样的:
- 先查家目录是否存在:
ls -ld /home/testdev,结果目录在。 - 再查属主:
ls -ld /home/testdev显示 root root,问题找到了——用户不是家目录属主。 - 顺手查了 .bashrc、.bash_profile 是否存在,结果也没拷进去。
修复分两步:
sudo chown testdev:testdev /home/testdev sudo cp /etc/skel/.bashrc /etc/skel/.bash_profile /home/testdev/手动创建的 useradd 用户默认不会自动复制 /etc/skel 里的骨架文件,因为这个目录里放着 .bashrc、.profile 等初始化脚本。没有它们,bash 就退回到一个极简的 PS1 配置,于是出现-bash-4.2$。
正确姿势其实是创建时就用上-m参数,或者直接用 adduser,这些骨架文件会自动拷贝。这个小案例再次说明:useradd 是给脚本和高手用的,新手老老实实用 adduser 能省掉大量排查时间。
4.3 删除用户后 UID 被复用导致权限残留
这条是真实发生过的事故,影响面很大,值得单独说。某台测试服务器上,管理员删除了一个离职员工的账号,但没删干净,家目录还留着。过了两个月,一个新同事入职,系统分配了一个跟旧账号相同的 UID,新用户登录后,直接就能读旧同事留下的所有项目文件。
这个事故的根因是:Linux 判断文件属主只看 UID,不看用户名。旧账号 1003 留下的文件,遇到新分配 UID 1003 的用户,通通认主。
所以在删除用户之前,哪怕用了userdel -r,我也建议额外执行一次无主文件扫描:
sudo find / -xdev -nouser 2>/dev/null-nouser会找出所有在 /etc/passwd 中没有对应 UID 的文件。一旦发现,要么确认无用后删除,要么手动 chown 给某个现存用户。服务器上线时间越长,历史账号越多,这一步越不能省。
4.4 用户能登录却无法使用 sudo
新用户创建得很顺利,登录也正常,一执行 sudo 就提示:
testdev is not in the sudoers file. This incident will be reported.这其实是 Linux 的安全设计在起作用。Ubuntu 系的默认行为是:只有 sudo 组成员可以提权;而 Red Hat 系是 wheel 组。你新建的用户默认不在任何特权组里,自然不能用 sudo。
处理方式:
sudo usermod -aG sudo testdev或者用 visudo 在 /etc/sudoers 里单独给用户加规则。还有一个容易踩的坑是,你把自己手动创建的用户加进了 sudo 组,却发现还是不行——因为当前登录会话的附加组缓存没有刷新,必须重新登录才生效。遇到“加了组还是不行”,先退出登录再试试。
5. 在真实环境中管好用户:批量操作与生命周期
5.1 批量创建用户的三种思路
单机两三台,手动 useradd 没问题。一旦到了几十台甚至上百台,逐台敲命令就是灾难。我常用的批量思路有三种,按场景取舍:
第一种,for循环配合 useradd,适合一次性交付多台已知名单的机器:
for user in alice bob carol; do sudo useradd -m -s /bin/bash "$user" echo "$user:临时密码123" | sudo chpasswd sudo chage -d 0 "$user" # 强制首次登录改密码 done第二种,用newusers从文件批量导入,适合从表格导出的用户清单。文件格式跟 /etc/passwd 类似:
alice:x:2001:2001::/home/alice:/bin/bash bob:x:2002:2002::/home/bob:/bin/bash执行:
sudo newusers /tmp/userlist.txt第三种,在装机阶段就把用户固化到镜像或配置管理工具里。现在很多团队用自动化工具统一下发,用户管理就变成了配置文件变更,顺带解决了多机器一致性问题。这种方式最推荐,但学习成本也相对高。
5.2 用户生命周期:入职、变更、离职
管理用户不能只在创建的时候上心,我更愿意把它看成一个生命周期:
- 入职:创建账号、设置密码策略、加入必需的附加组、确认家目录骨架文件齐全。如果公司有共享目录,记得把用户的附加组配好,不然后面到处报权限错。
- 变更:员工转岗,权限跟着变。用
usermod -G重设附加组,必要的话调整主组,同时检查他还有没有能访问旧项目目录的残留权限。 - 离职:我习惯先锁定账号而不是急着删除,因为数据审计可能随时需要查看历史归属。
sudo usermod -L alice sudo passwd -e alice # 让密码立即过期 sudo usermod -s /usr/sbin/nologin alice锁定之后观察一段时间,确认没有影响和隐患,再把账号和家目录一并归档。这比直接删除要稳妥得多,能避免前面说的 UID 复用问题。
另外说一句安全习惯:普通用户不要随便给 root 权限。很多安全隐患都是因为“反正他需要装软件,干脆给个 sudo”造成的。正确的做法是明确他要执行哪些命令,在 sudoers 里限制到具体命令,宁可多配几条规则,也别敞开大门。
我在实际运维中的另一个习惯是定期检查系统里有没有 UID 为 0 的隐藏账号,以及长期未登录却有 sudo 权限的账号。这个操作很快,但能避免很多麻烦:
# 找出所有 UID 为 0 的用户 sudo awk -F: '$3==0{print $1}' /etc/passwd # 找出最近 30 天未登录的用户 sudo lastlog -b 30做完这些,再结合备份好的 /etc/passwd、/etc/shadow,可以避免绝大多数账号管理事故。Linux 的用户体系初看是一堆命令,深看是一套权限哲学——账号可以删可以加,但权限边界才是真正需要你时刻守住的东西。