目录

Linux-03 用户、权限与安全模型

前置阅读:Linux-02 文件、链接与归档压缩

权限是每天都在用、但很少有人真正搞清楚的东西。这一篇的重点不是背 chmod 755,而是三个容易理解错的地方:

  1. 删除文件需要的是「目录的写权限」,不是文件的写权限——这是最反直觉的一条。
  2. x 在目录上的含义完全不是「执行」
  3. 容器里的 uid 和宿主机是同一套,这解释了挂载卷权限问题的所有现象。

1. 身份:一切都是数字

一句话:内核只认 UID 和 GID 这两个数字,用户名是给人看的

/etc/passwd 的作用就是「数字 ↔ 名字」的翻译表。内核在做权限检查时从来不查这张表,它只比较进程的 uid 和文件 inode 里的 i_uid

这个认知能解释很多现象:

id
# uid=1000(lhx) gid=1000(lhx) groups=1000(lhx),27(sudo),999(docker)
#     ^^^^ 内核认这个    ^^^^ 主组      ^^^^^^^^^^^^^^^^^^ 附加组

# 删掉一个用户后,它创建的文件会怎样?
ls -l /data/x
# -rw-r--r-- 1 1005 1005 0 Aug  6 20:00 /data/x
#              ^^^^ ^^^^ 翻译不出名字了,直接显示数字

root 的特殊性也只是「uid == 0」。内核里大量的权限检查代码就是 if (uid == 0) return ALLOWED;。所以:

# 把一个普通用户的 uid 改成 0,他就是 root
grep "^hacker" /etc/passwd
# hacker:x:0:0::/home/hacker:/bin/bash
#          ^ uid=0 -> 这就是一个后门账号

安全审计时检查「除 root 外还有谁的 uid 是 0」是必查项:

awk -F: '$3 == 0 {print $1}' /etc/passwd
# root          <- 应该只有这一个

1.1 三个文件的分工

文件 存什么 权限
/etc/passwd 用户名、uid、gid、家目录、登录 shell 644 所有人可读
/etc/shadow 密码哈希、密码有效期 640000,只有 root 能读
/etc/group 组名、gid、附加成员列表 644

为什么密码要单独拆到 shadow/etc/passwd 必须所有人可读(因为 ls -l 要把 uid 翻译成名字),而密码哈希如果放在里面,任何用户都能拿去离线爆破。所以 1990 年代把哈希拆到了只有 root 能读的 shadow 里。这就是 passwd 第二列现在永远是个 x 的原因——那个 x 表示「密码在 shadow 里」

head -1 /etc/passwd
# root:x:0:0:root:/root:/bin/bash
#      | | |  |     |       +- 登录 shell
#      | | |  |     +--------- 家目录
#      | | |  +--------------- 描述(GECOS)
#      | | +------------------ gid
#      | +-------------------- uid
#      +---------------------- 密码占位符

sudo head -1 /etc/shadow
# root:$6$xxxx$yyyy...:19800:0:99999:7:::
#      |  |
#      |  +- 哈希算法:$1=MD5 $5=SHA-256 $6=SHA-512 $y=yescrypt(现代默认)
#      +---- 若是 ! 或 * 开头 = 该账号无法用密码登录

shadow 第二列以 !* 开头表示禁用密码登录。系统服务账号(nginxmysql)都是这样——它们需要一个 uid 来跑进程,但绝不应该能登录。

1.2 系统账号与登录 shell

# 看看哪些账号能登录
awk -F: '$7 !~ /(nologin|false)$/ {print $1, $3, $7}' /etc/passwd
# root 0 /bin/bash
# lhx  1000 /bin/bash
# <- 其他的 shell 都是 /usr/sbin/nologin,登不进来

约定:uid < 1000 是系统账号,uid >= 1000 是普通用户(RHEL 系早期是 500 分界)。给服务创建账号时应该用 --system

