1. 用户管理是Linux系统安全的第一道防线
1.1 一台服务器三条命令引发的连锁事故
有次给一家小公司做运维巡检,发现他们的生产服务器上所有业务都用root账号跑着,数据库、Web服务、定时任务脚本,清一色root。问负责人为什么这样搞,他说"方便,省得权限出问题"。我当时就劝他赶紧把用户权限拆分,但对方没太当回事。结果三个月后,一个刚入职的实习生想手动清理日志文件,一条 rm -rf /var/log/ 因为路径写错,差点把整个系统文件都删了,还好当时Web服务进程占用着部分文件才没崩彻底,但日志全没了,排障的时候连报错记录都查不到。
这件事特别典型。Linux的用户管理看似基础,却是整台机器安全模型的地基。谁可以登录、谁能执行什么命令、谁能读写哪些文件,全部由用户和权限体系决定。如果这层没做好,后续什么防火墙、安全组、入侵检测都是白搭,因为攻击者只要拿到一个高权限账号,就能绕过大部分外围防护。
1.2 用户、组、权限三者如何映射到文件系统
很多初学者容易把用户管理理解成"建账号、设密码"这么简单,但实际上它背后是一整套围绕文件系统设计的权限逻辑。Linux里每个文件都有三个维度的归属:属主(owner)、属组(group)、其他用户(others)。每个维度又分别有读(r=4)、写(w=2)、执行(x=1)三种权限,数字表示法就是这三者相加的结果。
举个例子,你执行 ls -l 看到类似 -rw-r--r-- 1 nginx nginx 2048 Mar 15 10:32 config.conf,其中第一个 nginx 是该文件的属主用户,第二个 nginx 是属组,权限位 rw-r--r-- 表示属主可读写、属组成员可读、其他用户只读。
这套机制决定了用户管理从来不是孤立的。你建一个用户,实际上是在为"谁能以什么身份访问哪些资源"划边界。建用户只是第一步,接下来还要考虑他属于哪些组、能访问哪些目录、能不能用sudo、需不需要配置SSH密钥登录。这些动作组合起来,才算完成了一个用户的完整生命周期管理。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从创建到删除:用户管理命令的实操细节
2.1 useradd和adduser到底用哪个
Linux里新建用户有两个常见的命令入口:useradd 和 adduser。很多新手会以为它们是一样的,其实区别挺大。
useradd 是系统原生的底层命令,参数多、功能细,但不会自动帮你创建家目录、设置密码、生成邮件文件,属于"半成品"操作。比如你直接执行:
bash复制useradd zhangsan
系统只会创建用户,不会创建 /home/zhangsan 目录,也不会让你设置密码,你还得手动执行 passwd zhangsan 才能设置登录密码。
adduser 在Debian/Ubuntu系发行版中是一个更友好封装脚本,它会交互式地引导你完成设置密码、填写用户信息等步骤,并且自动创建家目录。但CentOS/RHEL系的 adduser 其实只是 useradd 的符号链接,并没有那些交互能力。
我的建议是:脚本自动化场景用 useradd,手动维护单台服务器用 adduser。但无论用哪个,创建用户时一定要把需要的参数一次性想清楚,避免后期反复修改。
以 useradd 为例,我最常用的参数组合是:
bash复制useradd -m -d /home/zhangsan -s /bin/bash -G wheel zhangsan
参数含义如下:
-m:自动创建家目录-d:指定家目录路径-s:指定登录Shell-G:指定附加组,多个组用逗号分隔-u:指定UID,适合需要固定UID做数据迁移的场景-e:指定账号过期日期,格式YYYY-MM-DD,适合临时账号
如果创建过程中漏了某些参数,不用删掉重建,用 usermod 补就行。
2.2 密码策略与chage命令的搭配使用
用户创建完,第一件事就是设置密码。但"设置密码"这个操作里有很多细节:
bash复制passwd zhangsan
这个命令会交互式地让你输入两次新密码。如果你需要非交互式设置,可以用:
bash复制echo "NewPassword123" | passwd --stdin zhangsan
--stdin 参数在CentOS/RHEL系中可用,Ubuntu系部分版本不支持,那种情况下可以用 chpasswd:
bash复制echo "zhangsan:NewPassword123" | chpasswd
但更关键的问题是密码策略。很多公司吃过弱口令的亏,服务器被暴力破解后成为肉鸡。Linux自带的 chage 命令就是用来管理密码生命周期的,它能控制密码多久必须改一次、过期后多久锁定账号等:
bash复制chage -M 90 -m 7 -W 7 zhangsan
含义分别是:密码最长使用90天、最短使用7天(防止改了又立刻改回去)、过期前7天开始警告。
查看用户当前的密码过期信息:
bash复制chage -l zhangsan
注意:如果查看结果里
Password expires显示为never,说明该用户密码永不过期。除非是像nologin这类服务账号,否则普通用户密码永不过期会带来安全隐患。
2.3 usermod修改用户属性的典型场景
用户信息不是建好就一劳永逸了。员工转岗、项目组调整、权限变更,都会需要修改用户属性。usermod 就是干这个的。
最常见的几个场景:
bash复制# 把zhangsan加入docker组,让他能执行docker命令
usermod -aG docker zhangsan
# 修改用户登录Shell
usermod -s /sbin/nologin zhangsan
# 锁定用户,不允许登录
usermod -L zhangsan
# 解锁用户
usermod -U zhangsan
# 修改用户家目录
usermod -d /data/zhangsan -m zhangsan
注意 -aG 里的 -a(append)非常重要。如果不加 -a,-G 会直接覆盖用户原来的附加组列表,导致用户之前所属的组全部被移除,可能瞬间丢失一堆权限。我见过不止一次有人因为这个操作把用户踢出了 wheel 组,管理员自己都没法sudo了。
2.4 删除用户时四个容易残留的痕迹
删除用户同样有讲究。直接跑 userdel zhangsan 只会删除用户本身,但很多东西会残留下来:
- 家目录残留:
/home/zhangsan还完整保留着。如果想一并删除,要加-r参数:userdel -r zhangsan - 邮件池文件残留:默认位于
/var/mail/zhangsan,不清理会占用空间 - UID/GID残留文件:如果用户曾经创建过大量文件,删除用户后这些文件会显示为一个数字UID,归属变得模糊。建议删除前先
find / -user zhangsan搜一遍,确认没有需要保留的文件,或提前chown给其他用户 - 定时任务残留:用户配置在
/var/spool/cron/下的crontab不会自动删除,需要手动清理
如果只是暂时不用某个账号,我更建议用 usermod -L 锁定而不是直接 userdel。因为一旦删除,重新创建时UID会变化,文件归属关系会断裂,排查起来很折腾。
3. 组管理:多用户协作场景下的权限收口方案
3.1 groupadd和gpasswd的完整用法
用户管理离不开组。组的作用是把一批用户归拢到一起,给这批人统一授权,而不是一个一个地给用户发权限。
创建组的命令很简单:
bash复制groupadd devops
也可以指定GID:
bash复制groupadd -g 1500 devops
组的增删改查对应命令分别是 groupadd、groupdel、groupmod 和 getent group 或直接查看 /etc/group 文件。
管理组成员时我推荐用 gpasswd:
bash复制# 把zhangsan加入devops组
gpasswd -a zhangsan devops
# 把zhangsan从devops组移除
gpasswd -d zhangsan devops
# 设置组的管理员,由组管理员自行管理成员
gpasswd -A lisi devops
查看一个用户属于哪些组:
bash复制id zhangsan
groups zhangsan
id 命令的输出会同时显示UID、GID和附加组列表,排查权限问题时非常有用。
3.2 主组和附加组:差别在文件创建时就能看出来
新建用户时会自动创建一个同名的组,这个组就是该用户的主组(Primary Group)。用户在创建新文件时,文件的属组默认就是用户的主组,这就是为什么新建用户的文件通常显示为 zhangsan:zhangsan。
附加组(Supplementary Groups)则是用户额外加入的其他组。用户对附加组目录的访问权限,取决于该目录对组开放了什么权限。
举个实际场景:项目组有 /data/project 目录,属组是 devops,权限是 rwxrwx---(属主和组成员可读写执行,其他用户无权限)。如果希望 zhangsan 能访问这个目录,只需要把他加入 devops 组,不需要修改目录权限,也不需要给他其他任何组权限。这就是附加组的意义所在——通过组成员关系灵活控制访问范围。
但有个坑要特别提醒:如果用户的主组不对,他新建的文件可能会无法被同组成员修改。比如用户主组是 zhangsan,即使他在 devops 组里,他在 /data/project 下新建的文件属组仍然是 zhangsan,devops 组其他成员默认没有写权限。解决办法有两种:
- 把该目录设置SetGID权限位,让新文件自动继承目录的属组:
chmod g+s /data/project - 用
usermod -g devops zhangsan把用户的主组改成devops
第一种方法更通用,不会影响用户在其他场景下的主组关系。
3.3 实际项目中的分组策略参考
以一台跑着Web应用和数据库的服务器为例,我通常这样设计分组:
| 组名 | 成员 | 目的 |
|---|---|---|
| webadmin | 前端和后端开发 | 共享代码目录读写权限 |
| dbadmin | DBA和运维 | 数据库配置和备份目录访问 |
| devops | 运维和CI/CD机器人 | 部署脚本、日志目录访问 |
| auditor | 安全审计人员 | 只读权限审查日志 |
分组的核心目的是最小化权限。用户能进入 devops 组不代表能进 dbadmin 组,数据库配置文件依然对非相关人员不可见。这与"一个用户拥有很多权限"的思路正好相反——组管理强调的是按业务角色收口权限,而不是给个人开绿灯。
4. SSH密钥登录与sudo权限:远程管理的两个关键配置
4.1 免密登录的配置方法与常见错误
密码登录虽然简单,但在生产环境里并不推荐,尤其是暴露在公网上的服务器。正确的做法是配置SSH密钥登录。
单用户配置其实很直接:
bash复制ssh-keygen -t ed25519 -C "zhangsan@workstation"
ssh-copy-id zhangsan@server-ip
如果服务器不允许密码登录,需要手动把公钥放到目标用户的家目录:
bash复制mkdir -p /home/zhangsan/.ssh
echo "ssh-ed25519 AAAA... zhangsan@workstation" >> /home/zhangsan/.ssh/authorized_keys
chmod 700 /home/zhangsan/.ssh
chmod 600 /home/zhangsan/.ssh/authorized_keys
chown -R zhangsan:zhangsan /home/zhangsan/.ssh
这里权限设置非常关键:.ssh 目录必须是700,authorized_keys 文件必须是600。如果权限过宽,SSH服务端出于安全考虑会直接忽略这个文件,表现为"密钥看起来配置了但就是登录不上"。
另外还要注意家目录本身的权限。如果用户家目录是 777 或者属主不是该用户,SSH同样会拒绝使用密钥认证。这类问题排查时可以看 /var/log/secure(CentOS系)或 /var/log/auth.log(Ubuntu系),里面会有明确提示。
4.2 sudoers的正确编辑姿势
超级用户权限不是谁都能给的,但完全不给又不现实。sudo 就是用来解决"临时授权"问题的。
在配置sudo规则时,切忌直接用 vim /etc/sudoers 改文件,因为语法错误可能直接让你失去sudo能力。正确姿势是:
bash复制visudo
visudo 会在保存前检查语法,如果写错了会提示你并阻止保存,算是最后一道保险。
常用的配置规则:
bash复制# 允许zhangsan执行所有命令
zhangsan ALL=(ALL) ALL
# 允许devops组执行所有命令
%devops ALL=(ALL) ALL
# 允许zhangsan不用密码执行systemctl命令
zhangsan ALL=(ALL) NOPASSWD: /usr/bin/systemctl
# 允许zhangsan以www-data用户身份执行php相关命令
zhangsan ALL=(www-data) /usr/bin/php
需要注意:%devops 中的百分号代表组,不带百分号的是用户名。NOPASSWD只在指定的命令范围内生效,如果后面对该用户还有带密码的规则,行为会叠加,建议同一用户的规则写在一起,避免逻辑混乱。
4.3 通过sudo限制用户可执行的命令
有时候需求很明确:这个用户只用重启某个服务,其他操作一律不允许。这时候可以不给他完全sudo权限,而是只放行特定命令。
比如让 zhangsan 只能重启Nginx:
bash复制zhangsan ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx
但这里有个容易被忽略的问题:systemctl 命令本身能做的事很多,直接放行 /usr/bin/systemctl 意味着他可以执行 systemctl stop nginx、systemctl start docker,甚至通过 systemctl edit 修改服务配置。更安全的做法是放行特定的systemctl子命令:
bash复制zhangsan ALL=(root) NOPASSWD: /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx
sudo规则配置本来就是"最小授权"的艺术。我见过很多团队图省事直接给 ALL=(ALL) ALL,出了一次事故后才缩权限。与其事后补救,不如一开始就按命令粒度去设计。
5. 用户管理实战:批量操作与脚本自动化
5.1 批量创建用户的两种方式
服务器规模上来之后,一个个 useradd 显然不现实。批量创建用户通常有两种思路。
第一种是循环脚本:
bash复制for user in zhangsan lisi wangwu; do
useradd -m -s /bin/bash "$user"
echo "${user}:Init@123456" | chpasswd
chage -M 90 "$user"
done
第二种是读CSV文件批量处理:
bash复制#!/bin/bash
# userlist.csv 格式:username,group,shell
while IFS=',' read -r username group shell; do
if ! id "$username" &>/dev/null; then
useradd -m -s "$shell" "$username"
if [ -n "$group" ]; then
usermod -aG "$group" "$username"
fi
fi
done < userlist.csv
批量操作时一定要加 id "$username" 判断,防止重复创建报错后脚本中断。
删除用户的批量操作同理,但建议先导出用户列表一份做备份,防止误删后无法恢复。
5.2 锁定与解锁用户的几种方式
锁定用户不让他登录,有几种不同层次的方法:
usermod -L:锁定用户密码,本质是在/etc/shadow的密码字段前加感叹号,用户无法用密码登录,但SSH密钥登录仍然有效passwd -l zhangsan:效果与usermod -L类似usermod -s /sbin/nologin:把登录Shell改成nologin,用户即使认证通过也无法获得交互式Shellchage -E 0 zhangsan:让账号立即过期,本质上账号被禁用
四种方式的适用场景不同。如果是员工离职但业务数据还需要他名下的文件,我一般用 usermod -L 锁定密码,保留Shell和家目录;如果只是为了临时停用某个账号,用 chage -E 0 或 usermod -L 都行;如果是要彻底禁止交互式登录但允许服务运行(比如数据库账号),就改成 nologin。
5.3 用户配置文件在哪里:/etc/passwd、/etc/shadow、/etc/group
用户管理的底层其实是对几个文本文件的读写。虽然平时用命令操作,但理解这些文件结构对排查问题帮助极大。
/etc/passwd 保存用户基本信息,每行格式为:
code复制用户名:密码占位符:UID:GID:描述信息:家目录:登录Shell
例如:
code复制zhangsan:x:1001:1001::/home/zhangsan:/bin/bash
密码字段显示 x 表示密码存放在 /etc/shadow 中,这是现代Linux的标准做法,防止普通用户读到密码哈希。
/etc/shadow 保存加密后的密码和密码策略信息,只有root可读。格式为:
code复制用户名:密码哈希:最近修改日期:最小修改间隔:最大有效期:过期前警告天数:宽限期:账号过期日期:保留字段
/etc/group 保存组信息,格式为:
code复制组名:组密码占位符:GID:组成员列表
排查用户无法登录、密码过期、组权限异常等问题时,直接查看这三个文件往往比到处敲命令更直观。
6. 用户管理中的高频踩坑:排查链路与修复方案
6.1 用户创建后无法登录,卡在密码验证这一环
一个很常见的现象:用 useradd 建完用户,也设置了密码,但SSH登录时一直提示密码错误。排查链路应该是:
第一步,确认密码是否真的设置成功:
bash复制getent shadow zhangsan
看第二个字段有没有值,如果只有一个感叹号或星号,说明密码未设置或被锁定。
第二步,确认用户没有过期或被禁用:
bash复制chage -l zhangsan
第三步,看SSH服务端日志:
bash复制tail -f /var/log/secure
日志里如果出现 User zhangsan not allowed because account is locked,说明用户被锁定了。如果出现 Permission denied (publickey,password),说明认证方式有问题,需要检查 /etc/ssh/sshd_config 里的 PasswordAuthentication 是否开启,以及 AllowUsers / DenyUsers 配置有没有把用户排除掉。
很多情况下问题不是出在建用户环节,而是SSH配置层面限制了用户登录。
6.2 sudo权限不生效:排查点比想象中多
用户明明在 wheel 或 sudo 组里,但执行sudo时提示不在sudoers文件中,这是我会员群里问过次数最多的问题。
排查步骤:
- 确认用户确实在组里:
id zhangsan - 确认组名和sudoers里的组名一致:
grep wheel /etc/sudoers或grep sudo /etc/sudoers - 确认修改组成员后用户重新登录过。这是最容易忽略的点:用户加入新组后,如果当前SSH会话是旧的,组信息不会自动刷新。必须退出重新登录,或者执行
newgrp切换到新组 - 确认sudoers文件语法没错:
visudo -c
第3点需要特别强调,因为在新手期它造成困惑的频率太高了。
6.3 用户目录权限混乱导致服务无法读写
有次部署应用,前端和后端共用一个 /data/www 目录,开发把代码上传后Nginx却报 Permission denied。最后查下来,问题出在一个很基础的环节:代码目录的属主是 www-data:www-data,但开发人员上传文件时用的是自己的账号,创建的文件属主是 dev:dev,Nginx进程以 www-data 身份运行,自然读不到 dev 用户的文件。
这类问题的通用解法:
bash复制# 把目录属主改成服务运行用户
chown -R www-data:www-data /data/www
# 或者把开发用户加入www-data组,并设置setgid
usermod -aG www-data dev
chown -R www-data:www-data /data/www
chmod -R g+w /data/www
chmod g+s /data/www
设置 g+s 后,目录下新建的文件会自动继承 www-data 组,新文件的组权限天然具备,不会再出现"文件属主不对导致服务读不到"的情况。这是我在多用户协作项目里的标准配置。
7. 用户管理常见隐患排查清单
定期检查用户管理层面的安全隐患,我通常按以下清单过一遍:
账号层面:
- 哪些用户有登录权限?
awk -F: '$7 != "/sbin/nologin" && $7 != "/bin/false" {print $1}' /etc/passwd - 有哪些用户空密码?
awk -F: '($2 == "") {print $1}' /etc/shadow - 有哪些用户密码永不过期?
chage -l逐人查看,或结合脚本批量检测 - 哪些用户属于特权组?
getent group wheel sudo docker逐一查看
安全层面:
-
SSH是否允许root直接登录?
grep PermitRootLogin /etc/ssh/sshd_config -
是否禁用了密码认证而只保留密钥认证?
grep PasswordAuthentication /etc/ssh/sshd_config -
所有登录用户是否都配置了密钥?没有密钥的用户如果不是服务账号,兜底也要设置强密码策略
残留层面:
- 离职员工的账号是否已锁定或删除?
- 是否存在长期不登录的僵尸账号?
lastlog -b 90可以列出最近90天未登录的用户
这类检查建议每个季度做一次,规模大的公司可以做成运维脚本定时跑,把异常结果直接推送给管理员。
8. 个人经验分享:用户管理做扎实的几个心得
8.1 给服务账号和人类账号做明确区分
服务账号(比如nginx、mysql、redis)和人类账号的诉求完全不同。服务账号不需要登录Shell,不需要家目录,也不应该拥有密码。创建服务账号时我推荐这样:
bash复制useradd -M -s /sbin/nologin -r nginx
-M 表示不创建家目录,-r 表示创建为系统账号(UID通常小于1000)。这样的账号即使被攻破,也无法获得交互式Shell,攻击面会小很多。
而人类账号要有家目录、有正常Shell、有合理的密码策略和SSH密钥。两类账号混为一谈,往往会造成权限管理混乱。
8.2 创建用户前先想好后路
公司的员工账号因离职、转岗、请假等原因,生命周期充满变化。我在创建任何一个账号前会先确认三件事:
- 这个账号的用途是什么,需要落到哪个组
- 这个账号的数据要存哪里,是否需要绑定固定UID
- 账号失效时间是什么时候,是否需要设置过期
虽然这些问题看起来很基础,但操作前多想一步,能免掉后面很多回收权限、迁移数据的精力。
8.3 用户管理日志要留痕
多管理员协作时,谁改过哪个用户的权限,一定要有据可查。可以配置bash history记录所有管理员执行过的用户管理命令,或者单独开一个审计日志目录,把每次用户变更操作写入记录文件。团队规模大了之后,这个习惯会救你很多次。
我在实际项目中见过多次由于管理员之间缺乏沟通导致的权限事故,基本都是"我以为你改了""我以为你没改"造成的。留痕不是为了追责,而是为了快速恢复现场。
