Linux 实战(七):useradd、sudoers、umask、SGID、软链 —— 三台真机实测

这一篇讲什么

用户和权限的命令都很简单,坑在于同一条命令,三个发行版的默认行为不一样 ,以及改完了,但对已经在跑的东西不生效。本篇在三台机器上逐个复现,贴原始输出。

机器:CentOS 7.9(VMware)、Rocky 9.8、Ubuntu 24.04.5(后两台是 LXD 虚拟机,cloud 镜像)。


1. useradd:Ubuntu 上不建家目录,shell 是 sh ✅

同样一条 useradd labu1,不带任何参数:

ruby 复制代码
labu1:x:1000:1000::/home/labu1:/bin/bash                        (Rocky 9)
drwx------. 2 labu1 labu1 4096 Sep 21 17:42 /home/labu1
labu1:!!

labu1:x:1000:1000::/home/labu1:/bin/sh                          (Ubuntu 24.04)
ls: cannot access '/home/labu1': No such file or directory
labu1:!

CentOS 7 和 Rocky 9 一样。Ubuntu 上:

  • 不建家目录 ------ /etc/passwd 里写着 /home/labu1,目录却不存在;
  • shell 是 /bin/sh(Ubuntu 上它是 dash,不是 bash)。

原因在默认配置里:

ini 复制代码
HOME=/home
SHELL=/bin/sh                                                   (Ubuntu 24.04,useradd -D)
CREATE_HOME	yes                                                 (Rocky 9 的 /etc/login.defs;Ubuntu 没有这一行)

跨发行版的脚本,一律写全:useradd -m -s /bin/bash。

另外,adduser 在 CentOS 7 / Rocky 9 上只是个软链:

bash 复制代码
lrwxrwxrwx. /usr/sbin/adduser -> useradd

Ubuntu 上 adduser 是一个独立的交互式程序,会建家目录、问密码。


2. passwd --stdin:Ubuntu 上没有 ✅

脚本里批量设密码的常见写法:

sql 复制代码
passwd: all authentication tokens updated successfully.         (CentOS 7 / Rocky 9)
passwd --stdin 退出码=0

passwd --stdin 退出码=6                                          (Ubuntu 24.04)

--stdin 是 RHEL 系 passwd 的扩展。通用写法是 chpasswd,三台都成功:

复制代码
chpasswd 退出码=0

3. usermod -G 忘了 -a,原来的组全没了 ✅

ini 复制代码
加两个组后:uid=1000(labu1) gid=1000(labu1) groups=1000(labu1),1002(labg1),1003(labg2)
usermod -G(不带 -a)后:uid=1000(labu1) gid=1000(labu1) groups=1000(labu1),1004(labg3)

三台一样。-G 是「把附加组设置成 这些」,不是「加上这些」。labg1、labg2 被清掉了 ------ 如果被清掉的是 wheel 或 sudo,这个用户就没有 sudo 权限了。永远写 usermod -aG。


4. 加了组,已经在跑的进程不认 ✅

usermod -aG labg4 labu1 之后:

bash 复制代码
id labu1:labu1 labg3 labg4
已在运行的进程(974)的组:Groups:	1000 1004
labg4 的 GID=1005
新登录 su - labu1 -c id:labu1 labg3 labg4

三台一样。id labu1 查的是配置,显示已经有 labg4 了;但已经在运行的进程 (看 /proc/<PID>/status 的 Groups),组列表是登录那一刻定下来的,里面没有 1005。

实际影响:把运行服务的用户加进某个组,服务要重启才能用上新组的权限 ;给自己加了 docker 组,要重新登录。


5. su 和 su -,PATH 不一样 ------ 但 Ubuntu 例外 ✅

从 PATH=/usr/bin:/bin 出发切到 root:

ruby 复制代码
su:   PATH=/usr/bin:/bin  目录=/root                                                   (Rocky 9)
su -: PATH=/root/.local/bin:/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin  目录=/root

su(不带 -)保留原来的 PATH,里面没有 /usr/sbin ,于是 useradd、ip、sysctl 这类命令会报「command not found」。CentOS 7 同样(su 后仍是 /usr/bin:/bin)。