# ✅ 创建一个不能登录、没有家目录的服务账号
useradd --system --no-create-home --shell /usr/sbin/nologin myapp

# ❌ 用 root 跑业务服务
# 一旦服务被 RCE,攻击者直接拿到 root

生产建议:业务服务一律用专属的低权限系统账号运行,systemd unit 里写 User=myapp。第 14 篇会讲怎么配。

1.3 主组与附加组

id lhx
# uid=1000(lhx) gid=1000(lhx) groups=1000(lhx),27(sudo),999(docker)
  • 主组(gid):写在 /etc/passwd 第 4 列。你创建的文件默认属于这个组
  • 附加组:写在 /etc/group 的成员列表里,用于获得额外权限。
# 加入 docker 组(能免 sudo 用 docker)
usermod -aG docker lhx
#         ^^ -a 必须加!不加 -a 会【覆盖】所有附加组

坑:usermod -G 不加 -a 会把用户从所有未列出的附加组里踢出去。把自己踢出 sudo 组、又没有别的 root 途径,是能把自己锁在门外的。永远写 -aG

另一个坑:改组之后当前会话不生效。因为组成员关系是登录时写进进程凭证的,之后不再变。必须重新登录,或者:

newgrp docker        # 开一个新 shell,带上新组
# 或者直接 exit 重新 ssh

「我明明加了 docker 组,为什么还是 permission denied」——答案基本都是这个。

2. 权限位:rwx 的两套语义

ls -l
# -rw-r--r--  1 lhx  dev  1024 Aug  6 20:00 file
# drwxr-xr-x  2 lhx  dev  4096 Aug  6 20:00 dir
# |+++++++++
# | |  |  +-- other:其他所有人
# | |  +----- group:属组成员
# | +-------- user:属主
# +---------- 类型:- 文件  d 目录  l 软链接  c/b 设备  s socket  p 管道

关键:同样的 rwx 三个字母,在文件和目录上含义完全不同。

