
经常有刚开始接触 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 里UID1001用户唯一数字 IDGID1001用户主组的数字 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因为它是非交互式的可以在自动化里稳定执行。这两个命令的对比可以看这张表维度useraddadduser类型底层二进制命令封装脚本交互性无参数驱动交互式向导自动创建家目录需要加 -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 gw /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切换过去发现提示符不是预期的 roothostname而是难看的-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: $30{print $1} /etc/passwd # 找出最近 30 天未登录的用户 sudo lastlog -b 30做完这些再结合备份好的 /etc/passwd、/etc/shadow可以避免绝大多数账号管理事故。Linux 的用户体系初看是一堆命令深看是一套权限哲学——账号可以删可以加但权限边界才是真正需要你时刻守住的东西。