Ubuntu 24.04 上 su 不带 - 也给了完整的 PATH:

ruby 复制代码
su:   PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin  目录=/root

所以同一个问题,在 Ubuntu 上复现不出来。要一个干净的 root 环境,用 su -。


6. sudo 权限:wheel 还是 sudo 组 ✅

scss 复制代码
%wheel	ALL=(ALL)	ALL                                          (CentOS 7 / Rocky 9)
#includedir /etc/sudoers.d

%sudo	ALL=(ALL:ALL) ALL                                        (Ubuntu 24.04)
@includedir /etc/sudoers.d

给用户 sudo 权限:RHEL 系加进 wheel,Ubuntu 加进 sudo。#includedir 开头的 # 不是注释 ,是老语法(新版 sudo 改成了 @),别删。


7. sudoers.d 里的文件名带点,整个文件被忽略 ✅

在 /etc/sudoers.d/ 里放一个规则,文件名分别叫 labu1.conf 和 labu1:

less 复制代码
[文件名 labu1.conf] sudo: a password is required
[文件名 labu1] 0

三台一样(CentOS 7 那台是中文提示「sudo: 需要密码」)。文件名里带 . 的文件会被跳过 ,不报错、不警告,规则就是不生效。习惯了 xxx.conf 的人最容易踩。


8. 往 sudoers.d 里放一个语法错误的文件 ✅

不用 visudo、直接写一个有语法错误的文件(NOPASSWD 后面少了冒号):

css 复制代码
labu2 ALL=(ALL) NOPASSWD ALL

CentOS 7:root 自己的 sudo 都用不了了:

bash 复制代码
[有一个坏文件时 root 用 sudo] >>> /etc/sudoers.d/labbad: syntax error near line 1 <<< sudo: parse error in /etc/sudoers.d/labbad near line 1 sudo: no valid sudoers sources found, quitting sudo: unable to initialize policy plugin

普通用户 labu1 那条正常规则也一起失效。如果这台机器 root 不能直接登录、只能靠 sudo,你就把自己锁在外面了。

Rocky 9(sudo 1.9.17p2)/ Ubuntu 24.04(sudo 1.9.15p5):只报错,sudo 照样能用:

sql 复制代码
/etc/sudoers.d/labbad:1:26: syntax error
labu2 ALL=(ALL) NOPASSWD ALL
                         ^~~
visudo -c 退出码=1
[有一个坏文件时 root 用 sudo] /etc/sudoers.d/labbad:1:26: syntax error labu2 ALL=(ALL) NOPASSWD ALL                          ^~~  退出码=0

新版 sudo 跳过坏的那一条,其余规则继续生效,还标出了出错的位置。但别指望这个 ------ 老系统上就是全挂。改 sudoers 一律用 visudo(它保存前会检查语法),改 sudoers.d 下的文件用 visudo -f /etc/sudoers.d/xxx,改完 visudo -c 再检查一遍。


9. SGID 共享目录:设了还是不能一起改 ✅

经典做法:建一个 labproj 组,目录属组设成它,加上 SGID(2775),这样组里的人新建的文件自动属于 labproj:

bash 复制代码
drwxrwsr-x 2 root labproj 4096 Sep 21 17:42 /data/labproj

labu1 建两个文件,labu2(同组)去追加内容:

CentOS 7 / Ubuntu 24.04(登录用户的 umask 是 0002):

less 复制代码
labu1 的 umask:0002
-rw-rw-r-- 1 labu1 labproj 8 Sep 21 17:42 a.txt
-rw-r--r-- 1 labu1 labproj 8 Sep 21 17:42 b.txt
u2 追加 a.txt 成功
-bash: line 1: /data/labproj/b.txt: Permission denied

b.txt 是用 umask 022 建的,组没有写权限。

Rocky 9:

diff 复制代码
labu1 的 umask:0022
-rw-r--r--. 1 labu1 labproj 8 Sep 21 17:42 a.txt
-rw-r--r--. 1 labu1 labproj 8 Sep 21 17:42 b.txt
-bash: line 1: /data/labproj/a.txt: Permission denied
-bash: line 1: /data/labproj/b.txt: Permission denied

