用户与 sudo
用户组、sudoers、最小权限。
用户与 sudo
这一课你将能做到
- 读懂
/etc/passwd 一行,用 id、groups、whoami 说明当前用户是谁、属于哪些组、UID 是多少
- 创建
deploy 专用部署用户并加入 sudo 组(Ubuntu)/ wheel 组(RHEL),验证其 sudo 权限
- 说出「为什么服务要用专用低权限用户跑」,并把 CodeRoadMap 的部署目录属主对齐到运行用户
为什么要学
Linux 是多用户系统,权限体系(文件课)全部围绕「用户和组」展开。生产事故的两大源头——误操作(root 手滑 rm -rf 无可挽回)和权限混乱(服务全用 root 跑,一旦被入侵即整机沦陷)——根治手段都是同一件事:给每个身份最小权限。
面试里 su 与 sudo 的区别、/etc/passwd 七段含义、「为什么不用 root 跑服务」是高频三连;实战里你每天 SSH 登录用哪个用户、服务以谁的身份运行、谁能 sudo,决定这台机器的安全水位。本课把身份这件事一次讲透。
先建立图像
用户在系统里的样子
用户(UID) 组(GID) 身份三问
──────────────────────────────────────────────────────
root UID=0 拥有至高权限 我是谁? whoami / id
系统用户 UID=1-999 服务专用(www/mysql) 我在哪些组?groups / id -Gn
普通用户 UID=1000+ 人用的(deploy) 我能干什么?sudo -l / 权限位
每个文件都挂着「属主+属组」,每个进程都带着「uid+gid」——上一课 ps aux 第一列的那个名字,就是本课的主角。
身份相关的四个关键文件
/etc/passwd 用户名册(人人可读) deploy:x:1001:1001::/home/deploy:/bin/bash
/etc/shadow 密码哈希(仅 root) deploy:$6$xxxx:19xxx:0:99999:7:::
/etc/group 组名册 sudo:x:27:deploy
/etc/sudoers sudo 白名单 只用 visudo 编辑,语法错了会锁死 root
/etc/passwd 七段读法:
deploy : x : 1001 : 1001 : : /home/deploy : /bin/bash
│ │ │ │ │ │ └ 登录 shell(/usr/sbin/nologin=禁止登录的系统用户)
│ │ │ │ │ └ 家目录
│ │ │ │ └ 备注字段(GECOS,常空)
│ │ │ └ 主组 GID
│ │ └ UID
│ └ 密码占位符(真哈希在 shadow)
└ 用户名
跟着做
步骤 1:搞清「我现在是谁」
whoami # 用户名
id # uid=1001(deploy) gid=1001(deploy) groups=1001(deploy),27(sudo)
groups # 只是组名列表
who # 当前登录在机器上的都有谁(pts/0 终端,几点连的)
sudo -l # 我能用 sudo 干什么(列出白名单;普通用户先输自己密码)
id 是最信息量大的一个命令:一条输出回答「我是谁+我的组」两问。
步骤 2:读 /etc/passwd,认识系统用户
cat /etc/passwd | head -5 # 前几个都是系统用户(root、daemon、bin…)
grep -E "^(root|www|mysql|deploy)" /etc/passwd # 看关键用户的 UID 和 shell
awk -F: '$3 >= 1000 {print $1, $3}' /etc/passwd # 只列普通用户(UID>=1000)
注意观察:mysql、www 这类系统用户的 shell 是 /usr/sbin/nologin——它们存在,但永远不该有人以它们登录,这正是「服务专用用户」的形态。
步骤 3:创建 deploy 部署用户(Ubuntu 路径)
# adduser 交互式(推荐,自动建家目录/设密码/配 shell):
sudo adduser deploy
# 按提示输两次密码,其他信息一路回车
# 验证:
id deploy
ls -ld /home/deploy # 家目录已建,属主正确
# 加入 sudo 组(Ubuntu 的 sudo 白名单组):
sudo usermod -aG sudo deploy
id deploy # groups 里多出 27(sudo)
# 验证 sudo 生效:切到 deploy 试一次
su - deploy # 输 deploy 的密码;- 表示加载完整登录环境
sudo whoami # 输 deploy 密码 → root,说明 sudo 通了
exit # 回到原用户
步骤 4:RHEL/CentOS 差异(会认即可)
# RedHat 系的对应操作(在有 sudo 的 Rocky/CentOS 上):
# useradd deploy && passwd deploy # useradd 不交互,passwd 单独设密码
# usermod -aG wheel deploy # RHEL 的管理员组叫 wheel,不是 sudo
# groups deploy # 确认 wheel 已在
| 事项 | Ubuntu/Debian | RHEL/Rocky/CentOS |
|---|
| 管理员组 | sudo | wheel |
| 建用户 | adduser(友好) | useradd + passwd |
| 默认用户 | Ubuntu 初始用户 | RHEL 默认只有 root |
步骤 5:su 与 sudo 的对比实验
# 场景 A:su —— 变成另一个人(要「对方」的密码)
sudo -i # root 身份的登录 shell(输入的是【当前用户】的 sudo 密码)
whoami # root
exit
su deploy # 变成 deploy(输入的是【deploy】的密码)
whoami
exit
# 场景 B:sudo —— 以别人身份执行一条命令(要「自己」的密码,有审计记录)
sudo whoami # root,但你的身份没变
sudo -u www touch /tmp/t1 # 以 www 身份建文件(文件课用过)
ls -l /tmp/t1 # 属主是 www
核心区别一句话:su 是「换人」(整段会话变身),sudo 是「借权」(单条命令借力)。sudo 的每次使用都记进日志(sudo grep sudo /var/log/auth.log),这就是审计能力——生产优先 sudo。
步骤 6:给 CodeRoadMap 落地「身份模型」
生产标准:人用 deploy 登录操作,服务用 www(或同名专用用户)运行,两者通过组协作。
# 假设服务运行用户是 www(宝塔默认),部署操作用户是 deploy
sudo mkdir -p /www/wwwroot/coderoadmap/{logs,conf}
sudo useradd -r -s /usr/sbin/nologin www 2>/dev/null || true # -r 系统用户,禁止登录
# 目录协作:www 拥有并读写,deploy 加入 www 组以便维护
sudo chown -R www:www /www/wwwroot/coderoadmap
sudo chmod -R 750 /www/wwwroot/coderoadmap
sudo usermod -aG www deploy # deploy 进 www 组
# 验证矩阵(文件课的 sudo -u 验证法):
sudo -u www touch /www/wwwroot/coderoadmap/logs/ok.log # www 能写日志 → 服务可运行
sudo -u deploy touch /www/wwwroot/coderoadmap/logs/d.log # deploy 经组权限也能写
ls -l /www/wwwroot/coderoadmap/logs/
以后 SSH 课会把 root 的密码登录关掉,所有人只走 deploy + 密钥——身份模型就闭环了。
步骤 7:sudo 白名单精细化(进阶,生产常用)
默认 sudo 组=「什么都能干」。若只想让 deploy 重启服务,不给他全部 root:
sudo visudo # 永远用 visudo(保存前做语法检查,防锁死)
在末尾加一行(语法:用户 主机=(以谁) 可执行命令):
deploy ALL=(root) NOPASSWD: /bin/systemctl restart coderoadmap-backend, /bin/systemctl status coderoadmap-backend
su - deploy
sudo -l # 能看到这一条 NOPASSWD
sudo -n systemctl restart coderoadmap-backend # -n 免交互,CI 里靠它自动执行
exit
这正是 GitHub Actions 自动部署重启服务的权限基础(github-actions 轨道会用到)。
讲清楚要点
- UID 是内核真正认的身份,用户名只是给人看的标签。UID 0 = root(可有多个名字但权力相同);1-999 系统用户;1000+ 普通用户。进程、文件权限判断全走 UID。
- su 借「对方」的密码,sudo 用「自己」的密码:su 适合长时间切换身份工作;sudo 适合单条命令借权,且天然带审计(谁、何时、执行了什么)。安全默认:人手 sudo,不用裸 root。
- 组是权限的「批量通道」:把人加进组(
usermod -aG)比逐个改文件属主优雅得多。-aG 的 -a 不能忘——漏了会把用户踢出其他所有组(经典事故:把自己踢出 sudo 组)。
- 服务专用用户三要素:
useradd -r(系统用户)+ nologin shell(不能登录)+ 目录属主对齐(文件课)。Nginx 的 worker、Java 服务、MySQL 各用各的用户,互不越界。
- visudo 是唯一安全的 sudoers 编辑方式:直接 vim 改
/etc/sudoers,一个语法错就能让所有人 sudo 失败(锁在系统外)。visudo 保存前会语法检查。
- su - 与 su 的区别:
su - user 加载对方的完整环境(PATH、家目录),su user 保留当前环境变量——后者常导致「明明装了 java 却 command not found」,因为 PATH 没换。
你可能会懵的地方
- sudo -i、sudo -s、sudo su 有什么区别?
sudo -i:模拟 root 重新登录,环境干净(推荐);sudo -s:root shell 但保留当前环境;sudo su -:效果近似 -i,绕了一层。日常记 sudo -i 即可,退出用 exit。
- 为什么我 sudo 时提示 xxx is not in the sudoers file?
你的用户不在 sudoers(不在 sudo/wheel 组,也无白名单条目)。补救只能由 root 或另一个管理员执行:
usermod -aG sudo 你的用户名。所以把自己踢出 sudo 组前,务必留着 root SSH 通道。
- useradd 和 adduser 用哪个?
Debian/Ubuntu:adduser 是友好封装(交互、自动建目录),useradd 是底层命令(需手动传参);RHEL 系没有 adduser 区别(adduser 是 useradd 的软链)。记 Ubuntu 用 adduser、RHEL 用 useradd+passwd。
- 服务用 www 跑,那我 deploy 还要 sudo 干什么?
部署动作大多需要 root 权限:systemctl restart、写 /etc/nginx、装包。规范是 deploy 有「精确白名单」(只许 restart 本服务、reload nginx),既自动化又最小权限。
- 密码策略/锁定用户怎么做?
锁定:
sudo usermod -L user(或 passwd -l),解锁 -U;禁用登录但不删:shell 改 /usr/sbin/nologin;删除:userdel -r user(-r 连家目录)。离职当天:锁定 + 踢出会话(pkill -u)+ 收回组与 sudoers。
错法对照
| 错法 | 问题 | 正解 |
|---|
| 一切操作都 root(SSH 用 root、服务用 root) | 误操作无防线,入侵即沦陷 | 人用 deploy+sudo,服务用 www/nologin |
usermod -G www deploy(漏 -a) | deploy 被踢出 sudo 等原有组 | -aG 追加;操作前 id deploy 留底 |
| 直接 vim /etc/sudoers | 语法错=全员锁死 | visudo(自带语法检查) |
su deploy 后找不到 java/环境不对 | 没加 -,环境还是上一个用户的 | su - deploy 完整登录环境 |
| 服务运行用户建成了可登录普通用户 | 多一个可 SSH 的账号面 | useradd -r -s /usr/sbin/nologin |
| 把 deploy 直接加进 www 组改文件属主 | 组权限混乱,越积越难理 | 目录属主归服务用户,人经 sudo/白名单操作 |
卡点急救
- 误把自己踢出 sudo 组(漏 -a):只要还能用 root SSH/控制台 VNC 登录,
usermod -aG sudo deploy 加回来;彻底锁死时用云厂商的「VNC/救援模式」以 root 进入修复——所以任何 usermod 前先 id 用户 截图留底。
- sudo 提示不在 sudoers 且没有 root 通道:云服务器走控制台 VNC 以 root 登录修复;本地 VM 用单用户模式(GRUB 编辑启动参数加
init=/bin/bash)。这是不重视「留 root 后门」的代价。
- 新建用户登录后只有
$、补全失效、乱码:家目录配置缺失(建用户时用了 useradd 没建骨架)。sudo cp /etc/skel/.bashrc /etc/skel/.profile /home/deploy/ && sudo chown deploy:deploy /home/deploy/.* 修复。
- NOPASSWD 不生效:
/etc/sudoers.d/ 里可能有后面的文件覆盖了规则(sudoers 后写的生效);用 sudo visudo -f /etc/sudoers.d/xxx 检查;确认路径是绝对路径(/bin/systemctl 还是 /usr/bin/systemctl,which systemctl 确认)。
- deploy 用户重启服务报权限错:白名单命令路径与实际执行不一致(如带了参数)。
sudo -l 看自己被允许的精确形式,命令照抄白名单;需要通配时用 ALL=(root) NOPASSWD: /bin/systemctl restart coderoadmap* 谨慎放开。
面试常问
-
su 和 sudo 的区别?生产环境推荐哪个?
su 切换为另一个用户(需对方密码),整个会话都是对方身份;sudo 以指定身份执行单条命令(输自己的密码),权限可白名单细化、操作全审计(auth.log)。生产推荐 sudo:最小权限 + 审计 + 不必共享 root 密码。root 密码甚至可以不设(Ubuntu 默认)。
-
/etc/passwd 每个字段是什么?为什么密码在 /etc/shadow?
七段:用户名:x:UID:GID:备注:家目录:shell。密码哈希放 shadow 是安全分层:passwd 需人人可读(很多程序查用户名→UID),哈希不能公开,所以拆到仅 root 可读的 shadow,用 x 占位。影子文件还带过期策略字段。
-
为什么生产服务(Nginx/Java/MySQL)不用 root 跑?
最小权限原则:服务进程若被漏洞攻破(如反序列化 RCE),攻击者继承的是运行用户的能力——www 用户写不了 /etc、读不了其他用户数据,爆炸半径可控;root 进程被攻破等于整机沦陷。配套做法:专用系统用户 + nologin + 目录属主对齐。
-
如何让一个用户只能重启某个服务而不能做别的?
sudoers 白名单:deploy ALL=(root) NOPASSWD: /bin/systemctl restart app.service(visudo 编辑,或放 /etc/sudoers.d/)。验证 sudo -l。CI 自动部署的重启权限就是这么开的。
-
usermod -aG 和 -G 的区别?
-G 是「设置」附加组列表,会把用户之前所有附加组覆盖掉;-aG 是「追加」。实践一律用 -aG,除非刻意重置。误用 -G 踢掉自己所在 sudo 组是经典运维事故。
-
Linux 用户 UID 有什么约定?
0=root(特权);1-999 为系统用户(服务专用,通常 nologin、不设密码);1000+ 为可登录普通用户。判断进程/文件身份看 UID 而非名字;多机同步时 UID 冲突会导致权限错乱(NFS 场景尤其注意)。
自测三问
id 与 whoami 输出的信息差在哪?groups deploy 能回答什么问题?
- 写出「创建禁止登录的系统用户 www、让其拥有 /www/wwwroot/coderoadmap 全部权限」的命令序列。
- deploy 用户被要求「只能重启 coderoadmap-backend、不能干别的」,写出 sudoers 条目并说明验证方法。
小结一句话
身份是权限的起点:人用普通用户 + sudo 白名单操作,服务用 nologin 系统用户运行,组做批量授权——「最小权限 + 可审计」六个字就是生产身份模型的全部。
延伸