对文件 对目录
r 读取内容 列出里面有哪些名字(ls
w 修改内容 增删里面的条目(创建/删除/改名文件)
x 作为程序执行 进入、访问里面的文件(cdopen

2.1 最反直觉的一条:删文件看的是目录权限

mkdir d && echo hi > d/f && chmod 444 d/f     # 文件设为只读
ls -l d/f
# -r--r--r-- 1 lhx lhx 3 Aug  6 20:30 d/f

rm d/f
# rm: remove write-protected regular file 'd/f'? y
# <- 只是问一下,回车就删掉了

为什么?因为「删除文件」的本质是从目录里删掉一条目录项unlink,见第 2 篇),改的是目录的内容,不是文件的内容。所以内核检查的是目录的 w 权限,跟文件本身的权限毫无关系。

反过来也成立:

# 你对文件有全部权限,但对目录没有 w
sudo mkdir /srv/d && sudo chmod 755 /srv/d
sudo touch /srv/d/f && sudo chown lhx:lhx /srv/d/f && chmod 777 /srv/d/f

rm /srv/d/f
# rm: cannot remove '/srv/d/f': Permission denied
# <- 文件是你的、权限 777,但删不掉,因为目录 /srv/d 你没有 w

规律:能不能改文件内容看文件权限,能不能删/改名文件看目录权限。

这条规律的直接应用就是 /tmp 的 Sticky 位(§4.3)——所有人都能在 /tmp 里创建文件(目录有 w),但不能删别人的文件。

2.2 目录的 x 是「通行权」

chmod 644 dir        # 有 r 无 x

ls dir
# f1  f2             <- 能列出名字

ls -l dir
# ls: cannot access 'dir/f1': Permission denied
# total 0
# -????????? ? ? ? ?            ? f1     <- 拿不到元数据

cd dir
# bash: cd: dir: Permission denied

没有 x 就无法「穿过」这个目录去访问里面的任何东西,连 inode 的元数据都读不到(因为读元数据要先解析路径)。

反过来,只有 x 没有 r 是个有用的组合:

chmod 711 /home/lhx     # rwx--x--x

# 别人不能 ls 你的家目录(不知道有哪些文件)
ls /home/lhx
# ls: cannot open directory: Permission denied

# 但如果知道确切路径,能访问
cat /home/lhx/public/readme.txt      # ✅ 可以

这就是「知道名字才能访问」模式,Web 服务器的目录常这么配——/var/www711,nginx 能穿过去读文件,但列不出目录内容。

路径上的每一层目录都需要 x。访问 /a/b/c/f 需要 //a/a/b/a/b/c 全都有 x。「明明文件权限是 777 却读不了」,答案通常是中间某层目录缺 x

# 快速定位是哪一层挡住了
namei -l /var/www/html/index.html
# f: /var/www/html/index.html
# drwxr-xr-x root root /
# drwxr-xr-x root root var
# drwx------ root root www        <- 就是这一层,other 没有 x
# drwxr-xr-x root root html
# -rw-r--r-- root root index.html

namei -l 是排查权限问题最快的命令,比一层层 ls -ld 高效得多。

2.3 八进制与符号法

r = 4    w = 2    x = 1

7 = rwx   6 = rw-   5 = r-x   4 = r--   0 = ---
# 数字法:一次设定全部
chmod 755 script.sh      # rwxr-xr-x
chmod 640 config.yaml    # rw-r-----
chmod 600 ~/.ssh/id_ed25519   # rw------- 私钥必须这个权限

# 符号法:增量修改,不影响其他位
chmod +x   script.sh     # 给所有人加 x
chmod u+x  script.sh     # 只给属主加 x
chmod go-w file          # 去掉组和其他人的 w
chmod a=r  file          # 所有人只读(= 是覆盖,+ 是追加)
chmod -R u+rwX dir/      # 大写 X:只给【目录】和【已有 x 的文件】加 x

大写 X 很实用:递归给目录加执行权限但不误伤普通文件。chmod -R 755 dir/ 会把所有 .txt 也变成可执行,而 chmod -R u+rwX,go+rX dir/ 不会。

常用的几组权限:

权限 用于
755 目录、可执行脚本、程序
644 普通文件、配置、静态资源
640 含敏感信息的配置(组内可读)
600 私钥、密码文件
700 私有目录(~/.ssh
777 永远不要用,见下

777 为什么是错的:它意味着任何一个能登上这台机器的用户(包括被攻破的低权限服务账号)都能改你的文件。遇到权限问题就 chmod -R 777 是把安全边界彻底拆掉。正确做法是搞清楚需要访问的是谁,然后用属主或属组授权。

3. umask:新建文件的默认权限

创建文件时的权限不是固定的,而是**「基础权限 减去 umask」**:

文件基础权限 666(rw-rw-rw-)   <- 注意:文件基础权限【没有 x】
目录基础权限 777(rwxrwxrwx)

实际权限 = 基础权限 & ~umask
umask
# 0022

touch f && mkdir d && ls -ld f d
# -rw-r--r-- 1 lhx lhx    0 Aug  6 21:00 f      <- 666 - 022 = 644
# drwxr-xr-x 2 lhx lhx 4096 Aug  6 21:00 d      <- 777 - 022 = 755

文件的基础权限是 666 而不是 777,这是内核的设计:新建文件默认不应该可执行,避免误把数据文件当程序运行。所以 umask 022 下新文件是 644 而不是 755。

常见的 umask 值:

umask 文件 目录 场景
022 644 755 默认,同组和其他人可读
027 640 750 推荐给生产服务,其他人完全无权限
077 600 700 最严格,只有自己能访问
002 664 775 团队协作目录(配合 SGID)

生产建议:服务的 systemd unit 里显式设 UMask=0027。不要依赖继承来的 umask——cron、systemd、交互式 shell 的默认 umask 可能不同,导致「手动跑生成的文件权限对,定时任务生成的就不对」。

坑:umask 只影响新建的文件,对已存在的文件没有任何作用。而且它是进程属性,改了只对当前 shell 及其子进程生效。要持久化就写进 /etc/profile~/.bashrc 或 systemd unit。

4. 三个特殊权限位

rwx 之外还有三个位,它们是权限模型里最容易被忽略、但在安全审计和团队协作中很关键的部分。

4.1 SUID:以文件属主的身份运行

只对可执行文件有效。带 SUID 的程序被执行时,进程的有效 uid 变成文件属主的 uid,而不是启动它的用户。

ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root 68208 Nov 24  2022 /usr/bin/passwd
#    ^ 这个 s 就是 SUID

为什么 passwd 需要 SUID:普通用户改自己的密码,要写 /etc/shadow,而那个文件只有 root 能写。SUID 让 passwd 这个程序在被任何人执行时都以 root 身份运行,从而能写 shadow——同时程序内部有逻辑保证你只能改自己的密码。

SUID 是提权漏洞的最大来源。一个有 SUID 的程序如果存在缓冲区溢出、或者能让用户指定任意命令,攻击者就直接拿到 root。所以:

# 安全审计:列出所有 SUID 程序
find / -perm -4000 -type f 2>/dev/null
# /usr/bin/passwd
# /usr/bin/sudo
# /usr/bin/su
# /usr/bin/mount
# <- 正常系统就这几个,多出来的要警惕

# 挂载用户可写的分区时禁用 SUID
mount -o nosuid,nodev,noexec /dev/vdb1 /data

nosuid 挂载选项是重要的加固手段:/tmp/home/data 这些用户可写的分区都应该带上,防止攻击者上传一个 SUID 程序。

4.2 SGID:继承目录的属组

可执行文件:进程的有效 gid 变成文件的属组(用得少)。

目录在这个目录里新建的文件,属组自动继承目录的属组,而不是创建者的主组。这个才是实际用途。

# 团队共享目录:让所有人建的文件都属于 dev 组
mkdir /srv/share
chgrp dev /srv/share
chmod 2775 /srv/share        # 2 = SGID
#      ^ 或 chmod g+s /srv/share

ls -ld /srv/share
# drwxrwsr-x 2 root dev 4096 Aug  6 21:20 /srv/share
#       ^ s 在 group 位

# 任何人在里面建文件
touch /srv/share/f && ls -l /srv/share/f
# -rw-rw-r-- 1 lhx dev 0 Aug  6 21:20 f
#                  ^^^ 属组是 dev,不是 lhx

SGID 目录 + umask 002 是团队共享目录的标准配方:SGID 保证属组统一,umask 002 保证组内可写,两者缺一不可。只配 SGID 而 umask 是 022 的话,文件属组虽然对了但组成员只能读不能写。

注意:SGID 只影响新建的文件。已经在目录里的旧文件不会自动变,需要 chgrp -R dev /srv/share 手动修一次。

4.3 Sticky:只有属主能删

**只对目录有效。**设了 Sticky 的目录里,文件只能被文件属主(或目录属主、root)删除或改名,即使其他人对目录有 w 权限。

ls -ld /tmp
# drwxrwxrwt 10 root root 4096 Aug  6 21:30 /tmp
#          ^ t 就是 Sticky

这正是 §2.1 那条规律的必要补丁:/tmp 权限是 777,所有人都能在里面创建文件——按「删文件看目录 w 权限」的规则,那所有人也都能删别人的文件。Sticky 位就是为了堵这个洞。

chmod 1777 /srv/upload       # 1 = Sticky
# 或 chmod +t /srv/upload

任何「所有人可写的共享目录」都必须加 Sticky,否则任何用户都能删光别人的文件。

4.4 三个位的速查

八进制 显示位置 对文件 对目录
SUID 4000 user 的 x 位 → s 以属主身份运行 无效
SGID 2000 group 的 x 位 → s 以属组身份运行 新文件继承属组
Sticky 1000 other 的 x 位 → t 无效(历史遗留) 只有属主能删
chmod 4755 f     # SUID
chmod 2775 d     # SGID
chmod 1777 d     # Sticky
chmod 6755 f     # SUID + SGID

大小写的含义:如果原本那一位有 x,特殊位显示为小写 s/t;如果原本没有 x,显示为大写 S/T

chmod 4644 f && ls -l f
# -rwSr--r--        <- 大写 S:设了 SUID 但文件根本不可执行 = 配错了

看到大写的 S 基本就是配置错误,因为 SUID 对不可执行的文件毫无意义。

5. ACL:超越 ugo 的细粒度授权

传统的 ugo 模型只能表达「属主 / 一个组 / 其他人」三种身份。当需求是「用户 A 可读写,用户 B 只读,其他人无权限」时,ugo 就不够用了——总不能为每种组合都建一个组。

ACL(Access Control List)解决这个问题:

# 查看 ACL
getfacl report.xlsx
# user::rw-
# group::r--
# other::---

# 给单个用户额外授权
setfacl -m u:alice:rw report.xlsx
setfacl -m u:bob:r   report.xlsx
setfacl -m g:audit:r report.xlsx

getfacl report.xlsx
# user::rw-
# user:alice:rw-        <- 额外的用户条目
# user:bob:r--
# group::r--
# group:audit:r--
# mask::rw-             <- 上限,见下
# other::---

设了 ACL 的文件,ls -l 会在权限位后面显示一个 +

ls -l report.xlsx
# -rw-r-----+ 1 lhx dev 8192 Aug  6 21:40 report.xlsx
#           ^ 有 ACL

mask 是个坑:它是所有「命名用户 / 组条目」权限的上限。chmod g-w 实际上改的是 mask,会同时削掉所有 ACL 条目的写权限,看起来像 ACL 失效了:

chmod g-w report.xlsx
getfacl report.xlsx | grep alice
# user:alice:rw-        #effective:r--     <- 实际生效的降级了

目录的默认 ACL-d)让新建文件自动继承,这才是 ACL 真正好用的地方:

setfacl -d -m u:alice:rw /srv/data      # 之后在这里新建的文件 alice 都能读写
setfacl -R -m u:alice:rw /srv/data      # -R 修改已有的
# 删除
setfacl -x u:bob file      # 删单条
setfacl -b file            # 清空所有 ACL

注意:cp 默认不保留 ACL,cp -a(或 --preserve=all)才保留。tar 需要 --acls 参数。rsync 需要 -A。备份脚本忘了这些参数,恢复后权限会静默丢失。

6. sudo:受控的提权

sudo 的核心价值不是「变成 root」,而是**「可审计、可限定范围的提权」**。

# 查自己能干什么
sudo -l
# User lhx may run the following commands on host:
#     (ALL : ALL) ALL

# 配置必须用 visudo(它会做语法检查,防止把自己锁死)
sudo visudo

配置语法:

# 用户  主机=(可切换到的用户:组)  命令
root    ALL=(ALL:ALL) ALL
%sudo   ALL=(ALL:ALL) ALL          # % 开头表示组

实际生产中应该做最小授权,而不是给 ALL

# /etc/sudoers.d/deploy   <- 放在 sudoers.d 下,别直接改主文件
# 部署账号只能重启这一个服务,且不需要输密码
deploy ALL=(root) NOPASSWD: /bin/systemctl restart myapp, /bin/systemctl reload myapp

# 运维组可以查看所有日志
%ops   ALL=(root) NOPASSWD: /usr/bin/journalctl, /usr/bin/tail /var/log/*

坑:给用户 sudo 执行任何能「跳出去」的程序,等于给了完整 root。比如 sudo vim:!sh 直接拿 shell)、sudo find-exec sh \;)、sudo less!sh)、sudo tar--to-command)。授权时要选择那些无法执行任意命令的程序。

sudo 相关的两个常见问题在第 1 篇讲过:secure_path 会重置 PATH(用绝对路径或 sudo -E env "PATH=$PATH"),sudo echo $PATH 看到的是当前用户的 PATH 而不是 sudo 的。

审计日志在这里:

grep sudo /var/log/auth.log | tail          # Debian/Ubuntu
journalctl _COMM=sudo -n 20                 # systemd 通用
# lhx : TTY=pts/0 ; PWD=/home/lhx ; USER=root ; COMMAND=/bin/systemctl restart nginx

7. 容器里的权限:uid 是同一套

这是后端最常撞的一类权限问题,而且现象很迷惑。

核心事实:容器没有独立的 uid 空间(除非显式开了 user namespace)。容器里的 uid 1000 和宿主机的 uid 1000 是同一个数字,内核在检查挂载卷的权限时用的就是这个数字。

# 容器里以 root(uid 0)运行,往挂载卷写文件
docker run -v /data/out:/out alpine sh -c 'echo hi > /out/f'

# 回到宿主机看
ls -l /data/out/f
# -rw-r--r-- 1 root root 3 Aug  6 22:00 f
#              ^^^^ 属主是宿主机的 root,你的普通账号删不掉

反过来更常见——容器里以非 root 运行,挂载卷写不进去

docker run -u 1000:1000 -v /data/out:/out myapp
# Error: permission denied: /out/app.log

ls -ld /data/out
# drwxr-xr-x 2 root root 4096 Aug  6 22:00 /data/out
# <- 目录属主是宿主机 root,uid 1000 没有 w

解决办法:

# ✅ 方案一:宿主机上把目录属主改成容器里的 uid
chown -R 1000:1000 /data/out

# ✅ 方案二:让容器用宿主机当前用户的 uid 运行
docker run -u "$(id -u):$(id -g)" -v /data/out:/out myapp

# ✅ 方案三(K8s):用 fsGroup,kubelet 会自动 chown 挂载卷
# K8s 里的标准写法
securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  runAsGroup: 1000
  fsGroup: 1000          # 挂载卷的属组会被设为 1000,且加 SGID
  readOnlyRootFilesystem: true
  capabilities:
    drop: ["ALL"]

**规律:容器里的权限报错,先看宿主机上那个目录的属主 uid 和容器进程的 uid 对不对得上。**用数字比对,别看名字——容器和宿主机的 /etc/passwd 不是同一份,同一个 uid 在两边可能翻译成完全不同的名字。

为什么不要用 root 跑容器:容器的隔离靠的是 namespace 和 cgroup(第 19 篇会讲),不是虚拟机级别的隔离。容器里的 root 一旦通过内核漏洞或错误的挂载配置逃逸,拿到的就是宿主机的 root。runAsNonRoot: true 应该是默认配置。

8. 面试题

Q:一个文件权限是 444(只读),为什么我还能把它删掉?

因为删除文件改的是目录的内容,不是文件的内容。rm 底层是 unlink,它从目录里移除一条「文件名 → inode」的映射,所以内核检查的是目录的写权限,与文件自身权限无关。反过来,一个属于你、权限 777 的文件,如果它所在目录你没有 w 权限,你也删不掉它。这也是 /tmp 必须加 Sticky 位的原因——否则 777/tmp 里谁都能删别人的文件。

Q:目录的 rx 权限分别是什么意思?只有 r 没有 x 会怎样?

目录的 r 是「能列出里面有哪些名字」,x 是「能穿过这个目录去访问里面的东西」。只有 r 没有 x 时,ls 能列出文件名,但 ls -l 拿不到任何元数据(显示一堆 ?),cd 进不去,里面的文件也全都读不了。反过来只有 x 没有 r 是有用的配置(如 711):别人列不出目录内容,但知道确切路径就能访问,Web 目录常这么配。注意:访问 /a/b/c/f 需要路径上每一层目录都有 x,排查用 namei -l /a/b/c/f 最快。

Q:SUID 是什么?为什么 passwd 需要它?有什么风险?

SUID 让可执行文件在被任何人运行时,进程的有效 uid 变成文件属主的 uid。passwd 需要写 /etc/shadow(只有 root 可写),但普通用户要能改自己的密码,所以它被设为 SUID root。风险在于这是提权漏洞的主要入口——SUID 程序一旦有溢出、命令注入或能让用户指定任意命令的路径,攻击者直接获得 root。加固手段是定期审计 find / -perm -4000 -type f,以及给用户可写的分区加 nosuid 挂载选项。

Q:umask 022 为什么新建文件是 644 而不是 755?

因为文件的基础权限是 666,不是 777。内核规定新建文件默认不带执行权限,避免数据文件被误当作程序运行。所以文件是 666 & ~022 = 644,而目录的基础权限是 777,777 & ~022 = 755注意:umask 只影响新建文件,对已有文件无效;它是进程属性,会被子进程继承,所以 cron 和 systemd 下的 umask 可能与交互式 shell 不同,导致「手动跑权限对、定时任务权限不对」。

Q:怎么做一个多人共享的目录,让所有人建的文件组内都能读写?

需要两件事配合:① 给目录设 SGIDchmod 2775 dir + chgrp team dir),让新建文件的属组自动继承目录的属组,而不是创建者的主组;② 把相关用户的 umask 设为 002,否则新文件是 664 中的组权限会被 umask 削掉变成只读。只做第一步的话,文件属组虽然对了但组成员只能读。如果目录还允许所有人写(如上传目录),必须再加 Sticky 位 防止互删。更细粒度的需求(用户 A 读写、B 只读)用 ACL 的默认条目:setfacl -d -m u:alice:rw dir

Q:usermod -G docker lhx 有什么问题?

-G 不加 -a覆盖语义——会把用户从所有未在命令中列出的附加组里移除。如果这个用户原本在 sudo 组里,这条命令会把他踢出去,且如果没有其他 root 途径就锁死了。正确写法是 usermod -aG docker lhx。另外改组之后当前会话不生效,因为组成员关系是登录时写入进程凭证的,必须重新登录或用 newgrp 开新 shell——「加了 docker 组还是 permission denied」几乎都是这个原因。

Q:容器里以非 root 用户运行,往挂载卷写文件报 permission denied,为什么?

因为容器没有独立的 uid 空间(未开 user namespace 时),容器里的 uid 1000 就是宿主机的 uid 1000,内核用这个数字去检查宿主机目录的权限。如果挂载目录在宿主机上属主是 root、权限 755,uid 1000 自然没有写权限。解决方式:宿主机 chown 1000:1000 那个目录,或用 -u "$(id -u):$(id -g)" 让容器以宿主机当前用户运行,K8s 里用 fsGroup(kubelet 会自动调整挂载卷属组)。注意:排查时要比对 uid 数字而不是用户名,容器和宿主机的 /etc/passwd 是两份,同一个 uid 两边可能显示成不同名字。

Q:给运维同事 sudo vim /etc/nginx/nginx.conf 的权限,安全吗?

不安全,等于给了完整 root。vim 里可以 :!sh 直接开一个 root shell,或者 :w /etc/sudoers 写任意文件。同类的还有 sudo find-exec sh \;)、sudo less/man!sh)、sudo tar--to-command)、sudo awksystem())。授权原则是只允许那些无法执行任意命令、无法写任意路径的程序,比如 systemctl restart 特定服务journalctl。如果确实需要编辑配置,用 sudoeditsudo -e)——它把文件复制到临时位置用普通权限编辑,写回时才提权,且不会让编辑器以 root 运行。


上一篇:Linux-02 文件、链接与归档压缩 | 下一篇:Linux-04 文本处理三剑客与正则