Rocky 9 的普通用户默认 umask 就是 0022,新建的文件组只读,两个都改不了。

结论:SGID 只管「文件属于哪个组」,不管「组有没有写权限」。 要真正共享写,还要保证新文件的组有写权限 ------ 上面 CentOS 7 / Ubuntu 的 a.txt 就是 umask 002 建出来、能共享写的样子。

去掉 SGID 后,新文件的属组变回用户自己的组:

bash 复制代码
-rw-rw-r-- 1 labu1 labu1 2 Sep 21 17:42 /data/labproj/c.txt

10. 文件是 644,却读不了 ------ 看路径上每一层目录 ✅

f.txt 是 644,所在目录 sub 是 700(属主 root):

bash 复制代码
cat: /data/labperm/sub/f.txt: Permission denied

namei -l 把路径上每一层的权限都列出来:

lua 复制代码
f: /data/labperm/sub/f.txt
drwxr-xr-x root root /
drwxr-xr-x root root data
drwxr-xr-x root root labperm
drwx------ root root sub
-rw-r--r-- root root f.txt

一眼看出卡在 sub。访问一个文件,要对路径上每一层目录都有 x 权限。

目录有 r 没有 x(744)时,更诡异:

ruby 复制代码
f.txt
ls: cannot access '/data/labperm/sub/f.txt': Permission denied
total 0
-????????? ? ? ? ?            ? f.txt
cat: /data/labperm/sub/f.txt: Permission denied

(Ubuntu 24.04,三台一样。)ls 能列出文件名(r 管读目录内容),但 ls -l 看不到属性、cat 读不了(x 管进入目录访问里面的文件)。


11. 删文件看的是目录权限,不是文件权限 ✅

在一个 777 的目录里,labu2 建了一个只读文件(444),labu1 去删:

bash 复制代码
-r--r--r-- 1 labu2 labu2 3 Sep 21 17:42 /data/labdel/u2.txt
labu1 删掉了 labu2 的只读文件

三台一样。删除是修改目录,和文件本身的权限无关。 这就是 /tmp 要加粘滞位(t)的原因:

bash 复制代码
drwxrwxrwt 2 root root 4096 Sep 21 17:42 /data/labdel
rm: cannot remove '/data/labdel/u2b.txt': Operation not permitted

加上 +t 后,只有文件属主(和 root)能删。


12. chmod -R 755,把文件也变成了可执行 ✅

ini 复制代码
-rwxr-xr-x 1 root root    0 Sep 21 17:42 a.conf
drwxr-xr-x 2 root root 4096 Sep 21 17:42 d
== u=rwX,go=rX:
-rw-r--r-- 1 root root    0 Sep 21 17:42 a.conf
drwxr-xr-x 2 root root 4096 Sep 21 17:42 d

chmod -R 755 让配置文件都带上了 x。用大写的 X :只给目录(和本来就有 x 的文件)加执行权限。


13. cp 丢了属主和时间 ✅

root 复制一个属于 labu1、日期 2020-01-01 的文件:

bash 复制代码
-rw-r----- root root 2026-09-21 /data/labcp/c1
-rw-r----- labu1 labu1 2020-01-01 /data/labcp/c2
-rw-r----- labu1 labu1 2020-01-01 /data/labcp/owned

c1 是普通 cp:属主变成 root、时间变成今天。c2 是 cp -a:原样保留。用 root 复制别人的文件或网站目录,不加 -a,程序就读不了了。

目标目录已存在时,cp -r src dst 的结果是 dst/src/,不是把 src 的内容放进 dst:

bash 复制代码
/data/labcp/dst
/data/labcp/dst/src
/data/labcp/dst/src/f

14. ln -sf 换不掉指向目录的软链 ✅

cur 是指向 v1 目录的软链,想把它改成指向 v2:

bash 复制代码
ln -sf 之后 cur -> /data/labln/v1;v1 里多了:v2
ln -sfn 之后 cur -> /data/labln/v2

三台一样。ln -sf v2 cur 把 cur 当成了目录,在 v1 里面建了一个叫 v2 的软链 ,cur 本身没变。发布系统用软链切换版本时,这个错误会让你以为切过去了。要加 -n。


15. find | xargs rm 遇到带空格的文件名 ✅

less 复制代码
xargs 之后剩:a b.log
-print0 之后剩:0 个

a b.log 被 xargs 拆成了 a 和 b.log 两个参数,都不存在,文件留了下来(如果恰好有叫 a 的文件,删的就是它)。用 find ... -print0 | xargs -0,或者直接 find ... -delete。


16. root 也删不掉的文件 ✅

bash 复制代码
rm: cannot remove '/data/labcp/imm': Operation not permitted
rm 退出码=1
----i---------e------- /data/labcp/imm
chattr -i 后删掉了

权限看着正常、root 也删不掉,用 lsattr 看有没有 i(不可修改)属性。


17. rm -rf "$DIR"/ 的保险 ✅

shell 复制代码
# DIR=; echo rm -rf "${DIR:?}"/x; echo "这一行不会执行"
bash: line 1: DIR: parameter null or not set

变量为空时,${DIR:?} 直接报错退出,后面的命令都不会执行。脚本里 rm -rf "$DIR/" 一旦 DIR 为空就是 rm -rf /,删除类命令里的变量一律写成 ${DIR:?}。


18. userdel 不带 -r,留下一堆无主文件 ✅

bash 复制代码
drwxr-x--- 2 1001 1001 4096 Sep 21 17:42 /home/labu2
/home/labu2
/home/labu2/.profile
/home/labu2/.bash_logout

用户删了,家目录还在,属主显示成数字 1001。以后新建的用户如果拿到了 1001 这个 UID,这些文件就自动归他了。 用 find / -nouser 找出来处理掉。

另外两个小差异:

  • rename:CentOS 7 / Rocky 9 自带(util-linux 版,rename .txt .bak *.txt);Ubuntu 默认没有。
  • locate:三台都没装。

小结

坑 对策
Ubuntu useradd 不建家目录、shell 是 sh useradd -m -s /bin/bash
Ubuntu 没有 passwd --stdin 用 chpasswd
usermod -G 清掉原有组 usermod -aG
加组后服务没权限 重启服务 / 重新登录
su 后找不到命令 su -
sudoers.d 规则不生效 文件名不能带 .
一个坏文件让 sudo 全挂(CentOS 7) 用 visudo -f
SGID 目录还是改不了别人的文件 保证 umask 是 002
644 的文件读不了 namei -l 看每层目录
chmod -R 755 文件都可执行 用 u=rwX,go=rX
软链切换没切过去 ln -sfn

下一篇:文本处理、压缩与传输 ------ sed -i 会破坏软链、tar -cfz 生成一个叫 z 的文件、scp 在新版本上已经不走 scp 协议。

相关推荐
拂拉氏3 小时前
【知识讲解】 Linux之动静态库的了解
linux·运维·服务器·动静态库
Fcy6483 小时前
Linux下 五种IO模型的认识与⾮阻塞IO的实现
linux·服务器·io
Mikko74 小时前
Linux 重启后日志没了?进程被 OOM 杀了去哪看?logrotate 之后日志还往旧文件写?三台真机实测日志与性能排查
linux·运维·centos
书生执笔画浮沉4 小时前
分享一个开源小工具CAdapt
linux·c·gnu·glibc·kernel·升级·灵感
mounter6254 小时前
Linux 内核热升级(Live Update)技术详解:PCI、VFIO 与 IOMMU 子系统的演进与实战解析引言
linux·linux kernel·kernel·iommu·vfio
砚凝霜5 小时前
【软考信息安全】第十九章 Unix/Linux安全分析与防护
linux·安全·unix
creator_Li5 小时前
Linux 的 Page Cache
linux·page cache
KING-WU5127 小时前
Linux GDB 调试实战指南:从编译到断点调试
linux·运维·服务器
Lucis__7 小时前
深入理解程序链接与加载—从动静态链接到库加载
linux·elf·链接·程序·动静态库